Ship email verification in minutes — 2,000 free credits, no card.
Developers

Build email verification into anything.

ZeMail is an API-first product. Verify a single address in real time or push millions in bulk, in any language, over plain REST. This page has everything: auth, endpoints, SDKs, webhooks, and a live sandbox you can hit right now.

Quickstart Browse SDKs OpenAPI spec

Try it live — no key needed

This calls the public sandbox endpoint GET /v1/try with deterministic fixtures (no billing, no real SMTP). Try local-parts like valid.jane@…, invalid.bob@…, catchall.x@…, or a real typo like someone@gmial.com.

GET /v1/try?email=…SANDBOX
Point at your own server by setting the base URL; production is https://verify.datomatic.ai/v1.

Quickstart

Three steps: grab a key, verify an address, read the bucket.

  1. Create a key in the dashboard — dvk_live_… (real) or dvk_test_… (sandbox).
  2. Call /v1/verify with a Bearer token.
  3. Send to deliverable, decide on risky via score, drop undeliverable.

Authentication

Every request needs a Bearer API key in the Authorization header. Keys are server-side only — never ship a live key in client-side code.

Authorization: Bearer dvk_live_xxxxxxxxxxxxxxxx
PrefixModeBehavior
dvk_live_LiveReal DNS/SMTP verification. Consumes 1 credit per determinate result; unknown is free.
dvk_test_SandboxDeterministic fixtures, no network, never charged. Perfect for CI + load tests.

Sandbox & test keys

A dvk_test_ key returns deterministic results driven by the local-part, so your integration tests never depend on real mailboxes or spend credits — and bulk batches run with no SMTP so you can load-test at any scale.

Local-part containsReturns
invalid / bounceundeliverable · mailbox_not_found
catchallrisky · catch-all
unknownunknown (free)
anything elsedeliverable · valid

Verify one email

GET/v1/verify?email=…

Real-time, sub-second on popular domains. Runs on a high-priority lane so single verifies never wait behind bulk lists. Billed 1 credit per determinate result (valid / invalid / catch-all / risky); unknown is always free.

Output & buckets

Every verification returns a rich object. The one field to build on is bucket — the action-ready roll-up of the granular status.

{
  "email": "john@company.com",
  "status": "valid",          // valid|invalid|catch-all|unknown|spamtrap|abuse|do_not_mail
  "sub_status": null,
  "score": 96,               // 0-100 confidence (null when unknown)
  "bucket": "deliverable",   // deliverable|risky|undeliverable|unknown
  "domain": "company.com",
  "free_email": false, "role_based": false, "disposable": false,
  "catch_all": false, "mx_found": true,
  "mx_record": "aspmx.l.google.com", "smtp_provider": "google",
  "domain_age_days": 6840, "did_you_mean": null,
  "enhanced": false, "processing_ms": 740
}
BucketMeaningRecommended action
deliverableMailbox confirmedSend
riskyCatch-all / role / low corroborationDecide via score (e.g. ≥ 85)
undeliverablePositive proof it bouncesDiscard
unknownCouldn't determine — never chargedRetry later
Design principle: only addresses with positive proof of non-delivery are ever marked undeliverable. Genuine uncertainty routes to risky with a score — so you never throw away a real lead.

Bulk batches

POST/v1/batches
GET/v1/batches/{id} · progress + summary
GET/v1/batches/{id}/download.csv

Send a JSON array or upload a CSV/XLSX. You get a batch_id immediately (202); prefer a webhook_url over polling. Send an Idempotency-Key so retries never double-charge.

Find an email

POST/v1/find · one person
POST/v1/find/batch · up to 25 people

Give a person's name and their company domain — we generate the likely address formats, rank them by the domain's known pattern (the same intelligence behind catch-all resolution), and SMTP-verify to return the deliverable one. Billed 1 credit only when found; not_found / unknown is free. On a catch-all domain you get a confidence-scored "catch-all" best guess instead of a false "valid". Same price as verification, billed independently — a verify is 1 credit, a find is 1 credit, and doing both is 2.

