API Documentation

The VerifyAnyEmail REST API lets you verify email addresses one at a time or in bulk. Base URL: https://api.verifyany.email. An interactive OpenAPI explorer is available at https://api.verifyany.email/docs.

Authentication

Create an API key in your dashboard and send it as a bearer token on every request:

Authorization: Bearer vae_your_api_key

Test mode

Build and test your integration without spending credits or probing real mailboxes. Any address at @test.verifyany.email returns a deterministic canned result — no credit is charged and nothing is stored. Use them to exercise each branch of your code:

Test addressstatussubStatus
deliverable@test.verifyany.emaildeliverablevalid_mailbox
undeliverable@test.verifyany.emailundeliverablemailbox_not_found
risky@test.verifyany.emailriskycatch_all
unknown@test.verifyany.emailunknownsmtp_unavailable
disposable@test.verifyany.emailriskydisposable
role@test.verifyany.emailriskyrole_based

Verify a single email

POST /v1/verify — costs 1 credit.

curl -X POST https://api.verifyany.email/v1/verify \
  -H "Authorization: Bearer vae_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"email":"john@example.com"}'

Example response · 200 OK

{
  "ok": true,
  "result": {
    "email": "john@example.com",
    "normalized": "john@example.com",
    "account": "john",
    "domain": "example.com",
    "status": "deliverable",
    "subStatus": "valid_mailbox",
    "score": 95,
    "checks": {
      "syntax": true,
      "hasMxRecords": true,
      "smtpConnectable": true,
      "mailboxExists": true,
      "catchAll": false,
      "roleBased": false,
      "disposable": false,
      "freeProvider": false,
      "possibleTypo": false,
      "gibberish": false
    },
    "suggestion": null,
    "safeToSend": true,
    "durationMs": 480,
    "checkedAt": "2026-07-18T16:00:00.000Z"
  }
}

Response format

Every verification returns a result object with these fields:

FieldTypeDescription
statusstringdeliverable · undeliverable · risky · unknown
subStatusstringFine-grained reason, e.g. valid_mailbox, mailbox_not_found, catch_all
scorenumberConfidence 0–100 in the status
checksobjectIndividual signal results (syntax, hasMxRecords, mailboxExists, catchAll…)
suggestionstring|null“Did you mean” domain correction for probable typos
safeToSendbooleanTrue when the address is safe to send to

Statuses

deliverableMailbox exists and accepts mail — safe to send.
undeliverableInvalid syntax, no MX, or the mailbox was rejected.
riskyAccepts mail but low quality: catch-all, role or disposable.
unknownCould not determine (greylisting, timeout, or probe disabled).

Batch verification

Batch verification is asynchronous. You submit a list, we queue it, and you either poll for status or receive a webhook when it completes. Each address costs 1 credit.

  1. POST /v1/verify/batch — submit up to your plan's max addresses. Returns 202 with a batch id.
  2. GET /v1/verify/batch/{id} — poll for status & live counts.
  3. GET /v1/verify/batch/{id}/results — fetch results (paginated: ?page=&pageSize=).

1. Submit a batch

curl -X POST https://api.verifyany.email/v1/verify/batch \
  -H "Authorization: Bearer vae_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"emails":["a@example.com","b@example.com"],"name":"my-list"}'

Optional body fields: name (label) and webhookUrl (called on completion).

Response · 202 Accepted

{
  "ok": true,
  "batch": { "id": "cmrq…", "status": "queued", "total": 2 },
  "message": "Batch queued. Poll the status endpoint or configure a webhook."
}

2. Check status

GET /v1/verify/batch/{id} · 200 OK

{
  "ok": true,
  "batch": {
    "id": "cmrq…",
    "name": "my-list",
    "status": "completed",          // queued | processing | completed | failed | canceled
    "total": 2,
    "processed": 2,
    "summary": {
      "deliverable": 1,
      "undeliverable": 1,
      "risky": 0,
      "unknown": 0
    },
    "createdAt": "2026-07-18T16:00:00.000Z",
    "completedAt": "2026-07-18T16:00:07.000Z"
  }
}

3. Fetch results

GET /v1/verify/batch/{id}/results · 200 OK

{
  "ok": true,
  "page": 1,
  "pageSize": 100,
  "total": 2,
  "results": [
    { "email": "a@example.com", "normalized": "a@example.com",
      "status": "deliverable", "subStatus": "valid_mailbox",
      "score": 95, "suggestion": null,
      "createdAt": "2026-07-18T16:00:05.000Z" },
    { "email": "b@example.com", "normalized": "b@example.com",
      "status": "undeliverable", "subStatus": "mailbox_not_found",
      "score": 92, "suggestion": null,
      "createdAt": "2026-07-18T16:00:06.000Z" }
  ]
}

Webhook payload

If you pass webhookUrl, we POST this when the batch finishes (signed with header X-VAE-Signature: sha256=…):

