EmailCheckerEmailChecker

Documentação · Python

Validar email em Python

Python tem várias bibliotecas HTTP boas. Recomendamos httpx (suporta async + sync com mesma API) ou requests (padrão, mais conhecido). O fluxo é em lote: envia os emails, guarda o id e consulta até fechar. Os exemplos abaixo usam httpx — adapte trivialmente pra requests.

Instalação (pip / poetry / uv)

bash
pip install httpx
# ou:
poetry add httpx
uv add httpx

Passo 1 · POST /api/v1/batch

Enviar o lote

Endpoint assíncrono. Manda de 1 a 100.000 emails num POST e recebe 201 com o id do lote. O campo name é opcional e serve pra você achar o lote depois.

py
# emailchecker.py
import os
import time

import httpx

API_KEY = os.environ['EMAILCHECKER_API_KEY']
BASE_URL = 'https://app.emailchecker.email/api/v1'

# um client só: keep-alive durante todo o polling
client = httpx.Client(
    base_url=BASE_URL,
    headers={'Authorization': f'Bearer {API_KEY}'},
    timeout=30.0,
)


def unwrap(r: httpx.Response) -> dict:
    """Devolve data[] ou levanta com a mensagem real da API."""
    body = r.json()
    if r.is_error:
        raise RuntimeError(f'EmailChecker {r.status_code}: {body.get("error")}')
    return body['data']


def submit_batch(emails: list[str], name: str | None = None) -> str:
    """POST /api/v1/batch — de 1 a 100.000 emails. Devolve o id do lote."""
    payload: dict = {'emails': emails}
    if name is not None:
        payload['name'] = name
    return unwrap(client.post('/batch', json=payload))['id']


batch_id = submit_batch(['joao@empresa.com.br', 'contato@outra.com.br'], 'minha-lista')
print('lote enviado:', batch_id)

Passo 2 · GET /api/v1/batch/{id}

Consultar o resultado

Não enviamos webhook: o jeito de saber que terminou é consultar o lote até status virar completed. Só aí o array emails vem preenchido, com result, score, reason e os atributos isv_*. O limite é de 300 req/min e 10.000/hora por key — um poll a cada 5 segundos sobra.

py
# poll.py — não existe webhook: consulte o lote até ele fechar
def wait_for_batch(batch_id: str, interval: float = 5.0, timeout: float = 1800.0) -> dict:
    """GET /api/v1/batch/{id} até status == 'completed'."""
    deadline = time.monotonic() + timeout

    while time.monotonic() < deadline:
        r = client.get(f'/batch/{batch_id}')
        if r.status_code == 429:  # 300 req/min e 10.000/hora por key
            time.sleep(60)
            continue

        batch = unwrap(r)
        if batch['status'] == 'completed':
            return batch
        if batch['status'] in ('failed', 'cancelled'):
            raise RuntimeError(f'lote {batch["status"]}')

        time.sleep(interval)  # pending | processing

    raise TimeoutError('lote não terminou dentro do tempo')


batch = wait_for_batch(batch_id)
print(batch['size'], 'emails, finalizado em', batch['finished_at'])

# emails[] só vem preenchido com status == 'completed'
for e in batch['emails']:
    # result: 'deliverable' | 'undeliverable' | 'risky' | 'unknown'
    if e['result'] == 'deliverable' and e['score'] >= 80:
        print(e['email'], e['score'], e['reason'], e['is_free'])

Boas práticas

Pitfalls

Armadilhas específicas de Python

Erros sutis que só aparecem em produção. Conheça antes de subir o código.

Response de erro não levanta sozinha

Tanto requests quanto httpx retornam o objeto Response em qualquer status — um 422 não levanta exceção por conta própria. Sem checar r.is_error (ou chamar r.raise_for_status()) você lê {"data": null, "error": "..."} como se fosse sucesso e estoura num KeyError bem longe da causa.

httpx.post() abre e fecha um client a cada chamada

Usar a função de módulo httpx.post() cria um client novo (e um pool TCP novo) por request — péssimo num loop de polling. Instancie um httpx.Client() no módulo (ou um with httpx.Client() as client:) e reaproveite; o keep-alive corta latência e file descriptors.

Misturar sync e async trava o event loop

Chamar client.get() sync dentro de uma corrotina bloqueia todo o loop durante o polling, anulando o ganho do asyncio. Dentro de async use httpx.AsyncClient com await, e await asyncio.sleep() no lugar de time.sleep(), que congela o FastAPI inteiro.

Timeout default do httpx é 5s — curto pra lote grande

O httpx aplica timeout de 5s por padrão e levanta httpx.ReadTimeout calado no meio de um POST com dezenas de milhares de emails. Passe timeout=30.0 explícito no client e, se o POST estourar, cheque antes se o lote foi criado em vez de reenviar às cegas e duplicar.

Mais linguagens

Exemplos em outras linguagens

Comece agora

Pronto pra parar de mandar email pra endereço morto?

Comece grátis com 500 créditos. Sem cartão, sem compromisso.