Ao integrar uma API de verificação de e-mail à sua aplicação, a integração em si costuma ser simples: faça uma requisição POST e receba uma resposta JSON. Mas o que acontece quando você precisa verificar 100.000 e-mails? Ou quando a API retorna um erro de limite de taxa? Ou quando você quer verificação em tempo real sem bloquear o fluxo de cadastro?
Este guia aborda as três coisas que toda integração de verificação de e-mail em produção precisa: limitação de taxa inteligente, processamento assíncrono orientado por webhooks e tratamento de erros robusto. Inclui exemplos de código funcionais em Python, JavaScript e PHP.
Padrões de Arquitetura para APIs de Verificação de E-mail
Padrão 1: Verificação Síncrona (em Tempo Real)
Use quando: Verificar um único e-mail em tempo real — formulários de cadastro, fluxos de checkout, captura de leads.
Client → Your Server → Verification API → Response → Your Server → Client
Latência: 200–800 ms (uma ida e volta SMTP). Rápido o suficiente para a maioria das experiências de uso, mas perceptível se chamado em linha.
Padrão 2: Verificação Assíncrona (em Lote)
Use quando: Verificar listas com 1.000+ e-mails — importações em massa, limpeza de listas, tarefas agendadas.
Client → Your Server → Queue Job → Worker → Verification API → Store Results
↓
Client polls or receives webhook when complete
Padrão 3: Verificação Orientada por Webhooks
Use quando: Você quer verificação em tempo real sem manter a conexão do cliente aberta.
Client → Your Server → Submit Job (returns job_id)
↓
Your Server → Verification API → POST webhook → Update database
↓
Client polls or receives notification when job completes
Limitação de Taxa: Como Verificar Milhões de E-mails Sem Ser Bloqueado
Entendendo os Limites de Taxa
| Tipo de Limite | Quando Se Aplica | Aplicação |
|---|---|---|
| Requisições por segundo (RPS) | 50 RPS | Token bucket — rajada até o limite, depois espera |
| Requisições por minuto (RPM) | 500 RPM | Janela deslizante |
| Requisições por dia | 50.000/dia | Janela fixa, reinicia à meia-noite UTC |
| Conexões simultâneas | 10 simultâneas | Limite de pool de conexões |
Estratégia 1: Limitador de Taxa Token Bucket (Python)
import time
import threading
class TokenBucket:
"""Limitador de taxa que permite rajadas até `capacity`, reabastece à razão de `rate` por segundo."""
def __init__(self, taxa de: float, capacity: int):
self.taxa de = taxa de
self.capacity = capacity
self.tokens = capacity
self.last_refill = time.monotonic()
self.lock = threading.Bloquear()
def Obter(self) -> bool:
with self.lock:
agora = time.monotonic()
decorrido = agora - self.last_refill
self.tokens = min(self.capacity, self.tokens + decorrido * self.taxa de)
self.last_refill = agora
if self.tokens >= 1:
self.tokens -= 1
return True
return False
def wait_and_acquire(self) -> None:
while not self.Obter():
time.sleep(0.05)
# Uso: 50 requisições por segundo, com rajadas de até 100
rate_limiter = TokenBucket(taxa de=50, capacity=100)
def verify_email(email: str) -> dict:
rate_limiter.wait_and_acquire()
response = requests.post(
"https://api.email-validator.com/v1/verify",
json={"email": email},
headers={"Authorization": f"Bearer {API_KEY}"}
)
return response.json()
Estratégia 2: Fila + Pool de Workers (Processamento em Lote)
from concurrent.futures import ThreadPoolExecutor, as_completed
from queue import Queue
def bulk_verify(e-mails: list[str], max_workers: int = 5, max_rps: int = 50) -> dict:
limiter = TokenBucket(taxa de=max_rps, capacity=max_rps * 2)
results = {}
queue = Queue()
for email in e-mails:
queue.put(email)
def worker():
while not queue.empty():
try:
email = queue.get_nowait()
except:
break
limiter.wait_and_acquire()
try:
result = verify_email(email)
results[email] = result
except Exceção as e:
results[email] = {"error": str(e)}
finally:
queue.task_done()
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(worker) for _ in range(max_workers)]
for future in as_completed(futures):
future.result()
return results
Estratégia 3: Backoff Exponencial (JavaScript/Node.js)
async function verifyEmailWithRetry(email, maxRetries = 5) {
let attempt = 0;
while (attempt < maxRetries) {
const response = await fetch('https://api.email-validator.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
if (response.status === 429) {
attempt++;
const retryAfter = response.headers.get('Retry-After');
const delay = retryAfter
? parseInt(retryAfter) * 1000
: Math.min(1000 * Math.pow(2, attempt), 60000);
console.warn(`Limite de taxa atingido. Repetindo em ${delay}ms (${attempt}/${maxRetries})`);
await sleep(delay);
continue;
}
if (!response.ok) {
throw new Error(`Erro de API: ${response.status}`);
}
return await response.json();
}
throw new Error(`Falha após ${maxRetries} tentativas`);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Webhooks: Verificação Assíncrona sem Polling
Implementando um Receptor de Webhook (PHP)
<?php
// webhook_receiver.php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_EMAILVERIFY_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $payload, getenv('WEBHOOK_SECRET'));
if (!hash_equals($expected, $signature)) {
http_response_code(401);
die('Assinatura inválida');
}
$data = json_decode($payload, true);
if (!isset($data['job_id'], $data['status'], $data['results'])) {
http_response_code(400);
die('Payload inválido');
}
$db = new PDO('mysql:host=localhost;dbname=email_verifier', 'user', 'pass');
foreach ($data['results'] as $result) {
$stmt = $db->prepare('
UPDATE email_queue
SET status = :status, result = :result, verified_at = NOW()
WHERE email = :email AND job_id = :job_id
');
$stmt->execute([
'status' => $result['status'],
'result' => json_encode($result),
'email' => $result['email'],
'job_id' => $data['job_id'],
]);
}
http_response_code(200);
echo json_encode(['received' => true]);
Segurança de Webhook
| Prática | Porquê |
|---|---|
| Verificação de assinatura HMAC | Comprova que o pedido veio da API |
| Lista de IPs permitidos | Aceitar apenas de intervalos de IP conhecidos |
| Chaves de idempotência | Evita processar webhooks duplicados |
| Tratamento de novas tentativas | Os webhooks podem chegar várias vezes |
| Confirmação atempada | Responda 200 rapidamente e processe de forma assíncrona |
Tratamento de Erros
Códigos de estado HTTP e a sua resposta
| Status | Error | O que fazer |
|---|---|---|
| 400 | Pedido incorreto | Valide os dados no cliente |
| 401 | Não autorizado | Verifique a chave de API e não tente novamente |
| 402 | Pagamento necessário | Atualize o plano ou aguarde a reposição da quota |
| 429 | Demasiados pedidos | Recuo exponencial |
| 422 | Não processável | Resultado válido — email não verificável, não é uma falha |
| 500 | Erro Interno do Servidor | Nova tentativa com backoff, monitorização de alertas |
| 503 | Serviço Indisponível | Tentar novamente após Retry-After header value |
Manipulador de Erros Robusto (Python)
import time
import requests
class VerificationAPIError(Exceção):
pass
def verify_email_safe(email: str, max_retries: int = 3) -> dict:
retryable = {429, 500, 502, 503}
for attempt in range(1, max_retries + 1):
try:
response = requests.post(
"https://api.email-validator.com/v1/verify",
json={"email": email},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10
)
if response.status_code == 200:
return response.json()
if response.status_code == 401:
raise VerificationAPIError("Chave de API inválida")
if response.status_code == 402:
raise VerificationAPIError("Quota excedida")
if response.status_code == 422:
return response.json() # resultado válido, não é um erro
if response.status_code in retryable:
if attempt == max_retries:
raise VerificationAPIError(f"Indisponível após {max_retries} tentativas")
time.sleep(min(2 ** attempt, 30))
continue
raise VerificationAPIError(f"Estado inesperado: {response.status_code}")
except requests.exceções.Timeout:
if attempt == max_retries:
raise VerificationAPIError("Pedido expirou repetidamente")
time.sleep(min(2 ** attempt, 30))
return None
Lista de Verificação para Produção
□ Taxa limitação implementado (token bucket ou queue-baseado)
□ Exponencial backoff em 429/5xx respostas
□ Webhook assinatura verificação (HMAC-SHA256)
□ Webhook idempotência (job_id + event_id deduplicação)
□ Todas API chamadas têm tempos limite (5–10 segundos máx.)
□ API chave armazenado in ambiente variáveis apenas
□ Error registro with nova tentativa contagens
□ Monitoramento/alertas for sustentado error taxa de aumento
□ Degradação controlada if API é fora do ar (nãobloqueie novos cadastros)
Pontos-chave
- Síncrono para formulários de cadastro; assíncrono + webhooks para listas em massa
- Token bucket lida com picos de forma elegante — implemente antes de precisar
- Recuo exponencial em 429/5xx — 1s → 2s → 4s → 8s → máx. 60s
- Verifique as assinaturas de webhook com HMAC — nunca confie em POSTs recebidos
- 422 é um resultado válido — e-mails não verificáveis não são falhas da API
- Nunca bloqueie cadastros de usuários se a API estiver fora do ar — coloque na fila e tente novamente
- Defina timeouts em todas as chamadas — os timeouts padrão do TCP podem chegar a mais de 60 segundos