curl -X POST "https://verify.datomatic.ai/v1/find" \
  -H "Authorization: Bearer dvk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"first":"jane","last":"doe","domain":"acme.com"}'

# Multiple (up to 25) — bills 1 credit per FOUND address:
curl -X POST "https://verify.datomatic.ai/v1/find/batch" \
  -H "Authorization: Bearer dvk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"people":[{"first":"jane","last":"doe","domain":"acme.com"},
               {"full_name":"John Smith","domain":"stripe.com"}]}'
{
  "found": true,
  "email": "jane.doe@acme.com",
  "status": "valid",            // valid | catch-all | not_found | unknown
  "confidence": 0.92,           // 0..1
  "pattern": "{word}.{word}",   // matched local-part shape
  "domain": "acme.com",
  "first": "jane", "last": "doe",
  "mx_found": true, "is_catch_all": false,
  "candidates_tried": 1,
  "alternates": ["jane.doe@acme.com", "jdoe@acme.com", "janedoe@acme.com"]
}
Accuracy-first: on a non-catch-all domain a returned valid is SMTP-confirmed. When the domain accepts all recipients, we never fake a confirmation — you get status:"catch-all" with a confidence you can threshold on.

Domain health

GET/v1/domain-health?domain=…

A DNS-only inspection of a domain's sending setup — MX, SPF, DKIM, DMARC, and Spamhaus DBL blocklist status — with a 0–100 score, an A–F grade, and prioritized fixes. Free (no credits, no SMTP), so you can run it as often as you like — great as a lead magnet or a pre-send check.

{
  "domain": "acme.com",
  "score": 82, "grade": "B",
  "mx":    { "found": true, "records": ["aspmx.l.google.com"] },
  "spf":   { "found": true, "record": "v=spf1 include:_spf.google.com ~all", "policy": "~all" },
  "dkim":  { "found": true, "selectors": ["google"] },
  "dmarc": { "found": true, "policy": "none", "record": "v=DMARC1; p=none; rua=..." },
  "blacklisted": false,
  "issues": ["DMARC is 'p=none' (monitor only) — move to 'quarantine' then 'reject' once clean."]
}

Webhooks

Set webhook_url on a batch and we POST a signed batch.completed event when it finishes. Verify the HMAC-SHA256 signature over {timestamp}.{rawBody} and reject stale timestamps.

HeaderValue
X-ZeMail-Signaturehex HMAC-SHA256
X-ZeMail-Timestampunix seconds
X-ZeMail-Eventbatch.completed

Errors, limits & idempotency

Errors use a consistent envelope: { "error": { type, code, message, doc_url } }. Every official SDK auto-retries 429 and 5xx honoring Retry-After.

StatusMeaning
200 / 202OK / batch queued
400 / 401Validation error / bad key
402Out of credits — top up to continue. (unknown results are never charged.)
429Rate limited — honor Retry-After
5xxRetry with backoff
  • Idempotency: send Idempotency-Key: <uuid> on batch creation — retries never double-charge.
  • Rate limits: X-RateLimit-Limit/Remaining/Reset headers; higher tiers get a wider lane.
  • Billing: unknown is never charged; enhanced credits never expire.

SDKs — every major language

Official, dependency-light clients with the same surface everywhere: verify, batch create/wait/download, account, and a webhook-signature verifier with automatic retry.

Any other language? Generate a client from the OpenAPI spec or call REST directly.

Endpoint reference

GET/v1/verify?email=… — single, real-time
POST/v1/find — find one person's email (name + domain)
POST/v1/find/batch — find up to 25 people
GET/v1/domain-health?domain=… — MX/SPF/DKIM/DMARC/blocklist (free)
POST/v1/batches — create batch (JSON or CSV/XLSX)
GET/v1/batches/{id} — progress + summary
GET/v1/batches/{id}/results — paginated JSON
GET/v1/batches/{id}/download.csv — CSV export
DELETE/v1/batches/{id} — delete now (purge source + results)
GET/v1/account — credits, plan, usage