Skip to main content

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.

PyPI Python CI Licença: MIT Downloads

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() e consulta_cep_async(), com os mesmos parâmetros.
  • Tipada: py.typed, verificada com mypy --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 httpx pró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).

  • httpx no lugar de requests. Erros de rede guardados em ServicosIndisponiveisError.erros sã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 devolvia None:

    # 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 (CEPInvalidoError herda dele).

  • Sem print(). As falhas de cada serviço vão para o logger consulta_cep (veja Logs).

  • Endereco:

    • servico passa a ser o nome curto ("brasilapi"), não mais "BrasilAPI"/"PostMon";
    • bairro e logradouro podem ser None;
    • novos campos cep, complemento, ibge, ddd, latitude e longitude (no fim, com padrão None; a ordem dos campos antigos não mudou);
    • str(endereco) mantém os acentos e inclui os campos novos;
    • estado é validado: criar um Endereco com uma UF inexistente lança ValueError.
  • 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.br não existe mais), e servicos=["postmon"] lança ValueError.

  • Serviços próprios: subclasses de ConsultaCEP implementam converter(dados, cep) (e definem nome e URL) em vez de consultar.

  • Linha de comando: a saída padrão agora é texto (use --formato json para 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

MIT

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)

Source distribution for consulta-cep 1.0.0
File Size Uploaded
consulta_cep-1.0.0.tar.gz 32.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for consulta-cep 1.0.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page