Documentación de la API
Una petición por dirección, o lotes de hasta un millón. Mismo motor y mismos créditos que el panel. Las claves salen de tu cuenta.
Autenticación
Crea una clave en Panel → Claves API y envíala en la cabecera X-API-Key (o como Authorization: Bearer mk_live_…). Las claves empiezan por mk_live_, se muestran una sola vez y se pueden revocar en cualquier momento. URL base: https://api.mailok.io.
Verificar una dirección
POST /v1/verify con { "email" }, o GET /v1/verify?email=. Responde en unos segundos; una conversación SMTP real puede tardar hasta 60 s en servidores lentos.
curl https://api.mailok.io/v1/verify \
-H "X-API-Key: mk_live_…" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'{
"email": "[email protected]",
"status": "valid",
"catch_all": false,
"disposable": false,
"role": false,
"mailbox_full": false,
"mx_found": true,
"mx_record": "mx1.northwindtraders.com",
"domain": "northwindtraders.com",
"account": "sarah",
"credits_remaining": 9999
}Estados
Indicadores en cada resultado: catch_all, disposable, role, mailbox_full, mx_found, mx_record. credits_remaining es tu saldo tras la llamada.
Lotes
Envía una lista, consulta hasta ready y recorre los resultados por páginas. Los duplicados dentro de una lista se marcan y son gratis. Las listas también aparecen en tu panel.
POST /v1/batches
{ "emails": ["[email protected]", "[email protected]"], "name": "October import" }
→ { "uid": "k3d9x1qzpa", "name": "October import", "status": "pending", "total": 2, "completed": 0,
"stats": { "valid": 0, "invalid": 0, "risky": 0, "unknown": 0, "duplicate": 0 }, "created_at": "…" }GET /v1/batches/k3d9x1qzpa → same shape plus "percent", "eta_seconds", "credits_used"; "status": "ready" when done
GET /v1/batches/k3d9x1qzpa/results?page_size=1000&status=valid,risky
→ { "items": [ { "id": 4102, "email": "[email protected]", "status": "valid", "checked_at": "…" } ], "total": 1, "next_after": 4102, … }
GET /v1/batches/k3d9x1qzpa/results?page_size=1000&after=4102 → the next page, in file order
GET /v1/batches/k3d9x1qzpa/download?format=xlsx&statuses=valid → the file (your columns + MailStatus, or email,MailStatus)POST /v1/batches/upload multipart: file (CSV or .xlsx), name, callback_url, auto_start (default true)
PUT /v1/batches/k3d9x1qzpa/start { "sample": 100 } optional: verify the first 100 first, then start again for the rest
PUT /v1/batches/k3d9x1qzpa/cancel stops it; verified addresses stay, nothing else is charged
DELETE /v1/batches/k3d9x1qzpa removes the batch, its results and its filesEstados: pending (leyendo) → parsed (solo con auto_start: false, esperando start) → processing → completed → ready; sampled tras una muestra; paused cuando la cuenta se queda sin créditos (continúa sola cuando llegan créditos); cancelled; failed con un mensaje error. Las direcciones desconocidas se vuelven a comprobar una vez gratis antes de ready. GET /v1/batches lista tus lotes.
Cuenta
GET /v1/account devuelve credits, subscription_credits, credits_remaining, plan y tu rate_limit_per_minute. Útil para alertas antes de una campaña.
Webhooks
Pasa una callback_url (host público https o http) al crear un lote y enviamos por POST un evento batch.ready cuando termina. Responde con cualquier 2xx en 10 s; si no, reintentamos tras 1, 5 y 30 minutos y luego desistimos. Cada intento se lista en Panel → Claves API, donde también vive el secreto de firma.
POST <your callback_url>
X-MailOK-Event: batch.ready
X-MailOK-Timestamp: 1760000000
X-MailOK-Signature: sha256=…
X-MailOK-Delivery: 42
{ "event": "batch.ready", "created_at": "…",
"data": { "uid": "k3d9x1qzpa", "name": "October import", "status": "ready", "total": 2, "completed": 2,
"stats": { "valid": 1, "invalid": 1, "risky": 0, "unknown": 0, "duplicate": 0 },
"created_at": "…", "completed_at": "…", "error": null } }Verifica la firma antes de confiar en el contenido: HMAC-SHA256 de "{timestamp}.{raw body}" con tu secreto, comparado en tiempo constante. Rechaza marcas de tiempo de más de unos minutos para bloquear repeticiones.
import crypto from 'node:crypto'
// signature = "sha256=" + HMAC_SHA256(secret, timestamp + "." + rawBody)
export function verify(rawBody, headers, secret) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret)
.update(headers['x-mailok-timestamp'] + '.' + rawBody).digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(headers['x-mailok-signature'] ?? ''))
}Errores y límites
Cada error tiene la misma forma y un code estable:
{ "error": { "code": "insufficient_credits", "message": "No credits left. Buy credits or choose a plan in the dashboard." } }Límite: 60 peticiones por minuto y clave por defecto (cabecera X-RateLimit-Limit en cada respuesta); los planes mensuales pueden tener un límite mayor. ¿Necesitas más? Escríbenos.
