Uma API pública de leitura sobre os registros do Avaí Futebol Clube no BID (Boletim Informativo Diário) da CBF, de 2003-01-01 até hoje. Tudo em JSON, com chave de API. Esta página é a referência completa: endpoints, parâmetros, exemplos prontos para copiar, dicionário de dados e os cuidados necessários para não interpretar o BID errado.
O que é esta API e de onde vem o dado
O BID é o diário oficial do futebol brasileiro: é nele que a CBF publica os registros de contrato dos atletas. Sem isso, ninguém entra em campo. Este site acompanha o que o BID publica sobre o Avaí Futebol Clube (clube 20058 no BID) e guarda tudo, desde 2003-01-01 até hoje.
A única fonte é o BID da CBF. Não há dado de imprensa, de site de transferências nem de redes sociais. Se não saiu no BID, não está aqui.
Um monitor consulta o BID a cada 20 minutos e grava as publicações novas (essas ficam com origem: "monitor").
Uma rotina de mineração percorre o passado dia a dia desde 2003-01-01 para recuperar o histórico (origem: "historico"). O progresso aparece em /estatisticas.
Uma consulta diária por atleta pergunta ao BID onde cada pessoa está registrada hoje. É a única forma de saber quem o clube emprestou para fora.
A URL base é https://bid.avaifc.com/api/v1. Todos os endpoints são GET e respondem JSON com charset=utf-8.
Autenticação
Uma chave por aplicação. A chave identifica você no limite de requisições.
Mande sua chave em um dos dois cabeçalhos — escolha o que for mais fácil no seu cliente:
Sem chave, ou com uma chave errada, a resposta é 401 com o código nao_autenticado. Os endpoints /saude e /openapi.json são abertos e não pedem chave.
Como pedir uma chave: fale com quem mantém o site — pelo próprio site ou pelo canal do projeto no Telegram — dizendo para que você vai usar e qual volume espera. As chaves são nominais e podem ser revogadas.
Nunca exponha a chave no navegador
Não coloque a chave em JavaScript de página, aplicativo móvel ou qualquer código que o usuário final possa ler: quem abrir o DevTools copia a sua chave. Chame esta API do seu servidor e repasse o resultado para o seu front-end. O CORS está liberado para facilitar testes e protótipos, não para você embutir a chave em produção.
Limites e cache
600 requisições por 1 minuto por chave. Sem chave válida, o limite é de 120 por minuto por IP.
Cada resposta traz X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (momento em que a janela reinicia, em segundos desde a época Unix).
Ao estourar: 429 com o código limite_excedido e o cabeçalho Retry-After em segundos. Espere o tempo indicado em vez de insistir.
Respostas de sucesso vêm com Cache-Control: public, max-age=60, stale-while-revalidate=120. Pode cachear por 60 segundos sem medo: o monitor só roda a cada 20 minutos.
Access-Control-Allow-Origin: *, então dá para chamar do navegador em testes.
Se você precisa varrer o acervo inteiro, use limit=100 com paginação por cursor e guarde o resultado do seu lado: é mais rápido para você e mais leve para o servidor do que repetir a mesma consulta.
Formato de erro
Todo erro da API — qualquer endpoint, qualquer status — tem o mesmo corpo:
{
"erro": {
"codigo": "parametro_invalido",
"mensagem": "Parâmetro \"limit\" inválido: esperado um número inteiro entre 1 e 100.",
"detalhes": {
"parametro": "limit",
"valor": "500",
"esperado": "um número inteiro entre 1 e 100"
}
}
}
codigo é estável e serve para o seu código tratar o erro; mensagem é texto para humanos e pode mudar; detalhes é opcional.
Código
HTTP
Quando acontece
api_nao_configurada
503
O servidor está sem nenhuma chave configurada. Nada a fazer do lado de quem chama.
nao_autenticado
401
Chave ausente, com erro de digitação ou revogada.
parametro_invalido
400
Algum parâmetro saiu do formato ou da faixa aceita. detalhes diz qual e o que era esperado.
nao_encontrado
404
Código de atleta desconhecido ou fora do formato.
limite_excedido
429
Você passou do limite de requisições por minuto.
erro_interno
500
Falha nossa ao consultar o banco. Tente de novo em instantes.
Paginação por cursor
Em /registros, pagine por cursor: chame sem cursor, leia paginacao.proximoCursor e repita passando aquele valor em ?cursor= até receber null. O cursor é opaco — não tente decodificá-lo, o formato pode mudar.
Por que não usar offset: o histórico ainda pode estar sendo minerado, e registros antigos entram no meio da lista. Com offset isso desloca as páginas seguintes e você acaba repetindo ou perdendo itens. O cursor fixa a posição pelo par (data de publicação, registro), então a varredura é consistente. offset continua existindo para casos simples e é ignorado quando há cursor.
import os, requests
chave = os.environ["BID_API_KEY"]
cursor, todos = None, []
while True:
r = requests.get(
"https://bid.avaifc.com/api/v1/registros",
params={"limit": 100, **({"cursor": cursor} if cursor else {})},
headers={"Authorization": f"Bearer {chave}"},
timeout=30,
)
r.raise_for_status()
pagina = r.json()
todos.extend(pagina["registros"])
cursor = pagina["paginacao"]["proximoCursor"]
if not cursor:
break
print(len(todos), "registros")
Em /atletas a paginação é por page simples, porque a lista é bem menor e ordenada por atividade recente.
Referência dos endpoints
Todos são GET e respondem JSON.
GET/api/v1/registroschave obrigatória
Cada item é um registro publicado no BID: uma linha que a CBF divulgou para um atleta do clube em uma data. É a base de tudo no site. A lista aceita filtros por período, atleta, tipo exato e categoria, e pagina por cursor.
Parâmetros
Parâmetro
Tipo
Padrão
Descrição
ordem
enum
recentes
Ordenação por data de publicação.
Valores aceitos: recentes · antigos
limit
inteiro
25
Quantos itens trazer por página.
Faixa: 1 a 100.
Exemplo: 50
cursor
string
—
Cursor de paginação devolvido em paginacao.proximoCursor. Com cursor, registros inseridos no meio da mineração do histórico não deslocam a página seguinte.
Paginação simples por deslocamento. Ignorado quando cursor é enviado.
Faixa: 0 a 10000000.
desde
data
—
Só registros publicados nesse dia ou depois (data_publicacao).
Exemplo: 2024-01-01
ate
data
—
Só registros publicados até esse dia, incluindo o dia inteiro.
Exemplo: 2024-12-31
atleta
string
—
Código CBF do atleta (codigo_atleta).
Exemplo: 123456
tipo
string
—
tipo_contrato exato, sem diferenciar maiúsculas nem acentos. Use quando você quer um status específico do BID, e não um agrupamento.
Exemplo: Contrato Definitivo
categoria
enum
—
Agrupamento dos tipos do BID feito pelo site (um tipo cai na primeira regra que casa). Útil porque o BID usa dezenas de redações diferentes para a mesma coisa.
Valores aceitos: contratacao · rescisao · emprestimo · renovacao · base · liberacao · transferencia · outros
total é o número de registros que casam com os filtros, não o tamanho da página.
Para varrer tudo, repita a chamada com cursor = paginacao.proximoCursor até receber null.
GET/api/v1/atletaschave obrigatória
Enquanto /registros tem uma linha por publicação, aqui cada item é um atleta: quantos registros ele tem, quando apareceu pela primeira e pela última vez, o status do registro mais recente e — quando a consulta diária por atleta já rodou — onde ele está registrado hoje.
Parâmetros
Parâmetro
Tipo
Padrão
Descrição
busca
string
—
Procura por nome, apelido ou código, sem diferenciar maiúsculas nem acentos. O atleta entra na lista quando qualquer registro dele casa.
situacaoAtual vem da consulta diária por atleta e pode apontar outro clube (empréstimo para fora).
idade é calculada no fuso de Brasília; é null quando o BID não traz data de nascimento.
GET/api/v1/atletas/{codigo}chave obrigatória
A ficha completa de uma pessoa: o mesmo resumo da listagem, todos os registros dela no BID (do mais novo para o mais antigo), a última consulta diária do BID por atleta — inclusive jogos por categoria nos últimos 12 meses — e, se ela está no elenco atual, em qual grupo e categoria.
Parâmetros
Parâmetro
Tipo
Padrão
Descrição
codigo
string (caminho)
—
Código CBF do atleta. Código desconhecido ou fora do formato responde 404.
situacaoBid é null quando a consulta diária ainda não passou por esse atleta.
elenco.pertence é false para quem já teve registro no clube mas não tem vínculo ativo hoje.
GET/api/v1/elencochave obrigatória
O mesmo cálculo da página /elenco: a partir do registro mais recente de cada pessoa, quem segue com vínculo ativo hoje. Vem agrupado em comissão técnica, profissional, sub-20 com contrato profissional, base por ano de nascimento, registrados em outro clube e feminino.
Resposta resumida acima: cada pessoa traz ainda apelido, sexo, idade_temporada, contrato_reativado, categorias12m, ultimo_jogo e consultadoEm.
O grupo outro-clube só aparece quando existem consultas por atleta no banco.
GET/api/v1/estatisticaschave obrigatória
Números agregados do acervo: quantos registros e atletas, quanto veio do monitor e quanto da mineração do histórico, registros por ano, as últimas execuções do monitor e o progresso da mineração dia a dia desde 2003.
monitor.runs traz as 20 execuções mais recentes; foi omitido no exemplo.
GET/api/v1/saudesem chave
Único endpoint que não pede chave, para monitoramento externo. Responde 200 quando o banco responde e 503 quando não. Também diz até quando o acervo tem dados e quando o monitor rodou com sucesso pela última vez.
O contrato da API em OpenAPI 3.1, gerado do mesmo objeto que gera esta página. Serve para importar em Postman/Insomnia, gerar clientes ou alimentar uma ferramenta de IA.
As dez contratações mais recentes, com nome e vigência do contrato.
Filtre por categoria=contratacao para juntar as várias redações do BID ('Contrato Definitivo', 'Transferencia Definitiva'…).
Mantenha ordem=recentes (o padrão) e limit=10.
Leia apelido, data_publicacao, data_inicio e data_termino de cada item.
Python
import os, requests
chave = os.environ["BID_API_KEY"]
r = requests.get(
"https://bid.avaifc.com/api/v1/registros",
params={"categoria": "contratacao", "limit": 10},
headers={"Authorization": f"Bearer {chave}"},
timeout=30,
)
r.raise_for_status()
for reg in r.json()["registros"]:
nome = reg["apelido"] or reg["nome"]
print(reg["data_publicacao"][:10], nome, reg["tipo_contrato"],
f'{reg["data_inicio"]} → {reg["data_termino"]}')
Elenco atual
Listar o elenco profissional de hoje e quem está emprestado para fora.
Chame /elenco: ele já vem agrupado e calculado para o dia de hoje em Brasília.
Percorra grupos e, dentro de cada um, categorias e pessoas.
O grupo outro-clube são os emprestados para fora — eles não entram nas contagens do elenco.
Python
import os, requests
chave = os.environ["BID_API_KEY"]
r = requests.get("https://bid.avaifc.com/api/v1/elenco",
headers={"Authorization": f"Bearer {chave}"}, timeout=30)
r.raise_for_status()
elenco = r.json()
print("Elenco em", elenco["hoje"], "| dados até", elenco["dadosAte"])
for grupo in elenco["grupos"]:
print(f'\n== {grupo["titulo"]} ({grupo["total"]})')
for cat in grupo["categorias"]:
for p in cat["pessoas"]:
print(" -", p["apelido"] or p["nome"], "|", p["tipo_contrato"],
"| até", p["data_termino"])
Ficha de um atleta
Achar um atleta pelo nome e ler a história dele no clube.
Procure em /atletas?busca=<nome> e pegue o codigo_atleta do resultado.
Chame /atletas/{codigo} para o resumo, todos os registros e a situação atual no BID.
Se situacaoBid.clube_codigo não for 20058, ele está registrado em outro clube hoje.
Python
import os, requests
chave = os.environ["BID_API_KEY"]
sessao = requests.Session()
sessao.headers["Authorization"] = f"Bearer {chave}"
busca = sessao.get("https://bid.avaifc.com/api/v1/atletas",
params={"busca": "joao", "limit": 1}, timeout=30)
busca.raise_for_status()
atletas = busca.json()["atletas"]
if not atletas:
raise SystemExit("ninguém encontrado")
codigo = atletas[0]["codigo_atleta"]
ficha = sessao.get(f"https://bid.avaifc.com/api/v1/atletas/{codigo}", timeout=30)
ficha.raise_for_status()
d = ficha.json()
print(d["atleta"]["apelido"], "-", d["atleta"]["totalRegistros"], "registros")
print("No elenco agora?", d["elenco"]["pertence"], d["elenco"]["grupo"])
if d["situacaoBid"] and d["situacaoBid"]["clube_codigo"] != "20058":
print("Registrado hoje em:", d["situacaoBid"]["clube_nome"])
for reg in d["registros"][:5]:
print(" ", reg["data_publicacao"][:10], reg["tipo_contrato"])
Dicionário de dados
O que cada campo significa no BID.
Registro do BID
Um item de /registros e de registros[] na ficha do atleta. É a linha que a CBF publicou no BID.
Campo
Tipo
Significado
record_key
string
Identificador estável do registro, calculado por nós (atleta + contrato + tipo + publicação). Use-o para deduplicar: o BID republica a mesma linha quando o status muda.
codigo_atleta
string
Código CBF da pessoa. É a chave para /atletas/{codigo}.
nome
string | null
Nome civil completo, como o BID escreve (em geral em maiúsculas).
apelido
string | null
Nome esportivo, o que aparece na camisa.
tipo_contrato
string | null
Status ATUAL do registro no BID, não o evento que o criou. 'Contrato Definitivo', 'Contrato Emprestimo', 'Rescisão', 'Vinculo Encerrado', 'Reversão', 'Vínculo Não Profissional'… A redação varia muito ao longo dos anos.
id_contrato
string | null
Identificador do contrato no BID.
contrato_numero
string | null
Número do contrato no clube. Uma rescisão reaproveita o número do contrato que ela encerra.
data_publicacao
string | null
Quando a CBF publicou no BID, no horário de Brasília, no formato 'AAAA-MM-DD HH:MM:SS' (sem fuso: não passe por new Date() esperando UTC). É o campo de ordenação e dos filtros desde/ate.
data_inicio
string | null
Início da vigência do vínculo ('AAAA-MM-DD').
data_termino
string | null
Fim da vigência ('AAAA-MM-DD'). Em contratos de comissão técnica sem prazo o BID repete a data de início.
data_nascimento
string | null
Nascimento ('AAAA-MM-DD'), quando o BID informa.
sexo
string | null
Como o BID manda: '1' masculino, '0' feminino.
codigo_clube
string | null
Código do clube no BID. Aqui é sempre 20058 (Avaí Futebol Clube).
clube
string | null
Nome do clube como o BID escreve.
uf
string | null
Federação do clube ('SC').
origem
'monitor' | 'historico'
'monitor' = capturado ao vivo quando saiu; 'historico' = recuperado pela mineração dia a dia do passado. Só muda como o dado chegou até nós, não a confiabilidade.
first_seen_at
string | null
Quando gravamos o registro pela primeira vez (instante ISO, UTC).
notified_at
string | null
Quando o registro foi anunciado no canal do Telegram; null para o que veio do histórico.
Atleta
Um item de /atletas e o campo atleta da ficha: a pessoa, resumida a partir dos registros dela.
Campo
Tipo
Significado
codigo_atleta
string
Código CBF.
nome / apelido
string | null
Como no registro mais recente dele.
data_nascimento
string | null
'AAAA-MM-DD', do registro mais recente.
idade
inteiro | null
Anos completos hoje (Brasília); null sem data de nascimento.
sexo
string | null
'1' masculino, '0' feminino.
totalRegistros
inteiro
Quantos registros ele tem no clube — inclui as rescisões.
primeiroRegistro
string | null
data_publicacao do registro mais antigo dele.
ultimoRegistro
string | null
data_publicacao do registro mais recente dele.
ultimoTipo
string | null
tipo_contrato desse registro mais recente: é o que diz se ele ainda está no clube.
situacaoAtual
objeto | null
Da consulta diária do BID por atleta: clube_codigo, clube_nome, tipo_contrato e desde (data_inicio). Pode apontar outro clube. null quando essa consulta ainda não passou por ele.
url
string
Link absoluto para a página dele no site.
Situação no BID (situacaoBid)
A última consulta por atleta feita direto no BID, atualizada diariamente. É o que revela empréstimo para fora.
Campo
Tipo
Significado
clube_codigo / clube_nome
string | null
Clube do registro ATIVO hoje segundo o BID.
tipo_contrato
string | null
Status desse registro atual.
contrato_numero
string | null
Número do contrato atual.
data_inicio
string | null
Início do registro atual ('AAAA-MM-DD').
data_publicacao
string | null
Publicação do registro atual (Brasília, sem fuso).
jogos_total
inteiro | null
Jogos que o BID lista na carreira dele.
jogos_pelo_clube_12m
inteiro | null
Jogos pelo clube nos últimos 12 meses.
ultimo_jogo_data / _categoria / _campeonato
string | null
Último jogo que o BID registra, com a categoria ('Série B', 'Sub-20'…) e o campeonato.
categorias_12m
objeto | null
{categoria: jogos} nos últimos 12 meses. É daqui que sai a estimativa de base × profissional.
consultadoEm
string | null
Quando essa consulta rodou (Brasília, sem fuso).
erro
string | null
Preenchido quando a última tentativa falhou — os dados acima são de uma consulta anterior.
Elenco
A resposta de /elenco, igual à da página /elenco do site.
Campo
Tipo
Significado
hoje
string
Dia (Brasília) para o qual o elenco foi calculado.
dadosAte
string | null
Publicação mais recente que temos para o clube.
totais
objeto
atletas, comissaoTecnica, profissional, sub20Profissional, base, feminino e outroClube (estes últimos não entram na contagem do elenco).
consultaBid
objeto | null
ate (consulta mais recente), consultados e semConsulta. null quando não há consultas por atleta.
comissaoTecnica
lista
Contratos de treinador/assistente técnico ativos.
grupos
lista
profissional, sub20-profissional, base, outro-clube e feminino. Cada grupo tem categorias, e cada categoria tem pessoas.
pessoa.vinculo
'profissional' | 'base' | 'comissao'
Tipo de vínculo que o registro mais recente dela indica.
pessoa.jogos12m
objeto | null
profissional, base, outra e total — jogos nos últimos 12 meses por tipo de competição.
pessoa.registradoEmOutroClube
booleano
A consulta por atleta mostra registro ativo em outro clube (emprestado para fora).
pessoa.aviso
string | null
Ressalva sobre o dado (por exemplo contrato sem prazo e antigo).
Como interpretar (leia antes de publicar número)
O BID é preciso, mas diz menos do que parece.
tipo_contrato é o status atual, não o evento
O BID republica a linha de um registro quando ele muda, e o tipo descreve como ele está AGORA. Uma linha com 'Vinculo Encerrado' não quer dizer que o encerramento aconteceu naquele dia: quer dizer que, na publicação daquele dia, aquele vínculo estava encerrado. Para saber quem está no clube hoje, olhe o registro mais recente da pessoa (ultimoTipo) ou use /elenco.
Quem o Avaí empresta para fora não aparece com o Avaí
Um atleta emprestado é registrado pelo clube que o recebe, então ele não sai em /registros do Avaí enquanto estiver lá. A única forma de vê-lo é a consulta diária por atleta: situacaoAtual / situacaoBid aponta o outro clube, e no /elenco ele vai para o grupo outro-clube.
Base × profissional é estimativa nossa
O BID não diz em que categoria a pessoa joga. A separação do /elenco combina o tipo de vínculo, a idade por ano de nascimento (como nas categorias da CBF) e os jogos dos últimos 12 meses por categoria. Um sub-20 com contrato profissional e jogos em competição profissional é contado como profissional. É um palpite bem informado, não um dado oficial.
O BID não informa a função da comissão técnica
Todos os contratos de comissão vêm como 'Contrato Treinador/Assistente Técnico'. Não há como saber pelo BID quem é técnico principal, auxiliar, preparador ou da base. Contratos sem prazo só saem da lista quando o clube publica a rescisão, então registros antigos podem estar desatualizados.
Datas vêm no horário de Brasília, sem fuso
data_publicacao e companhia são strings 'AAAA-MM-DD HH:MM:SS' no horário de Brasília. Passar isso por new Date() faz o JavaScript ler como UTC e deslocar o dia. Trate como texto ou acrescente o fuso explicitamente. Só first_seen_at, notified_at e geradoEm são instantes ISO em UTC.
O histórico ainda pode estar sendo minerado
Os registros antigos são recuperados dia a dia desde 2003. Enquanto isso não termina, /estatisticas mostra o progresso em backfill, e registros novos podem aparecer no meio da lista — por isso a paginação por cursor é mais segura que por offset.
Versionamento e changelog
O caminho começa com /api/v1. Enquanto for v1, não removemos nem renomeamos campos e não mudamos o significado de nenhum deles.
Campos novos podem ser acrescentados a qualquer momento: ignore o que você não conhece em vez de validar a resposta de forma estrita.
Mudança que quebre compatibilidade só em /api/v2, com o v1 funcionando em paralelo por um tempo.
Changelog
v1 — 2026-10-02
Primeira versão pública: registros, atletas, ficha do atleta, elenco, estatísticas, saúde e OpenAPI.
Autenticação por chave de API, limite de 600 requisições por minuto por chave.