APIs Públicas Brasileiras com Python: IBGE, Banco Central e Dados.gov.br
Use a API dados.gov.br com Python e consulte IBGE e Banco Central. Veja endpoints, HTTPX, Pandas, erros 401 e exemplos para projetos de dados públicos.
A API do dados.gov.br documenta o catálogo de conjuntos de dados em GET /dados/api/publico/conjuntos-dados, enquanto sua especificação OpenAPI fica em https://dados.gov.br/v3/api-docs. Como o portal pode alterar caminhos e regras de acesso — inclusive responder 401 Unauthorized em uma rota descrita como pública —, a integração correta começa consultando essa especificação e tratando falhas sem assumir que o JSON sempre estará disponível.
Para projetos que precisam funcionar imediatamente, as APIs do IBGE e do Banco Central continuam sendo pontos de partida mais previsíveis. Neste guia você vai consumir essas fontes com Python, HTTPX e Pandas, descobrir os endpoints atuais do dados.gov.br e preparar o código para mudanças, timeout, respostas vazias e erros de autenticação.
Dados públicos brasileiros são uma das melhores fontes para criar projetos Python com utilidade real. Em vez de treinar apenas com datasets artificiais, você pode montar dashboards, alertas, análises de mercado, robôs de consulta e estudos de portfólio com fontes oficiais.
Esse tipo de projeto chama atenção porque combina três habilidades valorizadas no mercado: consumir APIs, limpar dados e transformar informação em decisão. Para quem busca vaga júnior, freelance ou transição para dados, um projeto com dados brasileiros costuma ser mais forte do que mais uma aplicação de tarefas genérica. Ele mostra contexto local, domínio técnico e capacidade de lidar com problemas reais.
Se a sua meta é usar esse trabalho para candidatura, veja também como organizar projetos Python de portfólio para conseguir vaga, com ideias de escopo, README, testes e apresentação no GitHub.
Neste guia, vamos montar uma base prática para consumir APIs públicas com Python, tratar erros, paginar respostas, transformar JSON em DataFrame e salvar resultados para análise. Se você ainda está começando com requisições HTTP, leia também nosso guia de Python e APIs e o comparativo entre HTTPX e requests.
Por que usar APIs públicas brasileiras?
APIs públicas ajudam a construir projetos com dados atualizados e verificáveis. No contexto brasileiro, isso abre várias possibilidades:
- acompanhar inflação, juros, câmbio e séries temporais do Banco Central;
- consultar indicadores demográficos do IBGE;
- cruzar municípios, estados, população e atividade econômica;
- monitorar despesas, votações e proposições legislativas;
- montar painéis setoriais para clientes locais;
- automatizar relatórios para áreas financeiras, comerciais e operacionais.
Para um portfólio, a vantagem é clara: o recrutador entende o problema sem precisar de explicação longa. Um dashboard de IPCA por região, uma análise de população por município ou um alerta de taxa Selic comunica valor de negócio imediatamente.
Também há um ganho técnico. APIs públicas raramente são perfeitas. Você vai lidar com paginação, limites, campos inconsistentes, datas em formatos diferentes, respostas vazias e documentação incompleta. Isso se parece muito mais com trabalho real do que consumir uma API de exemplo criada para tutorial.
Preparando o ambiente
Para os exemplos, vamos usar httpx e pandas. O básico roda com poucas dependências:
python -m venv .venv
source .venv/bin/activate
pip install httpx pandas
Com uv, o fluxo fica mais rápido:
uv init apis-publicas-br
cd apis-publicas-br
uv add httpx pandas
Se você ainda não usa uv, veja o guia completo de UV como gerenciador de pacotes Python. Para projetos de dados, a combinação uv + pandas + scripts pequenos costuma ser suficiente antes de criar uma aplicação web completa.
Exemplo 1: consultando localidades do IBGE
A API de localidades do IBGE é uma ótima porta de entrada porque não exige autenticação e retorna JSON simples. Vamos buscar os estados brasileiros:
import httpx
URL = "https://servicodados.ibge.gov.br/api/v1/localidades/estados"
resposta = httpx.get(URL, timeout=10)
resposta.raise_for_status()
estados = resposta.json()
for estado in sorted(estados, key=lambda item: item["nome"]):
print(f'{estado["sigla"]}: {estado["nome"]}')
O método raise_for_status() é importante. Sem ele, seu script pode continuar como se tudo estivesse certo mesmo quando a API retorna erro 404, 429 ou 500. Em automações reais, falhar cedo é melhor do que gerar relatório errado.
Agora vamos transformar a resposta em um DataFrame:
import pandas as pd
df = pd.DataFrame(estados)
df = df[["id", "sigla", "nome", "regiao"]]
df["regiao_nome"] = df["regiao"].apply(lambda regiao: regiao["nome"])
print(df[["sigla", "nome", "regiao_nome"]].head())
Esse pequeno ajuste já mostra um padrão comum: APIs retornam objetos aninhados, e você precisa normalizar os campos antes de analisar. Para se aprofundar nessa parte, confira a introdução ao Pandas e o guia de Python para ciência de dados.
Exemplo 2: buscando municípios por UF
Depois de listar estados, podemos consultar municípios de uma UF. Isso permite criar bases locais para análises comerciais, logísticas ou demográficas.
import httpx
import pandas as pd
def buscar_municipios(uf: str) -> pd.DataFrame:
url = f"https://servicodados.ibge.gov.br/api/v1/localidades/estados/{uf}/municipios"
resposta = httpx.get(url, timeout=15)
resposta.raise_for_status()
dados = resposta.json()
linhas = []
for item in dados:
microrregiao = item["microrregiao"]
mesorregiao = microrregiao["mesorregiao"]
estado = mesorregiao["UF"]
linhas.append({
"municipio_id": item["id"],
"municipio": item["nome"],
"uf": estado["sigla"],
"estado": estado["nome"],
"regiao": estado["regiao"]["nome"],
"microrregiao": microrregiao["nome"],
"mesorregiao": mesorregiao["nome"],
})
return pd.DataFrame(linhas)
municipios_sp = buscar_municipios("SP")
print(municipios_sp.head())
print(f"Total de municípios: {len(municipios_sp)}")
Esse código já pode virar a base de um projeto de portfólio: um dashboard de municípios por região, um enriquecedor de planilhas comerciais ou um serviço interno para validar cadastros. Se quiser persistir os resultados localmente, salve em SQLite:
import sqlite3
with sqlite3.connect("dados_brasil.db") as conexao:
municipios_sp.to_sql("municipios", conexao, if_exists="replace", index=False)
Temos um guia separado sobre Python e SQLite caso você queira transformar scripts em uma base consultável.
Exemplo 3: série temporal do Banco Central
O Banco Central do Brasil oferece o Sistema Gerenciador de Séries Temporais (SGS), muito usado para buscar indicadores como Selic, IPCA, dólar e outras séries econômicas. O endpoint público segue um formato previsível:
import httpx
import pandas as pd
def buscar_serie_bcb(codigo: int, data_inicial: str, data_final: str) -> pd.DataFrame:
url = f"https://api.bcb.gov.br/dados/serie/bcdata.sgs.{codigo}/dados"
params = {
"formato": "json",
"dataInicial": data_inicial,
"dataFinal": data_final,
}
resposta = httpx.get(url, params=params, timeout=20)
resposta.raise_for_status()
df = pd.DataFrame(resposta.json())
df["data"] = pd.to_datetime(df["data"], format="%d/%m/%Y")
df["valor"] = pd.to_numeric(df["valor"].str.replace(",", "."), errors="coerce")
return df
selic = buscar_serie_bcb(432, "01/01/2025", "31/12/2025")
print(selic.tail())
O código 432 representa a meta Selic. O ponto técnico mais importante aqui é a conversão de tipos: datas vêm como texto, valores podem vir com vírgula decimal, e sua análise só será confiável depois de normalizar esses campos.
Com isso, você pode gerar um gráfico simples:
import matplotlib.pyplot as plt
selic.plot(x="data", y="valor", legend=False, title="Meta Selic em 2025")
plt.ylabel("% ao ano")
plt.tight_layout()
plt.show()
Para transformar isso em produto, combine com Streamlit e publique um painel simples. Um dashboard que cruza Selic, inflação e indicadores regionais já é um ótimo projeto para entrevistas e freelances.
Exemplo 4: consultando a API do dados.gov.br
A fonte mais segura para descobrir os caminhos atuais do portal é a especificação OpenAPI publicada pelo próprio dados.gov.br:
https://dados.gov.br/v3/api-docs
Na atualização deste guia, a especificação lista estas operações de leitura do catálogo:
| Operação | Endpoint documentado |
|---|---|
| Listar conjuntos | GET /dados/api/publico/conjuntos-dados?pagina=1 |
| Detalhar um conjunto | GET /dados/api/publico/conjuntos-dados/{id} |
| Listar tags do conjunto | GET /dados/api/publico/conjuntos-dados/{id}/tag |
| Listar formatos | GET /dados/api/publico/conjuntos-dados/formatos |
O primeiro passo de uma integração resistente é ler a documentação de máquina e confirmar se o caminho esperado ainda existe:
from typing import Any
import httpx
OPENAPI_URL = "https://dados.gov.br/v3/api-docs"
def carregar_openapi() -> dict[str, Any]:
resposta = httpx.get(
OPENAPI_URL,
headers={"Accept": "application/json"},
timeout=20,
follow_redirects=True,
)
resposta.raise_for_status()
return resposta.json()
def endpoints_de_conjuntos() -> list[str]:
especificacao = carregar_openapi()
caminhos = especificacao.get("paths", {})
return sorted(
caminho
for caminho in caminhos
if "conjuntos-dados" in caminho
)
for endpoint in endpoints_de_conjuntos():
print(endpoint)
Esse código não baixa o catálogo: ele verifica o contrato publicado pela API. Isso é útil porque uma URL antiga encontrada em tutorial, cache ou mecanismo de busca pode deixar de existir, mudar de prefixo ou ganhar uma regra de acesso.
Tentando listar conjuntos e tratando o erro 401
A operação de catálogo aceita pagina e documenta filtros como nomeConjuntoDados, dadosAbertos e idOrganizacao. Uma função de consulta pode ser escrita assim:
from typing import Any
import httpx
CATALOGO_URL = "https://dados.gov.br/dados/api/publico/conjuntos-dados"
class CatalogoIndisponivel(RuntimeError):
pass
def buscar_conjuntos(termo: str, pagina: int = 1) -> dict[str, Any]:
params = {
"pagina": pagina,
"nomeConjuntoDados": termo,
"dadosAbertos": "true",
}
try:
resposta = httpx.get(
CATALOGO_URL,
params=params,
headers={"Accept": "application/json"},
timeout=20,
follow_redirects=True,
)
except httpx.RequestError as erro:
raise CatalogoIndisponivel(
f"Falha de rede ao consultar dados.gov.br: {erro}"
) from erro
if resposta.status_code == 401:
raise CatalogoIndisponivel(
"O dados.gov.br respondeu 401. "
"Consulte a especificação OpenAPI e as regras atuais de acesso."
)
resposta.raise_for_status()
try:
return resposta.json()
except ValueError as erro:
raise CatalogoIndisponivel(
"O portal não devolveu um JSON válido."
) from erro
Em vez de ocultar o problema com tentativas infinitas, a função produz um erro específico e acionável. Um 401 Unauthorized quer dizer que o servidor recusou a chamada por autenticação ou autorização. Mesmo que o nome da rota contenha publico, o gateway do portal pode estar em manutenção ou ter mudado a política de acesso.
Não coloque tokens aleatórios, cookies copiados do navegador ou credenciais encontradas em fóruns para “fazer funcionar”. Se o catálogo responder 401, registre a ocorrência, confira https://dados.gov.br/v3/api-docs e a documentação oficial, e mantenha uma fonte alternativa no pipeline. Para um projeto de portfólio, você pode continuar com IBGE ou Banco Central e deixar a integração do catálogo isolada atrás dessa função.
Como inspecionar a resposta sem depender de nomes fixos
Quando a chamada estiver autorizada e retornar JSON, examine primeiro as chaves do objeto antes de assumir que a lista se chama content:
payload = buscar_conjuntos("educação")
print("Chaves recebidas:", sorted(payload))
itens = (
payload.get("content")
or payload.get("resultados")
or payload.get("items")
or []
)
for item in itens[:5]:
titulo = (
item.get("titulo")
or item.get("title")
or item.get("nome")
or "Sem título"
)
print(titulo)
Esse fallback é útil durante diagnóstico, mas não deve esconder uma mudança de contrato em produção. Depois de observar a resposta real, crie um modelo explícito com os campos usados pela aplicação e falhe se uma informação obrigatória desaparecer. O guia de Pydantic para validação de dados mostra como transformar esse contrato em código.
APIs governamentais mudam com o tempo. Por isso, escreva funções pequenas, valide a estrutura da resposta e trate campos opcionais com .get(). Em vez de espalhar acesso direto a chaves por todo o projeto, concentre a adaptação em uma camada de coleta.
Como organizar um projeto real
Um projeto pequeno pode começar assim:
apis-publicas-br/
├── pyproject.toml
├── README.md
├── src/
│ └── apis_publicas_br/
│ ├── __init__.py
│ ├── ibge.py
│ ├── bcb.py
│ └── dados_gov.py
├── scripts/
│ ├── atualizar_municipios.py
│ └── atualizar_selic.py
└── data/
└── dados_brasil.db
Essa divisão evita que o projeto vire um notebook gigante difícil de manter. Os módulos em src/ cuidam de coleta e normalização. Os scripts executam tarefas. A pasta data/ guarda saídas locais que podem ser ignoradas no Git se ficarem grandes.
Para automações recorrentes, adicione logs e códigos de saída previsíveis. Nosso guia de logging em Python ajuda a deixar scripts mais operacionais.
Boas práticas para APIs públicas
Alguns cuidados fazem diferença quando o script sai do tutorial e vira rotina:
- Defina timeout em toda requisição: nunca deixe uma chamada HTTP travar indefinidamente.
- Use
raise_for_status(): trate erro HTTP como erro de verdade. - Diferencie falhas:
401pede revisão de acesso;404, revisão do endpoint;429, redução de frequência;5xx, retry limitado com espera. - Consulte o contrato atual: quando houver OpenAPI, use a especificação oficial para confirmar caminhos, parâmetros e modelos.
- Respeite limites de chamada: se a API rate-limitou, reduza frequência e use cache.
- Salve respostas brutas quando necessário: isso facilita depuração quando o formato muda.
- Normalize tipos cedo: datas, números e booleanos devem chegar limpos à análise.
- Documente fontes: anote URL, data de coleta e significado das colunas.
- Não confunda dado público com dado livre de responsabilidade: valide contexto antes de publicar conclusões.
Em projetos profissionais, também vale separar coleta, transformação e apresentação. Essa arquitetura simples facilita testes, reprocessamento e troca de fonte. Se a API do IBGE mudar, você altera ibge.py, não o dashboard inteiro.
Ideias de projetos para portfólio
Aqui vão ideias que combinam Python, dados públicos e contexto brasileiro:
- painel de evolução da Selic, IPCA e câmbio com atualização semanal;
- ranking de municípios por população e região usando dados do IBGE;
- mapa de indicadores educacionais por estado;
- alerta por email quando uma série do Banco Central muda acima de um limite;
- enriquecedor de planilhas com município, UF e região;
- API própria em FastAPI que encapsula consultas a fontes públicas;
- dashboard de dados abertos para um setor específico, como varejo, saúde suplementar ou logística.
Para quem trabalha como freelancer, esses projetos podem virar ofertas concretas: relatórios automatizados, painéis internos e integrações com planilhas. Veja também nosso guia sobre Python freelancer e o artigo de automação de planilhas com Python.
Se o seu foco é backend, uma evolução natural é expor os dados tratados via API própria. Comece com FastAPI e compare quando faz sentido usar outra linguagem para serviços de alta concorrência, como Go no backend.
Perguntas frequentes
Qual é o endpoint da API do dados.gov.br?
A especificação OpenAPI atual lista GET /dados/api/publico/conjuntos-dados para consultar o catálogo e GET /dados/api/publico/conjuntos-dados/{id} para detalhar um conjunto. Confirme sempre em https://dados.gov.br/v3/api-docs, pois caminhos e políticas podem mudar.
Por que a API dados.gov.br retorna 401?
O status 401 Unauthorized indica que o servidor não autorizou a chamada. Não tente contornar a resposta com credenciais desconhecidas. Consulte a documentação oficial, registre o erro e use uma fonte alternativa até a rota pública voltar a aceitar sua requisição.
Qual API pública brasileira é melhor para iniciantes?
A API de localidades do IBGE é uma boa primeira escolha: os objetos são compreensíveis e permitem projetos com estados e municípios. O SGS do Banco Central é ótimo para praticar datas, valores numéricos e séries temporais.
HTTPX ou requests: qual usar?
As duas funcionam. HTTPX tem uma API moderna e opção assíncrona; requests é amplamente conhecida. O mais importante é definir timeout, validar status, tratar JSON inválido e testar o comportamento em falhas.
Como transformar JSON de API em Pandas?
Use pandas.DataFrame quando a resposta contém uma lista de objetos relativamente planos. Para estruturas aninhadas, pandas.json_normalize pode ajudar. Depois, converta datas e números explicitamente e valide colunas obrigatórias.
Conclusão
Consumir APIs públicas brasileiras com Python é uma forma prática de sair do estudo abstrato e construir algo que parece trabalho real. Você aprende HTTP, tratamento de erro, limpeza de dados, persistência, visualização e organização de projeto em um único fluxo.
Comece pequeno: consulte estados do IBGE, salve em SQLite e gere uma tabela limpa. Depois adicione uma série do Banco Central, crie um gráfico e publique um dashboard. Ao explorar o dados.gov.br, descubra primeiro o contrato em /v3/api-docs e trate 401, 404, 429 e mudanças de JSON como situações esperadas de integração — não como detalhes para ignorar.
O ponto principal é tratar dados públicos como matéria-prima para produtos pequenos. Python brilha exatamente nesse espaço: automação rápida, bibliotecas maduras e integração fácil com APIs, bancos, dashboards e pipelines. Para transformar a coleta em uma rotina de produção, continue com ETL em Python e qualidade de dados com Pandera.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português