consulta-cep
Endereço a partir do CEP em uma linha, consultando várias APIs públicas brasileiras ao mesmo tempo e devolvendo a primeira que responder.
Por que usar
- Várias APIs gratuitas, sem chave: BrasilAPI, ViaCEP, OpenCEP e AwesomeAPI.
- Fallback automático: se uma API cair, demorar ou não conhecer o CEP, a resposta vem de outra. Por padrão todas são consultadas ao mesmo tempo e vale a primeira que responder.
- Síncrona e assíncrona:
consulta_cep()econsulta_cep_async(), com os mesmos parâmetros. - Tipada:
py.typed, verificada commypy --strict. - Uma dependência só: httpx.
- Erros claros: exceções específicas para CEP inválido, CEP inexistente e
serviços fora do ar, em vez de
None. - Extras: cache em memória opcional, cliente
httpxpróprio (proxy, conexões reaproveitadas) e linha de comando.
Instalação
pip install consulta-cep
Requer Python 3.10 ou mais recente.
Uso rápido
from consulta_cep import consulta_cep
endereco = consulta_cep("01001-000")
print(f"{endereco.logradouro}, {endereco.bairro} - {endereco.cidade}/{endereco.estado}")
# Saída:
# Praça da Sé, Sé - São Paulo/SP
O CEP pode vir como 01001-000, 01001000 ou 01.001-000 (espaços nas
pontas são ignorados).
O endereço
consulta_cep() devolve um Endereco:
>>> from consulta_cep import consulta_cep
>>> endereco = consulta_cep("01001-000", servicos="viacep")
>>> endereco
Endereco(servico='viacep', estado='SP', cidade='São Paulo', bairro='Sé', logradouro='Praça da Sé', cep='01001-000', complemento='lado ímpar', ibge='3550308', ddd='11', latitude=None, longitude=None)
>>> endereco.to_dict()["ibge"]
'3550308'
>>> print(endereco.to_json())
{"servico": "viacep", "estado": "SP", "cidade": "São Paulo", "bairro": "Sé", "logradouro": "Praça da Sé", "cep": "01001-000", "complemento": "lado ímpar", "ibge": "3550308", "ddd": "11", "latitude": null, "longitude": null}
| Campo | Tipo | Observação |
|---|---|---|
servico |
str |
Nome do serviço que respondeu. |
estado |
str |
Sempre a sigla da UF ("SP"). |
cidade |
str |
|
bairro |
str | None |
|
logradouro |
str | None |
|
cep |
str | None |
Formato 12345-678. |
complemento |
str | None |
|
ibge |
str | None |
Código IBGE do município. |
ddd |
str | None |
|
latitude, longitude |
float | None |
Campos que o serviço não informa (ou informa vazios) ficam None.
str(endereco) devolve o mesmo JSON de to_json().
Serviços
Por padrão, os quatro são consultados.
| Nome | API | Campos além de estado, cidade, bairro, logradouro e CEP |
|---|---|---|
brasilapi |
BrasilAPI (brasilapi.com.br/api/cep/v2/{cep}) |
latitude, longitude (quando disponíveis) |
viacep |
ViaCEP (viacep.com.br/ws/{cep}/json/) |
complemento, ibge, ddd |
opencep |
OpenCEP (opencep.com/v1/{cep}) |
complemento, ibge |
awesomeapi |
AwesomeAPI (cep.awesomeapi.com.br/json/{cep}) |
ibge, ddd, latitude, longitude |
>>> from consulta_cep import SERVICOS_PADRAO, servicos_disponiveis
>>> servicos_disponiveis()
['brasilapi', 'viacep', 'opencep', 'awesomeapi']
>>> SERVICOS_PADRAO
('brasilapi', 'viacep', 'opencep', 'awesomeapi')
Escolhendo serviços e estratégia
from consulta_cep import consulta_cep
# Só alguns serviços, na ordem de preferência (nomes ou instâncias)
endereco = consulta_cep("01001-000", servicos=["viacep", "awesomeapi"])
# Um por vez, na ordem, parando no primeiro sucesso: poupa requisições
endereco = consulta_cep(
"01001-000",
servicos=["viacep", "brasilapi", "opencep"],
estrategia="sequencial",
)
# Tempo máximo de cada requisição, em segundos (padrão: 5)
endereco = consulta_cep("01001-000", timeout=2)
| Estratégia | Comportamento |
|---|---|
"concorrente" (padrão) |
Consulta todos ao mesmo tempo e devolve o primeiro que responder com sucesso, sem esperar os demais. |
"sequencial" |
Tenta um por vez, na ordem de servicos, e para no primeiro sucesso. |
Tratando erros
consulta_cep() nunca devolve None: ou devolve um Endereco, ou lança uma
exceção. Todas herdam de ConsultaCEPError.
| Exceção | Quando |
|---|---|
CEPInvalidoError (também é ValueError) |
O CEP não tem um formato válido. Nenhuma requisição é feita. |
CEPNaoEncontradoError |
Algum serviço respondeu que o CEP não existe e nenhum devolveu endereço. |
ServicosIndisponiveisError |
Nenhum serviço respondeu; o atributo erros traz o erro de cada um. |
from consulta_cep import (
CEPInvalidoError,
CEPNaoEncontradoError,
ServicosIndisponiveisError,
consulta_cep,
)
for cep in ["01001-000", "99999-999", "123"]:
try:
endereco = consulta_cep(cep)
except CEPInvalidoError:
print(f"{cep}: formato inválido")
except CEPNaoEncontradoError:
print(f"{cep}: CEP não existe")
except ServicosIndisponiveisError as erro:
for servico, falha in erro.erros.items():
print(f"{cep}: {servico} falhou ({falha})")
else:
print(f"{cep}: {endereco.cidade}/{endereco.estado}")
# Saída:
# 01001-000: São Paulo/SP
# 99999-999: CEP não existe
# 123: formato inválido
Assíncrono (asyncio)
consulta_cep_async() tem os mesmos parâmetros e exceções. Na estratégia
concorrente, devolve o primeiro sucesso e cancela as consultas que ainda não
terminaram.
import asyncio
from consulta_cep import consulta_cep_async
async def main() -> None:
endereco = await consulta_cep_async("01001-000")
print(endereco.cidade)
asyncio.run(main())
# Saída:
# São Paulo
Cache
Desligado por padrão. Com cache=True, os endereços encontrados ficam num
cache em memória (LRU, até 1024 CEPs, válidos por 24 horas), seguro entre
threads e compartilhado entre consulta_cep() e consulta_cep_async().
Falhas e CEPs não encontrados nunca são guardados.
from consulta_cep import CacheLRU, consulta_cep, limpar_cache
consulta_cep("01001-000", cache=True) # consulta os serviços
consulta_cep("01001-000", cache=True) # vem do cache, sem requisição
limpar_cache()
# Cache próprio, com outro tamanho e validade (em segundos)
meu_cache = CacheLRU(maximo=100, ttl=60 * 60)
consulta_cep("01001-000", cache=meu_cache)
O cache é indexado só pelo CEP: um acerto devolve o endereço guardado, seja qual for o serviço que o obteve.
Usando seu próprio cliente httpx
Passe um httpx.Client (ou httpx.AsyncClient na versão assíncrona) para
reaproveitar conexões entre consultas ou configurar proxy, cabeçalhos e
certificados. O cliente não é fechado pela biblioteca. O timeout da consulta
vale para cada requisição, mesmo que o cliente tenha outro configurado.
import httpx
from consulta_cep import consulta_cep
with httpx.Client(proxy="http://proxy.empresa.local:3128") as client:
endereco = consulta_cep("01001-000", client=client)
import asyncio
import httpx
from consulta_cep import consulta_cep_async
async def consultar_varios(ceps: list[str]) -> None:
async with httpx.AsyncClient(headers={"User-Agent": "minha-app/1.0"}) as client:
enderecos = await asyncio.gather(
*(consulta_cep_async(cep, client=client) for cep in ceps)
)
for endereco in enderecos:
print(endereco.cep, endereco.cidade)
asyncio.run(consultar_varios(["01001-000", "01.001-000"]))
# Saída:
# 01001-000 São Paulo
# 01001-000 São Paulo
Logs
As falhas de cada serviço são registradas no logger consulta_cep do módulo
logging (nível WARNING; CEP não encontrado em INFO). Se a sua aplicação
não configura logging, o Python mostra esses avisos na saída de erro. Para
escondê-los:
import logging
logging.getLogger("consulta_cep").setLevel(logging.ERROR)
Linha de comando
Instalar o pacote também instala o comando consulta-cep (equivalente a
python -m consulta_cep).
$ consulta-cep 01001-000 --servico viacep
CEP: 01001-000
Logradouro: Praça da Sé
Complemento: lado ímpar
Bairro: Sé
Cidade: São Paulo
Estado: SP
IBGE: 3550308
DDD: 11
Serviço: viacep
$ consulta-cep 01001000 --servico viacep --formato json
{"servico": "viacep", "estado": "SP", "cidade": "São Paulo", "bairro": "Sé", "logradouro": "Praça da Sé", "cep": "01001-000", "complemento": "lado ímpar", "ibge": "3550308", "ddd": "11", "latitude": null, "longitude": null}
$ consulta-cep 99999-999 --servico viacep
99999-999: CEP não encontrado: 99999999.
$ echo $?
1
consulta-cep 01001-000 01.001-000 --formato json # vários CEPs: um JSON por linha
consulta-cep 01001-000 -s viacep -s brasilapi --estrategia sequencial
consulta-cep 01001-000 --timeout 2
python -m consulta_cep 01001-000
| Opção | |
|---|---|
-s, --servico |
Serviço a consultar; repita para usar mais de um, na ordem de preferência. |
-e, --estrategia |
concorrente (padrão) ou sequencial. |
-t, --timeout |
Tempo máximo de cada requisição, em segundos (padrão: 5). |
-f, --formato |
texto (padrão) ou json (um objeto por linha). |
--version |
Mostra a versão. |
Erros vão para a saída de erro (em JSON, com --formato json). Códigos de
saída: 0 sucesso, 1 CEP não encontrado, 2 CEP inválido, 3 serviços
indisponíveis. Com vários CEPs, vale o maior código.
Migrando da 0.2
A 1.0 tem mudanças incompatíveis. As principais:
-
Python 3.10 ou mais recente (antes: 3.6).
-
httpxno lugar derequests. Erros de rede guardados emServicosIndisponiveisError.errossão exceções do httpx (httpx.HTTPStatusError,httpx.ConnectError,httpx.TimeoutException…). -
Exceções no lugar de
None. Antes, se nenhum serviço respondia,consulta_cep()imprimia o erro e devolviaNone:# 0.2 endereco = consulta_cep("01001-000") if endereco is None: ...
Agora:
# 1.0 try: endereco = consulta_cep("01001-000") except ConsultaCEPError: ...
CEP inválido continua sendo
ValueError(CEPInvalidoErrorherda dele). -
Sem
print(). As falhas de cada serviço vão para o loggerconsulta_cep(veja Logs). -
Endereco:servicopassa a ser o nome curto ("brasilapi"), não mais"BrasilAPI"/"PostMon";bairroelogradouropodem serNone;- novos campos
cep,complemento,ibge,ddd,latitudeelongitude(no fim, com padrãoNone; a ordem dos campos antigos não mudou); str(endereco)mantém os acentos e inclui os campos novos;estadoé validado: criar umEnderecocom uma UF inexistente lançaValueError.
-
Serviços: a lista padrão agora é BrasilAPI (v2), ViaCEP, OpenCEP e AwesomeAPI. O Postmon foi removido: a API dele foi desativada (o domínio
api.postmon.com.brnão existe mais), eservicos=["postmon"]lançaValueError. -
Serviços próprios: subclasses de
ConsultaCEPimplementamconverter(dados, cep)(e definemnomeeURL) em vez deconsultar. -
Linha de comando: a saída padrão agora é texto (use
--formato jsonpara JSON); erros vão para a saída de erro com código de saída diferente de zero.
O CHANGELOG tem a lista completa.
Contribuindo
Contribuições são bem-vindas, inclusive novas APIs de CEP. Veja o CONTRIBUTING.md para preparar o ambiente, rodar os testes e adicionar um serviço. Encontrou um problema? Abra uma issue.
Licença
Metadata
Release files for consulta-cep 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| consulta_cep-1.0.0.tar.gz | 32.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| consulta_cep-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.1 kB
Release files / consulta_cep-1.0.0.tar.gz
| Download URL | consulta_cep-1.0.0.tar.gz |
|---|---|
| Size | 32.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e49c69ecfae7b6766fe19558b525d077955fa7c3e35941395e8eecf91867e661
|
|
BLAKE2b-256 checksum How to use checksums |
e20c4e8dd530cd4390a54e75a91f07096493b0f3a8a41914c43e813c3e6e8587
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / consulta_cep-1.0.0-py3-none-any.whl
| Download URL | consulta_cep-1.0.0-py3-none-any.whl |
|---|---|
| Size | 19.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
635c6746b9a031cd44a8d659f9c23790c5466377bd2baefbaa22147ef8e99a2c
|
|
BLAKE2b-256 checksum How to use checksums |
bbbfb2207ec9fbacc97fea74fa17c3e32d98c2196baf2de2513856d92fc9d939
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log