Como Verificar Endereços de E-mail em Python
Verificar um endereço de e-mail em Python é simples nas partes fáceis e surpreendentemente difícil na parte que mais importa. Este guia mostra checagens de sintaxe e consultas DNS que você pode rodar localmente, explica exatamente por que sondagens SMTP brutas falham na prática e mostra como obter um resultado no nível da caixa de correio com uma API REST.
Use Python para rejeitar entradas malformadas e, se quiser, guardar em cache as checagens de DNS/MX. Use uma API de verificação para o resultado no nível da caixa de correio, porque sondagens smtplib brutas são fáceis de limitar e muitas vezes retornam respostas ambíguas.
O Que "Verificar" um E-mail Significa de Verdade
Verificação de email é o processo de confirmar que um endereço é real e pode receber e-mails antes de você enviar qualquer coisa para ele. Ela se divide em uma sequência de checagens, cada uma mais difícil que a anterior: o endereço está formatado corretamente, o domínio dele aceita e-mails e a caixa de correio específica existe. Em Python, as duas primeiras são fáceis de fazer bem. A terceira é onde a maioria das soluções caseiras falha sem fazer barulho, e entender por quê é a coisa mais útil deste artigo.
O plano abaixo é em camadas. Rode localmente as checagens baratas e rápidas para descartar entradas obviamente quebradas e depois chame uma API de verificação para a resposta no nível da caixa de correio sobre se o endereço pode ser usado. Assim você rejeita lixo na hora, sem chamada de API, e tem um resultado mais claro para tudo o que passar.
Passo 1: Validação de Sintaxe
A primeira checagem confirma que o endereço está bem formado. É tentador escrever uma expressão regular para isso, mas a sintaxe de e-mail é muito mais intrincada do que parece, e regexes feitas à mão quase sempre são rígidas ou permissivas demais. A biblioteca de referência para esse trabalho é a email-validator, que implementa corretamente os padrões relevantes e é amplamente usada em produção.
Instale com pip:
pip install email-validator
Depois valide um endereço. A biblioteca lança EmailNotValidError para qualquer coisa malformada e retorna um objeto normalizado para entradas válidas:
from email_validator import validate_email, EmailNotValidError
def check_syntax(address):
try:
result = validate_email(address, check_deliverability=False)
return True, result.normalized
except EmailNotValidError as error:
return False, str(error)
ok, value = check_syntax("jane.doe@example.com")
print(ok, value) # True jane.doe@example.com
ok, value = check_syntax("jane.doe@@example")
print(ok, value) # False, with a description of the problem
Definir check_deliverability=False mantém este passo como pura sintaxe, sem chamadas de rede, então ele é rápido o bastante para rodar a cada tecla digitada ou envio de formulário. Sempre armazene o valor normalized que a biblioteca retorna em vez da entrada bruta do usuário, já que ela canoniza o endereço para um armazenamento consistente.
Passo 2: Checagens de Domínio e MX
Um endereço bem formatado continua inútil se o domínio dele não pode receber e-mails. A próxima checagem consulta os registros DNS do domínio e procura registros MX, que indicam os servidores de e-mail responsáveis por aquele domínio. Um domínio sem registros MX não aceita e-mails, então qualquer endereço nele pode ser rejeitado na hora.
Os email-validator pode fazer isso por você quando você passa check_deliverability=True, que faz a consulta MX como parte da validação:
from email_validator import validate_email, EmailNotValidError
def check_domain(address):
try:
result = validate_email(address, check_deliverability=True)
return True, result.domain
except EmailNotValidError as error:
return False, str(error)
Se você quiser rodar a consulta DNS por conta própria, talvez para guardar em cache os resultados de muitos endereços do mesmo domínio, use a biblioteca dnspython diretamente:
pip install dnspython
import dns.exception
import dns.resolver
def has_mx_record(domain):
try:
records = dns.resolver.resolve(domain, "MX")
return len(records) > 0
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
return False
except dns.exception.DNSException:
return False # Treat resolver errors as inconclusive.
print(has_mx_record("gmail.com")) # True
print(has_mx_record("no-such-domain-xyz.invalid")) # False
Isso é realmente útil. Remove endereços em domínios que expiraram, nunca existiram ou simplesmente não lidam com e-mails. Mas repare no que ainda não foi dito: se someone@gmail.com é uma caixa de correio real. O Gmail tem registros MX, então todo endereço do Gmail passa nessa checagem, seja válido ou não.
Passo 3: A Checagem da Caixa de Correio, e Por Que o SMTP Bruto Não Basta
Confirmar a caixa de correio em si exige conversar com o servidor de e-mail de destino. Em princípio, você se conecta via SMTP, se identifica, envia um comando RCPT TO com o endereço-alvo e lê o código de resposta: um código positivo sugere que a caixa existe, uma rejeição sugere que não. O módulo nativo do Python smtplib consegue fazer isso, e o código parece enganosamente simples:
import smtplib
import dns.resolver
def naive_smtp_check(address):
domain = address.split("@")[1]
mx_host = str(dns.resolver.resolve(domain, "MX")[0].exchange)
server = smtplib.SMTP(timeout=10)
server.connect(mx_host)
server.helo("example.com")
server.mail("probe@example.com")
code, message = server.rcpt(address)
server.quit()
# 250 and 251 generally mean the mailbox was accepted.
return code in (250, 251)
Isso funciona em um tutorial e falha em produção. Vale conhecer os motivos em detalhe, porque eles explicam por que nenhum verificador sério depende de um script como este.
Reputação de IP e limitação
Os provedores de caixa de correio detectam ativamente sondagens de verificação. Rode algumas checagens RCPT TO a partir de um único servidor e provedores como Gmail e Outlook vão limitar ou bloquear esse IP em poucas tentativas. Depois de limitado, o servidor retorna respostas ambíguas, exista a caixa ou não, e seus resultados perdem o sentido. A verificação de verdade é feita a partir de grandes pools de endereços IP bem aquecidos e bem gerenciados, exatamente por esse motivo.
Greylisting
Muitos servidores usam greylisting, retornando de propósito uma falha temporária 4xx na primeira vez que um remetente desconhecido faz contato. Servidores de e-mail legítimos tentam de novo mais tarde; spammers geralmente não. Um script ingênuo que não tenta de novo lê essa falha temporária como rejeição e marca uma caixa de correio perfeitamente boa como inválida. É um falso negativo causado apenas pela pressa.
Domínios catch-all
Um domínio catch-all aceita e-mails para todos os endereços possíveis, então o RCPT TO retorna um código positivo tanto para realperson@domain.com e na random-string-9f3a@domain.com sem distinção. Uma checagem SMTP bruta não consegue distinguir uma caixa real de uma inexistente nesses domínios. Detectar um catch-all exige enviar uma segunda sondagem para um endereço que com certeza não existe e ver se ele também é aceito, o que é detecção de catch-all, não um simples sim ou não.
Respostas ambíguas dos grandes provedores
Alguns grandes provedores tornaram ambíguo, de propósito, o comportamento do RCPT TO deles. Alguns aceitam todos os endereços na etapa do handshake e só geram bounce depois, então a resposta SMTP simplesmente não traz a informação que você está pedindo.
É por isso que os mantenedores da email-validator deixam as checagens SMTP fora da biblioteca de propósito. A documentação diz claramente que não há nada a ganhar contatando um servidor SMTP por conta própria, porque os servidores são bons em não revelar se um endereço é entregável, e um endereço que parece aceitar e-mails ainda pode gerar bounce depois. A conclusão honesta: checagens de sintaxe e MX são coisas que você faz bem no seu próprio código, mas uma checagem confiável de caixa de correio não é.
Passo 4: Verificando com a API VeriMails
A solução prática é deixar que um serviço de verificação cuide da checagem da caixa de correio a partir de uma infraestrutura feita para isso, e chamar esse serviço a partir do Python com uma única requisição HTTP. A VeriMails oferece uma API REST que roda o pipeline completo, sintaxe, MX, DNS, um handshake SMTP ao vivo, detecção de catch-all, detecção de descartáveis e detecção de endereços baseados em função, e retorna um único resultado estruturado.
Usando a biblioteca padrão requests , uma chamada de verificação fica assim. Para detalhes do endpoint, veja a Documentação da API do VeriMails:
import requests
def verify_email(address, api_key):
response = requests.get(
"https://api.verimails.com/v1/verify",
params={"email": address},
headers={"Authorization": f"Bearer {api_key}"},
timeout=10,
)
response.raise_for_status()
return response.json()
result = verify_email("jane.doe@example.com", api_key="YOUR_API_KEY")
print(result["status"]) # e.g. "deliverable", "undeliverable", "catch_all"
print(result["disposable"]) # True if a throwaway address
print(result["role_based"]) # True for addresses such as info@ or support@
Depois você age com base no resultado. Um formulário de cadastro pode aceitar endereços deliverable , rejeitar os undeliverable e na disposable e definir a sua própria política para catch_all:
def is_acceptable_for_signup(address, api_key):
result = verify_email(address, api_key)
if result["disposable"]:
return False, "Disposable addresses are not allowed."
if result["status"] == "undeliverable":
return False, "That address does not appear to exist."
return True, "Address verified."
Como cada chamada retorna um resultado claro, ela é rápida o bastante para rodar dentro do envio de um formulário sem deixar o usuário esperando. Para uma lista de endereços que já está na sua base, a verificação de CSV em massa é a opção mais adequada, já que subir um arquivo é muito mais eficiente do que chamar o endpoint de endereço único milhares de vezes em loop.
O padrão recomendado para uma aplicação Python é combinar as duas camadas: mantenha a checagem de sintaxe local com email-validator para rejeitar lixo na hora, antes de gastar um crédito, e depois chame a API para obter o resultado de entregabilidade definitivo de tudo o que passar. Você tem retorno rápido para erros óbvios e um veredicto confiável para endereços reais.
Tabela de políticas da aplicação
| Resultado | Política de cadastro | Política de importação ou CRM |
|---|---|---|
deliverable | Aceite e armazene o endereço normalizado | Mantenha no segmento ativo |
undeliverable | Rejeite com uma mensagem clara de correção | Suprima antes de qualquer campanha |
catch_all | Aceite ou revise de acordo com o risco da conta | Segmente separadamente e envie com cautela |
disposable | Rejeite na maioria dos produtos | Suprima das listas de prospecção |
role_based | Permita apenas quando caixas compartilhadas forem aceitáveis | Revise antes de cold outreach |
Chamadas de API versus jobs de lista
Use a API onde uma decisão imediata importa: cadastro, checkout, formulários de leads, convites e alterações de conta. Use a verificação em massa para uma exportação em CSV, uma limpeza de CRM ou um público de campanha em que milhares de endereços precisam da mesma revisão. Os dois fluxos devem alimentar as mesmas regras de supressão, para que um endereço rejeitado em um caminho não receba e-mails de outra ferramenta depois.
Juntando Tudo
Uma implementação limpa tem três camadas. Primeiro, uma checagem email-validator de sintaxe com check_deliverability=False, rodada localmente e na hora para rejeitar entradas malformadas. Segundo, uma checagem MX opcional com dnspython se você quiser filtrar domínios mortos antes de chamar a API, embora a API também cubra isso. Terceiro, uma chamada à API VeriMails para o resultado no nível da caixa de correio, incluindo detecção de catch-all, descartáveis e baseados em função, que você não consegue reproduzir de forma confiável por conta própria.
O que importa lembrar é a divisão de trabalho. Sintaxe e DNS são de fato seus para tratar em Python. A checagem ao vivo da caixa de correio depende de reputação de IP, comportamento de greylisting e detecção de catch-all que um único script não dá conta, e é por isso que a biblioteca de validação padrão deixa a sondagem SMTP de fora de propósito. Use checagens locais para ter velocidade, uma API para a verificação no nível da caixa de correio e a página de preços para escolher o plano que combina com o volume esperado.
Perguntas Frequentes
Artigos Relacionados
Comece com Dados Limpos
100 créditos grátis no cadastro. Sem cartão de crédito. Coloque o conselho em prática hoje mesmo.
Começar Grátis