{
  "event": "batch.completed",
  "data": {
    "batchId": "cmrq…",
    "total": 2,
    "deliverable": 1,
    "undeliverable": 1,
    "risky": 0,
    "unknown": 0
  },
  "sentAt": "2026-07-18T16:00:07.000Z"
}

Suppression list

Maintain an account-level list of emails and whole domains that should be auto-skipped during verification. A suppressed address returns immediately with subStatus: "suppressed" andsuppressed: true — no SMTP probe runs and no credit is charged, whether it comes through /v1/verify or a batch. Send addresses as a plain text array.

# Add emails and/or domains
curl -X POST https://api.verifyany.email/v1/suppression \
  -H "Authorization: Bearer vae_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "values": ["john@old.com", "spam-trap.example"], "reason": "unsubscribed" }'

# List entries
curl https://api.verifyany.email/v1/suppression -H "Authorization: Bearer vae_your_api_key"

# Remove entries
curl -X DELETE https://api.verifyany.email/v1/suppression \
  -H "Authorization: Bearer vae_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "values": ["john@old.com"] }'

values accepts a mix of emails and bare domains. You can also manage the list from your dashboard.

Webhooks

Instead of polling, you can be notified when a batch finishes. There's nothing to register — just include a webhookUrl when you submit the batch, and we POST thebatch.completed event to it on completion.

Specify a webhook

curl -X POST https://api.verifyany.email/v1/verify/batch \
  -H "Authorization: Bearer vae_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "emails": ["a@example.com", "b@example.com"],
    "name": "my-list",
    "webhookUrl": "https://your-app.com/webhooks/vae"
  }'

You can also add a webhook URL when uploading a list from the dashboard.

Verify the signature

Every delivery includes an X-VAE-Signature: sha256=… header — an HMAC-SHA256 of the raw request body using your webhook signing secret(find it in Settings). Always verify it before trusting a payload:

import crypto from "crypto";

// Express: use a raw body parser for this route so the bytes are unmodified.
app.post("/webhooks/vae", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.headers["x-vae-signature"];             // "sha256=…"
  const signature = Array.isArray(header) ? header[0] : header;
  const expected =
    "sha256=" + crypto.createHmac("sha256", process.env.VAE_WEBHOOK_SECRET)
      .update(req.body).digest("hex");

  // timingSafeEqual throws if the buffers differ in length, so compare lengths
  // first (and require a well-formed header) before the constant-time check.
  const a = Buffer.from(signature ?? "", "utf8");
  const b = Buffer.from(expected, "utf8");
  const ok = a.length === b.length && crypto.timingSafeEqual(a, b);
  if (!ok) return res.status(401).end();

  const { event, data } = JSON.parse(req.body.toString());
  // event === "batch.completed", data.batchId, data.deliverable, …
  res.sendStatus(200);
});
Tips. Respond 2xx quickly (do heavy work async); we retry failed deliveries with exponential backoff. Verify against the raw bytes — don't re-serialize the JSON first, or the signature won't match.

Rate limits

Requests are rate-limited per API key according to your plan. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers.

Errors

Errors return a JSON body with an error code and human-readable message.

400invalid_inputMalformed request body
401unauthorizedMissing or invalid API key
402insufficient_creditsOut of verification credits
429rate_limitedToo many requests

Diagnostics API

Every free DNS & deliverability tool is also available as an authenticated API, so you can run the same checks at scale (monitoring, dashboards, bulk domain audits). These endpoints use the same Authorization: Bearer API key and are rate-limited by your plan. Diagnostic calls do not consume verification credits.

EndpointPurpose
GET /v1/tools/mx?domain=MX records, hosts, resolved IPs, provider
GET /v1/tools/spf?domain=SPF record + recursive DNS-lookup count
GET /v1/tools/dkim?domain=&selector=DKIM key by selector (or common-selector discovery)
GET /v1/tools/dmarc?domain=DMARC policy, alignment, reporting
GET /v1/tools/bimi?domain=BIMI record, logo and certificate checks
GET /v1/tools/mta-sts?domain=MTA-STS policy (DNS + HTTPS policy file)
GET /v1/tools/tls-rpt?domain=TLS-RPT record and reporting address
GET /v1/tools/smtp?host=SMTP banner, EHLO, STARTTLS, TLS, certificate
GET /v1/tools/reverse-dns?ip=PTR + forward-confirmed reverse DNS
GET /v1/tools/blacklist?target=IP/domain against 35+ DNS blocklists
GET /v1/tools/dns-propagation?domain=&type=Compare a record across public resolvers
GET /v1/tools/health?domain=Aggregated inbound email-health report + score
POST /v1/tools/header-analysisParse raw email headers (JSON body { headers })

Example

curl "https://api.verifyany.email/v1/tools/spf?domain=example.com" \
  -H "Authorization: Bearer vae_your_api_key"

Each returns a result object with status (pass / warn / fail / unknown), summary, records and findings (each with a recommendation). The same shape powers the public tool pages.