Referencia de la API de verificación de correo
POST/v1/email/validate
Verifica una dirección de correo: sintaxis, dominios desechables/temporales conocidos, el servidor de correo (MX) del dominio, erratas probables y direcciones de rol, y devuelve un veredicto accept/review/reject con los motivos. Envía un cuerpo JSON { "email": "user@example.com" }. Está pensado para llamarse desde el propio JavaScript de un formulario de registro, con el mismo modelo de clave publicable restringida por origen que GET /v1/geo. Para muchas direcciones o una decisión de registro en el servidor, usa los endpoints por lotes, de trabajos de lista y de verificación de registro que se describen más abajo. Todas las comprobaciones son a nivel de dominio y de dirección: no se contacta ningún buzón, así que ningún resultado establece que un buzón exista, quién es su titular ni que el correo vaya a llegar.
Autenticación
POST /v1/email/validate acepta una clave publicable restringida por origen mediante la cabecera X-Api-Key: el mismo tipo de clave y el mismo modelo de orígenes permitidos que GET /v1/geo. Todos los demás endpoints de esta página (lotes, trabajos de lista, verificación de registro) aceptan muchas direcciones o una IP arbitraria, por lo que requieren tu clave secreta mediante Authorization: Bearer y solo deben llamarse desde tu backend; una clave publicable recibe un 403 en ellos.
Campos de la respuesta
- La dirección que enviaste, devuelta exactamente como se envió.
- domain
- El dominio en minúsculas (dominios internacionalizados en punycode), o null cuando syntaxValid es false.
- syntaxValid
- Si la dirección es sintácticamente válida. No es una comprobación de existencia del buzón ni de entregabilidad.
- isDisposable
- Si el dominio coincide con un proveedor de correo desechable/temporal conocido: directamente, a través de un dominio padre o a través de un servidor de correo que se sabe que atiende dominios desechables. Siempre false cuando syntaxValid es false.
- normalizedEmail
- Dirección sin espacios y en minúsculas con el dominio en punycode, o null cuando syntaxValid es false.
- canonicalEmail
- normalizedEmail sin ningún +tag (y, para Gmail, sin puntos y con googlemail.com convertido en gmail.com): guárdalo para detectar a la misma persona registrándose dos veces. Null cuando syntaxValid es false.
- disposableSource
- Por qué isDisposable es true: list (el propio dominio está en la lista), parent_domain (un dominio padre está en la lista, p. ej. a.b.mailinator.com) o mx_host (el servidor de correo del dominio atiende dominios desechables conocidos). Null cuando isDisposable es false.
- isFreeProvider
- Si el dominio es un proveedor de correo de consumo como gmail.com u outlook.com. Solo informativo: nunca cambia el veredicto.
- isRoleAccount
- Si la parte local es una dirección de rol como admin@, info@ o noreply@.
- didYouMean
- Una corrección probable de errata de la dirección completa (john@gmial.com → john@gmail.com), o null. Un dominio con su propio servidor de correo operativo no se «corrige» salvo que esté a una edición de un proveedor común.
- mx.status
- found (existen registros MX), implicit (sin MX, pero con un registro A/AAAA), none (ninguno de los dos), null_mx (el dominio declara que no acepta correo), no_domain (el dominio no existe) o unknown (la consulta falló o agotó el tiempo). El objeto mx completo es null cuando syntaxValid es false.
- mx.host
- El host MX de menor preferencia cuando status es found; en caso contrario, null. No indica nada sobre si existe el buzón concreto.
- verdict
- accept, review o reject: la más grave de las condiciones de reasons. Una recomendación para tu propia política, no una decisión final.
- reasons
- Cada condición que se cumplió, de la más grave a la menos grave; vacío cuando verdict es accept. Consulta «Veredictos y códigos de motivo» más abajo.
Errores
- 400
- Cuerpo de solicitud mal formado. Lotes: empty_batch o batch_too_large (más de 100 direcciones). Verificación de registro: invalid_ip. Trabajos de lista: invalid_body, empty_job o job_too_large (más de 100.000 direcciones). Una dirección de correo no válida nunca produce un 400: es un resultado con veredicto reject y motivo invalid_syntax.
- 401
- Clave de API faltante o no válida.
- 403
- POST /v1/email/validate: el Origin/Referer de la solicitud no está en la lista de orígenes permitidos de la clave (no se establece cabecera CORS, así que el navegador bloquea la respuesta sea cual sea el cuerpo JSON). Endpoints con clave secreta: se usó una clave publicable.
- 404
- job_not_found: no existe ningún trabajo de lista con este id en tu cuenta.
- 409
- job_not_completed: el trabajo sigue en cola o en proceso, o ha fallado; los resultados solo se pueden descargar cuando ha terminado.
- 410
- job_expired: las direcciones subidas y los resultados del trabajo se han eliminado (7 días después de su creación).
- 413
- body_too_large: una subida CSV de un trabajo de más de 10 MB.
- 415
- Un cuerpo de trabajo de lista que no es JSON ni text/csv.
- 429
- El límite de tasa por minuto, o monthly_limit_exceeded cuando se superaría el límite mensual de solicitudes del plan; consulta errores y límites de tasa para saber cuál es. Un lote o trabajo de lista que superaría el límite se rechaza entero, antes de hacer ningún trabajo.
- 503
- job_storage_unavailable: no se pudo almacenar un trabajo de lista. No se cobró nada; vuelve a intentarlo.
Ejemplo
Solicitud
curl https://api.watch-ip.com/v1/email/validate \
-H "X-Api-Key: YOUR_PUBLISHABLE_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "John.Doe+news@Gmial.com"}'Respuesta
{
"email": "John.Doe+news@Gmial.com",
"domain": "gmial.com",
"syntaxValid": true,
"isDisposable": false,
"normalizedEmail": "john.doe+news@gmial.com",
"canonicalEmail": "john.doe@gmial.com",
"disposableSource": null,
"isFreeProvider": false,
"isRoleAccount": false,
"didYouMean": "john.doe+news@gmail.com",
"mx": {
"status": "none",
"host": null
},
"verdict": "reject",
"reasons": [
"no_mail_server",
"typo_suspected"
]
}Veredictos y códigos de motivo
Cada condición fija un veredicto mínimo; gana la más grave, y reasons las enumera todas, de la más grave a la menos grave. Si una dirección de un proveedor gratuito es aceptable lo decides tú: nunca cambia el veredicto.
- invalid_syntax
- Reject. La dirección no es sintácticamente válida (límites de longitud, caracteres permitidos, posición de los puntos, un dominio real en lugar de una IP literal). También se rechazan las partes locales entre comillas: son legales, pero en la práctica nunca se usan en un registro.
- domain_not_found
- Reject. El dominio no existe en el DNS.
- no_mail_server
- Reject. El dominio declara que no acepta correo (un «null MX») o no tiene registros MX ni A/AAAA.
- disposable_domain
- Reject. El dominio, un dominio padre o el servidor de correo del dominio coincide con proveedores de correo desechable/temporal conocidos; consulta disposableSource.
- typo_suspected
- Review. didYouMean contiene una corrección probable, como gmial.com → gmail.com.
- role_account
- Review. La parte local es una dirección de rol como admin@, info@, support@ o noreply@, normalmente compartida y no personal.
- implicit_mx
- Review. El dominio no tiene registro MX; el correo recurriría a su dirección A/AAAA, algo poco habitual en dominios de correo reales.
- dns_unavailable
- Review. La consulta DNS falló o agotó el tiempo, así que la configuración de correo del dominio es desconocida (mx.status: unknown). No es una prueba en contra de la dirección.
Verificación por lotes
POST/v1/email/batch
Verifica de 1 a 100 direcciones en una sola solicitud, con las mismas comprobaciones y la misma forma de resultado que POST /v1/email/validate, un resultado por dirección en el orden de entrada. Envía { "emails": [...] }. Las direcciones del mismo dominio comparten una única consulta DNS. Cuenta una solicitud por dirección contra el límite mensual de tu plan; un lote que superaría el límite se rechaza entero con 429 monthly_limit_exceeded. Para el límite de tasa por minuto, la llamada completa cuenta como una sola solicitud. Solo con clave secreta.
Solicitud
curl https://api.watch-ip.com/v1/email/batch \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"emails": ["user@gmail.com", "someone@mailinator.com"]}'Respuesta
{
"results": [
{ "email": "user@gmail.com", "verdict": "accept", "reasons": [], ... },
{ "email": "someone@mailinator.com", "isDisposable": true, "disposableSource": "list", "verdict": "reject", "reasons": ["disposable_domain"], ... }
]
}Trabajos de lista de correo
POST/v1/email/jobs
Verifica hasta 100.000 direcciones en segundo plano. Envía JSON { "emails": [...], "filename": "..." } o un cuerpo text/csv de 10 MB como máximo: se usa la columna titulada email (también e-mail, email address, email_address o mail, sin distinguir mayúsculas), o la primera columna si ninguna cabecera la nombra; se detectan los separadores coma, punto y coma y tabulación. El trabajo se cobra al crearse (una solicitud por dirección distinta, coincidencia exacta, así que los duplicados se verifican pero se cobran una vez) y una lista que superaría tu límite mensual se rechaza entera. Si parte de un trabajo no se puede procesar tras varios reintentos, el trabajo se marca como failed y se devuelven los créditos de la parte no procesada. Responde 202 con el objeto del trabajo. Solo con clave secreta; los mismos trabajos también se pueden subir y descargar desde la página Email lists del panel.
Solicitud
curl https://api.watch-ip.com/v1/email/jobs \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: text/csv" \
--data-binary @newsletter.csvRespuesta
{
"id": "5b0d6f5e-8a51-4f7e-9d0e-2c1f3f7f8a11",
"status": "queued",
"source": "api",
"filename": null,
"total": 12840,
"credits": 12512,
"processed": 0,
"summary": null,
"createdAt": "2026-09-28T10:00:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-05T10:00:00.000Z"
}Estado del trabajo
GET/v1/email/jobs/{id}
Devuelve el mismo objeto del trabajo. Consúltalo periódicamente hasta que status sea completed; las consultas no se cobran.
- status
- queued → processing → completed. failed: parte de la lista no se pudo procesar tras varios reintentos; no hay resultados disponibles y se devolvieron los créditos de la parte no procesada. expired: las direcciones subidas y los resultados se eliminaron 7 días después de la creación.
- total
- Filas enviadas, duplicados incluidos.
- credits
- Solicitudes cobradas: una por dirección distinta.
- processed
- Filas verificadas hasta ahora.
- summary
- Una vez completado: número de filas por veredicto (accept/review/reject) y por cada código de motivo que apareció. Null hasta entonces.
- expiresAt
- Cuándo se eliminan las direcciones subidas y los resultados.
Resultados del trabajo
GET/v1/email/jobs/{id}/results
Descarga un CSV con una fila por cada fila enviada, en el orden de envío, duplicados incluidos. Columnas: email, normalized_email, verdict, reasons, syntax_valid, is_disposable, disposable_source, is_free_provider, is_role_account, did_you_mean, mx_status, mx_host. reasons va separado por punto y coma, las celdas vacías significan null y mx_status está vacío para las direcciones sintácticamente no válidas (no se consultó nada). Una dirección no válida que empieza por =, +, - o @ lleva delante un apóstrofo para que las hojas de cálculo no la ejecuten como fórmula. 409 hasta que el trabajo termine, 410 cuando haya caducado. La descarga no se cobra.
Solicitud
curl https://api.watch-ip.com/v1/email/jobs/5b0d6f5e-8a51-4f7e-9d0e-2c1f3f7f8a11/results \
-H "Authorization: Bearer YOUR_SECRET_KEY" -o results.csvVerificación de registro
POST/v1/signup/check
Puntúa un registro a partir de su dirección de correo y su IP en una sola llamada, desde tu backend cuando se envía el formulario de registro (un veredicto calculado en el navegador se puede falsificar). Envía { "email": "...", "ip": "...", "userAgent": "..." }; userAgent es opcional. La respuesta contiene el resultado completo de la verificación de correo (email), el país y el ASN de la IP y, en los planes que incluyen datos de seguridad, sus indicadores de VPN/Tor/centro de datos/amenaza (ip), la antigüedad del dominio del correo en días según los datos públicos de registro (RDAP) (emailDomainAgeDays, null cuando se desconoce y para los grandes proveedores de consumo), una puntuación de 0 a 100 (la suma de los pesos de los motivos siguientes, con un máximo de 100) y un veredicto: reject a partir de 60, review a partir de 25 y, en otro caso, accept. En un plan sin datos de seguridad, ip.security es null, la puntuación usa solo las señales del correo y reasons incluye ip_security_unavailable. Cuenta como una solicitud. Solo con clave secreta.
- invalid_syntax
- 70
- domain_not_found · no_mail_server · disposable_domain
- 60 cada uno
- ip_tor · ip_threat
- 40 cada uno
- ip_vpn
- 25
- new_domain
- 25: el dominio del correo se registró hace menos de 30 días
- ip_datacenter
- 20: solo cuando la IP no es ya una salida de VPN o Tor
- automated_user_agent
- 20: el userAgent proporcionado parece un bot o un script
- typo_suspected
- 15
- role_account · implicit_mx
- 10 cada uno
- dns_unavailable
- 5
- ip_security_unavailable
- 0: informativo, este plan no incluye datos de seguridad
Solicitud
curl https://api.watch-ip.com/v1/signup/check \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@new-startup.io", "ip": "203.0.113.1"}'Respuesta
{
"email": { "email": "jane@new-startup.io", "verdict": "accept", "reasons": [], ... },
"ip": {
"ip": "203.0.113.1",
"country": "US",
"asn": 64500,
"asOrganization": "Example Hosting",
"security": { "isTor": false, "isVpn": false, "isDatacenter": true, "isThreat": false, "isSanctionedNetwork": false }
},
"emailDomainAgeDays": 12,
"score": 45,
"verdict": "review",
"reasons": ["new_domain", "ip_datacenter"]
}Límites
Cada dirección verificada cuenta como una solicitud contra el límite mensual compartido de tu plan (una verificación individual, cada dirección de un lote, cada dirección distinta de un trabajo de lista) y una verificación de registro es una solicitud. Los lotes y los trabajos de lista se admiten o se rechazan enteros, nunca se atienden parcialmente. El límite de tasa por minuto cuenta llamadas HTTP, no elementos: un lote de hasta 100 IP o direcciones de correo cuenta como una sola solicitud, mientras que cada elemento sigue contando por separado en el límite mensual de solicitudes. Las subidas y los resultados de los trabajos de lista se eliminan 7 días después de crear el trabajo. Las consultas DNS tienen un margen de tiempo corto: cuando una falla o agota el tiempo, el resultado se degrada a mx.status unknown y motivo dns_unavailable en lugar de un error. Los datos de dominios desechables se combinan a partir de tres listas de código abierto mantenidas por la comunidad, se actualizan cada pocas horas y se amplían con los servidores de correo observados atendiendo dominios de las listas; consulta fuentes de datos. Es posible que un proveedor desechable muy reciente aún no se marque. Todas las comprobaciones son a nivel de dominio y de dirección: no se contacta ningún buzón, así que ningún resultado establece que un buzón exista, quién es su titular ni que el correo vaya a llegar.
Páginas relacionadas
- AutenticaciónClaves publicables vs. secretas, restricción por origen y límites de tasa.
- Referencia de GET /v1/geoReferencia completa de campos para el endpoint de geolocalización de visitantes.
- Detección de correos desechablesMarca correos de registro desechables, mal escritos o sin servidor de correo, de uno en uno o de forma masiva.
- Prevención de abuso en registrosCombina señales del correo y de la IP en una decisión de registro: aceptar, revisar o rechazar.
- Fuentes de datosQué fuente respalda cada campo de la respuesta y sus limitaciones conocidas.