API v1

Documentation de l'API

Une requête par adresse, ou des lots jusqu'à un million. Même moteur et mêmes crédits que le tableau de bord. Les clés viennent de votre compte.

Authentification

Créez une clé dans Tableau de bord → Clés API et envoyez-la dans l'en-tête X-API-Key (ou sous la forme Authorization: Bearer mk_live_…). Les clés commencent par mk_live_, ne sont affichées qu'une fois et peuvent être révoquées à tout moment. URL de base : https://api.mailok.io.

Vérifier une adresse

POST /v1/verify avec { "email" }, ou GET /v1/verify?email=. Réponse en quelques secondes ; une vraie conversation SMTP peut prendre jusqu'à 60 s sur les serveurs lents.

curl https://api.mailok.io/v1/verify \
  -H "X-API-Key: mk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]"}'
Réponse
{
  "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
}

Statuts

ValideLe serveur a confirmé la boîte. Envoi sûr.1 crédit
InvalideSyntaxe incorrecte, pas d'enregistrement MX, ou le serveur a refusé la boîte.1 crédit
RisquéAcceptée mais non confirmable : domaine catch-all, boîte pleine ou fournisseur jetable. Regardez les indicateurs.1 crédit
InconnuLe serveur n'a pas répondu à temps (greylisting, délais dépassés).0 crédit

Indicateurs sur chaque résultat : catch_all, disposable, role, mailbox_full, mx_found, mx_record. credits_remaining est votre solde après l'appel.

Lots

Envoyez une liste, interrogez jusqu'à ready, parcourez les résultats page par page. Les doublons au sein d'une liste sont marqués et gratuits. Les listes apparaissent aussi dans votre tableau de bord.

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 files

Statuts : pending (lecture) → parsed (seulement avec auto_start: false, en attente de start) → processing → completed → ready ; sampled après un échantillon ; paused quand le compte n'a plus de crédits (reprend seul dès que des crédits arrivent) ; cancelled ; failed avec un message error. Les adresses inconnues sont revérifiées une fois gratuitement avant ready. GET /v1/batches liste vos lots.

Compte

GET /v1/account renvoie credits, subscription_credits, credits_remaining, plan et votre rate_limit_per_minute. Pratique pour des alertes avant une campagne.

Webhooks

Passez une callback_url (hôte public https ou http) à la création d'un lot et nous envoyons en POST un événement batch.ready quand il est terminé. Répondez par un 2xx sous 10 s ; sinon nous réessayons après 1, 5 et 30 minutes, puis abandonnons. Chaque tentative est listée dans Tableau de bord → Clés API, où se trouve aussi le secret de signature.

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 } }

Vérifiez la signature avant de faire confiance au contenu : HMAC-SHA256 de "{timestamp}.{raw body}" avec votre secret, comparé en temps constant. Rejetez les horodatages vieux de plus de quelques minutes pour bloquer les rejeux.

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'] ?? ''))
}

Erreurs et limites

Chaque erreur a la même forme et un code stable :

{ "error": { "code": "insufficient_credits", "message": "No credits left. Buy credits or choose a plan in the dashboard." } }
400invalid_email, empty_list, list_too_large, invalid_callback_url, invalid_pageCorrigez la requête.
401unauthorizedClé absente, révoquée ou inconnue.
402insufficient_creditsPlus de crédits. Rien n'a été vérifié.
404not_foundLe lot n'existe pas ou appartient à un autre compte.
429rate_limitedLimite par minute dépassée ; attendez Retry-After.
503verifier_unavailableMoteur de vérification injoignable ; réessayez dans une minute. Rien n'a été facturé.

Limite : 60 requêtes par minute et par clé par défaut (en-tête X-RateLimit-Limit sur chaque réponse) ; les abonnements peuvent avoir une limite plus élevée. Besoin de plus ? Écrivez-nous.