Has decidido añadir la verificación de correo electrónico a tu aplicación. Buena decisión: es el cambio con mayor impacto que puedes hacer para mejorar la calidad de los registros, reducir los rebotes y prevenir el fraude.
Esta guía te ofrece código listo para producción para integrar la verificación de correo electrónico en cualquier stack. Python, JavaScript, PHP, cURL: elige tu lenguaje y copia el código.
Por qué importa la verificación del lado del servidor
Antes de escribir código, dejemos algo claro: la validación de correo electrónico en el lado del cliente no es suficiente.
// Esto NO es verificación: solo comprueba la sintaxis
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
// "thisdoesnotexist8472@gmail.com" → true (la sintaxis es correcta, pero el buzón no existe)
Las expresiones regulares del cliente detectan errores de formato. No te dicen si el buzón realmente existe. Para eso necesitas verificación a nivel SMTP, lo que requiere una llamada a la API desde el servidor.
Cómo obtener tu clave de API
Primero, regístrate en email-validaton.com y copia tu clave de API desde el panel de control. Las cuentas gratuitas incluyen 100 verificaciones al mes.
Mantén tu clave de API en secreto: nunca debe aparecer en JavaScript del lado del cliente. Llama siempre a la API de verificación desde tu backend.
Verificación de un solo correo electrónico
Python
import requests
from typing import Opcional
def verify_email(email: str, api_key: str) -> dict:
"""Verifica una sola dirección de correo electrónico."""
resp = requests.get(
"https://email-validaton.com/api/v1/verify",
params={"email": email},
headers={"Authorization": f"Bearer {api_key}"},
timeout=15,
)
resp.raise_for_status()
return resp.json()
# Usage
result = verify_email("user@example.com", "YOUR_API_KEY")
print(f"Estado: {result['status']}")
print(f"Puntuación: {result['score']}/10")
print(f"Confianza: {result.get('confidence', 'N/D')}")
if result["status"] == "valid":
# Proceder con la creación de la cuenta
create_user(email)
elif result["status"] a ("invalid", "disposable"):
# Rechazar o marcar
raise ValueError(f"Correo electrónico no válido: {result['reason']}")
JavaScript (Node.js)
async function verifyEmail(email, apiKey) {
const url = new URL("https://email-validaton.com/api/v1/verify");
url.searchParams.set("email", email);
const resp = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!resp.ok) throw new Error(`Error de API: ${resp.status}`);
return resp.json();
}
// Uso
const result = await verifyEmail("user@example.com", process.env.EV_API_KEY);
console.log(`Estado: ${result.status}, Puntuación: ${result.score}/10`);
if (result.status === "valid") {
await db.users.insert({ email, verified: true });
} else {
throw new Error(result.reason || "La verificación del correo electrónico falló");
}
PHP
function verify_email(string $email, string $api_key): array {
$url = "https://email-validaton.com/api/v1/verify?email=" . urlencode($email);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $api_key"],
CURLOPT_TIMEOUT => 15,
]);
$body = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200) {
throw new RuntimeException("API returned $http_code");
}
return json_decode($body, true);
}
// Uso
$result = verify_email("user@example.com", getenv("EV_API_KEY"));
echo "Estado: {$result['status']}, Puntuación: {$result['score']}/10\n";
cURL (CLI / Scripts de shell)
#!/bin/bash
API_KEY="your_api_key"
EMAIL="user@example.com"
curl -s -H "Authorization: Bearer $API_KEY" \
"https://email-validaton.com/api/v1/verify?email=$EMAIL" | python3 -m json.tool
Comprender la respuesta
Cada verificación devuelve una estructura JSON coherente:
{
"email": "user@gmail.com",
"status": "valid",
"score": 9,
"confidence": "high",
"reason": "El buzón existe y acepta correo",
"mx_server": "gmail-smtp-in.l.google.com",
"duration_ms": 287,
"disposable": false,
"role_account": false,
"free_provider": true
}
Explicación de los campos clave
| Campo | Valores | Significado |
|---|---|---|
status |
valid, invalid, catch_all, unknown, risky, disposable, over_quota, error |
El resultado de la verificación |
score |
0–10 | Puntuación de calidad compuesta. 8+ = excelente, 5-7 = moderada, <5 = arriesgada |
confidence |
high, medium, low |
Lo fiable que es el resultado |
reason |
Cadena de texto legible para personas | Motivo por el que se asignó el estado |
disposable |
booleano | Dirección de correo electrónico temporal |
role_account |
booleano | info@, support@, admin@, etc. |
free_provider |
booleano | Gmail, Yahoo, Outlook, etc. |
Patrones de integración
Patrón 1: validación del formulario de registro (el más común)
@app.route("/register", methods=["POST"])
def register():
email = request.form["email"]
password = request.form["password"]
# 1. Verificación de sintaxis (rápida, local)
if "@" No a email o "." No a email.split("@")[-1]:
return jsonify({"error": "Formato de correo electrónico no válido"}), 400
# 2. Verificación SMTP (llamada a la API)
try:
result = verify_email(email, current_app.config["EV_API_KEY"])
except requests.RequestException:
# La API no está disponible: no bloquear los registros, solo marcar para revisión
create_user(email, password, flags=["unverified"])
return redirect(url_for("dashboard"))
# 3. Lógica de negocio según el resultado
if result["status"] == "valid":
create_user(email, password, verified=True)
return redirect(url_for("welcome"))
if result["status"] == "disposable":
return jsonify({
"error": "Por favor, utiliza una dirección de correo electrónico permanente"
}), 400
if result["status"] == "invalid":
return jsonify({
"error": "Esta dirección de correo electrónico parece no existir"
}), 400
# Catch-all, desconocido, arriesgado: permitir pero marcar
create_user(email, password, flags=[result["status"]])
return redirect(url_for("welcome"))
Patrón 2: Verificación asíncrona (no bloqueante)
Para producción a gran escala, no quieres que los usuarios esperen la llamada a la API:
import threading
def verify_async(email: str, api_key: str, user_id: int):
"""Verifica el correo electrónico en segundo plano y actualiza el registro del usuario."""
try:
result = verify_email(email, api_key)
db.users.update(user_id, {
"email_status": result["status"],
"email_score": result["score"],
"verified_at": datetime.utcnow(),
})
except Excepción as e:
logger.error(f"La verificación en segundo plano falló para {user_id}: {e}")
# En tu controlador de registro:
usuario = create_user(email, password)
threading.Thread(
objetivo=verify_async,
args=(email, api_key, usuario.id),
daemon=True,
).start()
Patrón 3: Procesamiento de listas masivas
Para limpiar listas grandes mediante el endpoint de carga masiva:
import requests
import time
def bulk_verify(file_path: str, api_key: str) -> str:
"""Sube un archivo para la verificación masiva. Devuelve el ID del trabajo."""
with open(file_path, "rb") as f:
resp = requests.post(
"https://email-validaton.com/api/v1/bulk",
files={"file": f},
headers={"Authorization": f"Bearer {api_key}"},
)
return resp.json()["job_id"]
def wait_for_results(job_id: str, api_key: str, poll_sec: int = 5):
"""Consulta periódicamente hasta que el trabajo masivo finalice y luego descarga los resultados."""
while True:
resp = requests.get(
f"https://email-validaton.com/api/v1/bulk/status/{job_id}",
headers={"Authorization": f"Bearer {api_key}"},
)
data = resp.json()
if data["status"] a ("completed", "failed", "cancelled"):
return data
time.sleep(poll_sec)
# Usage
job_id = bulk_verify("my_list.csv", API_KEY)
result = wait_for_results(job_id, API_KEY)
# Descargar el archivo de resultados
download = requests.get(result["result_url"])
Gestión de errores y buenas prácticas
Gestión de tiempos de espera
La API de verificación suele responder en 200-800 ms. Configura un tiempo de espera de 10-15 segundos para gestionar con elegancia los problemas de red.
Limitación de velocidad
Las cuentas gratuitas están limitadas a 100/día. Los planes de pago van de 5.000 a 500.000/mes. La API devuelve 429 Too Many Requests when you exceed your limit.
Lógica de reintentos
En caso de fallos temporales (errores de red, 503), vuelve a intentarlo hasta 3 veces con retroceso exponencial:
import time
from requests.exceptions import RequestException
def verify_with_retry(email, api_key, max_retries=3):
for attempt a range(max_retries):
try:
return verify_email(email, api_key)
except RequestException:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
No bloquees los registros si falla la API
Si la API de verificación no está disponible temporalmente, no bloquees el registro de usuarios. En su lugar:
1. Permite el registro y marca la cuenta como "verificación pendiente"
2. Ejecuta la verificación de forma asíncrona cuando la API se recupere
3. Notifica al usuario si su correo electrónico resulta no válido posteriormente
Bloquear los registros durante una caída de la API hace perder usuarios reales.
Conclusión
La verificación de correos electrónicos no es opcional en aplicaciones de producción. Los correos no válidos provocan rebotes, los correos desechables permiten abusos y las listas sin verificar dañan la reputación de tu remitente.
Los patrones de integración de esta guía cubren los casos de uso más habituales. Copia el código, adáptalo a tu stack y empieza a verificar cada correo electrónico que entre en tu sistema.
¿Listo para integrarlo? Obtén tu clave API gratuita: incluye 100 verificaciones.