Python · algoritmo local + API

Validar CEP em Python

Para validar CEP em Python, confira o formato de 8 dígitos com hífen opcional e consulte o ViaCEP, porque o CEP não tem dígito verificador. Formato correto não garante que o CEP existe: 00000-000 passa na regex e não está na base dos Correios. Abaixo: uma função Python só com a biblioteca padrão para o formato, a consulta de existência e testes sem depender de rede.

Fonte: estrutura do CEP definida pelos Correios. A consulta de existência usa o ViaCEP, serviço público e gratuito.

TL;DR

import json
import re
import urllib.request

cep = "01310-100"
assert re.fullmatch(r"\d{5}-?\d{3}", cep), "formato inválido"

with urllib.request.urlopen(f"https://viacep.com.br/ws/{cep.replace('-', '')}/json/", timeout=5) as resp:
    dados = json.load(resp)

print("não existe" if dados.get("erro") else f"{dados['logradouro']}, {dados['localidade']}/{dados['uf']}")

Python 3.9+, sem pip install. Salve cada bloco no arquivo indicado no topo.

Rota 1: validar o formato do CEP em Python

Validação offline, instantânea e sem rede. Serve para barrar erro de digitação no formulário antes de qualquer consulta.

# validador_cep.py: formato (offline, só stdlib)
import re

FORMATO_CEP = re.compile(r"\d{5}-?\d{3}")


def cep_formato_valido(cep: str) -> bool:
    """8 dígitos, hífen opcional depois do quinto. Não confirma que o CEP existe."""
    return FORMATO_CEP.fullmatch(cep.strip()) is not None


def normaliza_cep(cep: str) -> str:
    """'01310-100' -> '01310100'. Levanta ValueError se o formato for inválido."""
    if not cep_formato_valido(cep):
        raise ValueError(f"CEP com formato inválido: {cep!r}")
    return cep.strip().replace("-", "")


if __name__ == "__main__":
    for cep in ("01310-100", "01310100", "1310-100", "01310-10", "abcde-fgh"):
        print(f"{cep!r:14} -> {cep_formato_valido(cep)}")

Como o CEP é estruturado (e por que não existe dígito verificador)

Um exemplo calculado à mão, para você conferir o código contra a conta.

CEP 01310-100

0 1 3 1 0 - 1 0 0
| | | | |   +-- 3 últimos dígitos: sufixo de distribuição (identifica o logradouro ou a unidade)
| | | | +-- 5º dígito: subdivisor de subsetor
| | | +-- 4º dígito: subsetor
| | +-- 3º dígito: setor
| +-- 2º dígito: subregião
+-- 1º dígito: região postal (0 = Grande São Paulo)

Não há dígito verificador: nenhuma conta prova que o CEP existe.
Só a consulta à base (ViaCEP) confirma.

Rota 2: confirmar que o CEP existe com o ViaCEP

O ViaCEP responde 400 quando o formato é inválido e 200 com o campo erro quando o formato está certo mas o CEP não existe. O código abaixo trata os dois casos e deixa falha de rede propagar, sem confundir queda de serviço com CEP inexistente.

# existencia_cep.py: consulta ViaCEP. Salve a Rota 1 como validador_cep.py
import json
import urllib.error
import urllib.request

from validador_cep import cep_formato_valido, normaliza_cep


def cep_existe(cep: str, timeout: float = 5.0) -> bool:
    """True se o CEP existe na base do ViaCEP. Erros de rede propagam como URLError."""
    if not cep_formato_valido(cep):
        return False
    try:
        with urllib.request.urlopen(f"https://viacep.com.br/ws/{normaliza_cep(cep)}/json/", timeout=timeout) as resp:
            dados = json.load(resp)
    except urllib.error.HTTPError as e:
        if e.code == 400:  # ViaCEP responde 400 quando o formato é inválido
            return False
        raise
    return not dados.get("erro")  # CEP inexistente: 200 com {"erro": "true"}


if __name__ == "__main__":
    print(cep_existe("01310-100"))  # True (Av. Paulista, São Paulo)
    print(cep_existe("99999-999"))  # False (formato ok, não existe)
    print(cep_existe("1234"))       # False (formato inválido, nem consulta)

Como testar o validador de CEP com pytest

Rode com pytest -q. Os testes trocam o urlopen por um dublê, então rodam em CI sem acessar a internet.

# test_validador_cep.py (pytest, sem rede: urlopen é trocado por um dublê)
import json
import urllib.request

import pytest

from existencia_cep import cep_existe
from validador_cep import cep_formato_valido, normaliza_cep


@pytest.mark.parametrize("cep", ["01310-100", "01310100", " 01310-100 "])
def test_formato_valido(cep):
    assert cep_formato_valido(cep)


@pytest.mark.parametrize("cep", ["1310-100", "01310-10", "013101000", "abcde-fgh", "", "01310--100"])
def test_formato_invalido(cep):
    assert not cep_formato_valido(cep)


def test_normaliza():
    assert normaliza_cep("01310-100") == "01310100"
    with pytest.raises(ValueError):
        normaliza_cep("123")


class RespostaFalsa:
    def __init__(self, corpo):
        self._corpo = json.dumps(corpo).encode()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        return False

    def read(self, *args):
        return self._corpo


def test_cep_existente(monkeypatch):
    monkeypatch.setattr(urllib.request, "urlopen", lambda url, timeout=None: RespostaFalsa({"cep": "01310-100", "uf": "SP"}))
    assert cep_existe("01310-100") is True


def test_cep_inexistente(monkeypatch):
    monkeypatch.setattr(urllib.request, "urlopen", lambda url, timeout=None: RespostaFalsa({"erro": "true"}))
    assert cep_existe("99999-999") is False


def test_formato_invalido_nem_consulta(monkeypatch):
    def nao_deveria_chamar(*args, **kwargs):
        raise AssertionError("consultou a rede com CEP inválido")

    monkeypatch.setattr(urllib.request, "urlopen", nao_deveria_chamar)
    assert cep_existe("123") is False

Validar CEP: algoritmo local ou serviço externo

CenárioRegex localConsulta ao ViaCEP
Confere o formato de 8 dígitosSimSim, responde 400
Confirma que o CEP existeNãoSim
Devolve logradouro, bairro, cidade e UFNãoSim
Funciona offlineSimNão
LatênciaDesprezívelDepende da rede
CustoR$ 0Gratuito

Perguntas frequentes sobre validar CEP em Python

Como validar CEP em Python?+

Valide o formato com a regex \d{5}-?\d{3} e depois consulte https://viacep.com.br/ws/{cep}/json/. Resposta com o campo erro significa CEP inexistente. O código desta página usa só urllib da biblioteca padrão.

CEP tem dígito verificador?+

Não. O CEP tem 8 dígitos, escritos como 5 + hífen + 3, e nenhum deles é verificador. Validar um CEP significa checar o formato e depois confirmar a existência numa base, como o ViaCEP.

Como checar se um CEP existe?+

Consulte GET https://viacep.com.br/ws/{cep}/json/ com os 8 dígitos. Resposta 200 com dados significa que existe. Resposta 200 com o campo erro significa formato certo e CEP inexistente. HTTP 400 significa formato inválido.

O CEP 00000-000 é válido?+

No formato, sim: são 8 dígitos. Na existência, não: o ViaCEP devolve erro. É o exemplo clássico de por que só a regex não basta.

A API do FakeForge valida CEP?+

Não. A API gera CEPs com prefixo de região coerente (type=cep), mas não garante que cada um exista na base dos Correios. Para confirmar existência, use o ViaCEP ou a página /buscar-cep.

Páginas relacionadas