Vouch API

One authenticated GET verifies an email address: syntax, live MX lookup, a real SMTP RCPT TO handshake and catch-all detection, folded into a single verdict with the receipts. No email is ever sent. Base URL: https://vouch.fast — all responses are JSON unless noted.

Overview

Vouch answers one question — will mail to this address land? — by holding a live SMTP conversation with the receiving mail server, the same opening steps a real delivery uses, and quitting before any message is transmitted. Every answer carries:

  • a status you can branch on — deliverable, risky, undeliverable or unknown,
  • a machine-readable reason (one of 11 fixed codes),
  • a 0–100 score for ranking and thresholds,
  • per-stage checks and quality flags (disposable, role account, free provider), plus a typo suggestion in didYouMean.

Most verifications return in about a second; each SMTP step has a five-second timeout and a whole probe is capped at eight seconds, so Vouch fails fast on unresponsive servers.

Quickstart

Grab an API key from the dashboard(it's shown once, at mint time), then:

curl
curl 'https://vouch.fast/v1/[email protected]' \
  -H 'Authorization: Bearer vch_live_YOUR_KEY'
Node 18+
const res = await fetch(
  `https://vouch.fast/v1/verify?email=${encodeURIComponent(email)}`,
  { headers: { Authorization: `Bearer ${process.env.VOUCH_API_KEY}` } },
);
const result = await res.json();

if (result.status === "deliverable") {
  // send with confidence
} else if (result.didYouMean) {
  // offer the corrected address back to the user
}
Python 3
import os, requests

res = requests.get(
    "https://vouch.fast/v1/verify",
    params={"email": "[email protected]"},
    headers={"Authorization": f"Bearer {os.environ['VOUCH_API_KEY']}"},
    timeout=15,
)
result = res.json()
print(result["status"], result["reason"], result["score"])

Want to see it run before signing in? The Try it box on the homepage calls the same engine, no key required.

Authentication

Every /v1 endpoint takes your API key — format vch_live_… — in the Authorization header:

header (preferred)
Authorization: Bearer vch_live_YOUR_KEY

Where headers are awkward (spreadsheet importers, quick tests) a ?apikey= query parameter is also accepted. Prefer the header: query strings end up in logs.

Keys are credentials, not wallets — rotating or deleting a key in the dashboard never touches your credit balance. A request with a missing, unknown or deactivated key returns 401.

Verify one address

GET/v1/verify?email={address}

Runs the full pipeline — syntax → MX → SMTP probe → catch-all — short-circuiting the network when an earlier stage already settles the verdict (bad syntax, no mail hosts, disposable domain).

Query parameterRequiredDescription
emailyesThe address to verify.
apikeynoAlternative to the Authorization header.

Response — 200

application/json
{
  "email": "[email protected]",
  "user": "ada",
  "domain": "lovelace.dev",
  "status": "deliverable",
  "reason": "mailbox_exists",
  "score": 97,
  "checks": {
    "syntax": true,
    "domainHasMx": true,
    "smtpConnected": true,
    "mailboxExists": true,
    "catchAll": false
  },
  "flags": {
    "disposable": false,
    "roleAccount": false,
    "freeProvider": false
  },
  "didYouMean": null,
  "mxHost": "aspmx.l.google.com",
  "meta": { "durationMs": 412, "checkedAt": "2026-06-12T09:30:00.000Z" }
}
FieldTypeMeaning
email · user · domainstringThe normalized address and its two halves.
statusenumdeliverable · risky · undeliverable · unknown. Branch on this.
reasonenumWhy — one of the 11 codes below.
scoreinteger0–100 confidence, explained below.
checksobjectPer-stage booleans: syntax, domainHasMx, smtpConnected, mailboxExists, catchAll.
flagsobjectdisposable (burner domain), roleAccount (info@, billing@, …), freeProvider (gmail.com, outlook.com, …). Flags inform the score; what to do with them is your policy.
didYouMeanstring · nullA corrected address when the domain looks like a typo ([email protected][email protected]). Ideal for signup-form hints.
mxHoststring · nullThe mail host that answered (or the highest-priority one found).
metaobjectdurationMs and checkedAt (ISO 8601).

Statuses & reason codes

status is the verdict; reason is the evidence. The pairing is fixed — every reason always maps to the same status:

ReasonStatusWhat happened
mailbox_existsdeliverableThe server accepted RCPT TO for this exact mailbox.
catch_allriskyThe domain accepts any local-part, so acceptance proves little. Mail usually lands, but the address may not exist.
role_accountriskyMailbox exists but is a shared role address (info@, support@) — weak for outreach, fine for receipts.
invalid_syntaxundeliverableNot a well-formed mailbox address. Nothing was probed.
no_mx_recordsundeliverableThe domain has no MX (or fallback A) records — nowhere to deliver.
disposable_domainundeliverableA known burner/temp-mail domain. Short-circuits before any network call.
mailbox_not_foundundeliverableThe server rejected the recipient with a 5xx. Sending would bounce.
greylistedunknownThe server said "try again later" (4xx). Re-verify after 15–30 minutes.
connection_failedunknownNo SMTP conversation could be established.
timeoutunknownThe probe ran out of its time budget mid-conversation.
unexpected_errorunknownSomething else went sideways. Retry, then treat as unknown.

Treat unknownas "retry later", not as a verdict — servers greylist, rate-limit and hiccup.

The score

score compresses the verdict and the flags into one 0–100 number: a base by status — deliverable 100, risky 55, unknown 30, undeliverable 0 — multiplied by penalties: disposable forces 0, catch-all ×0.8, role account ×0.8, free provider ×0.9.

Use status for hard branching and score for soft decisions — list cleaning thresholds, lead ranking, or "accept above 80, review 40–80, reject below".

Errors & rate limits

Handlers never throw HTML at you: every API-level problem is JSON shaped { "error": "<code>", "message": "<human text>" }.

HTTPerrorWhen
400missing_emailNo ?email= on /v1/verify.
401missing_api_key · invalid_api_keyNo key, unknown key, or a deactivated key.
402insufficient_creditsThe account's credit balance is empty (header X-Credits-Remaining: 0).
429rate_limitedPer-key limit exceeded — wait Retry-After seconds (also in the body as retryAfter).
500server_errorVouch itself failed. Never charged a credit; safe to retry.

Rate limits

Verification calls are limited per key — 60 per minute by default. Every /v1/verify response carries X-RateLimit-Limit and X-RateLimit-Remaining. Batch status polls and result downloads don't count against the limit, so poll freely.

Need sustained throughput beyond the default? Don't hammer the single endpoint — upload a batch, which runs server-side in parallel, or write [email protected].

Bulk verification

Upload a CSV or Excel file; Vouch scans every cell for addresses, lowercases and de-duplicates them, verifies each unique address in parallel as a background job, and hands back a results CSV. The de-dup report comes back immediately — you only ever pay for unique addresses.

POST/v1/batches

multipart/form-data with the file in a file field. Accepts .csv and .xlsx, up to 20 MB and 10,000 unique addresses per job (the report says if the list was capped).

curl
curl -X POST 'https://vouch.fast/v1/batches' \
  -H 'Authorization: Bearer vch_live_YOUR_KEY' \
  -F '[email protected]'
202 Accepted
{
  "jobId": "684aa1b2c3d4e5f60718",
  "status": "pending",
  "total": 4810,
  "report": {
    "candidatesFound": 5214,
    "duplicatesRemoved": 404,
    "uniqueCount": 4810,
    "capped": false
  }
}

GET/v1/batches

Your recent jobs, newest first, as { "batches": [ … ] } — each entry shaped like the single-job response below.

GET/v1/batches/{id}

Status and progress. Poll until status is complete (it moves pending processingcomplete, or failed with an error message).

200 OK
{
  "id": "684aa1b2c3d4e5f60718",
  "filename": "subscribers.csv",
  "status": "processing",
  "total": 4810,
  "processed": 1932,
  "report": { "candidatesFound": 5214, "duplicatesRemoved": 404, "uniqueCount": 4810, "capped": false },
  "counts": { "deliverable": 1410, "undeliverable": 361, "risky": 102, "unknown": 59, "skipped": 0 },
  "hasResults": false,
  "error": null,
  "createdAt": "2026-06-12T09:14:03.000Z",
  "finishedAt": null
}

GET/v1/batches/{id}/results

The results CSV (text/csv) once the job is complete — 409 not_ready before that. Columns: email, status, reason, score, disposable, roleAccount, freeProvider, catchAll, didYouMean, mxHost.

Prefer clicking to curling? The dashboard has the same upload — drag a file in, watch progress live, download the CSV.

Free list health check

A structural-only diagnosis of a whole list — syntax, disposable domains, MX records, role accounts — with no SMTP probe and no credit charge. The response is aggregate-only: counts and percentages per problem category, never per-address verdicts. Every API key gets one free check.

POST/v1/health-check

JSON body with an emails array — up to 50,000 addresses, de-duplicated on intake. Returns 202 with a background job. A repeat POST replays your existing job (alreadyUsed: true) instead of starting a new one; a failed job doesn't burn the allowance.

curl
curl -X POST 'https://vouch.fast/v1/health-check' \
  -H 'Authorization: Bearer vch_live_YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "emails": ["[email protected]", "[email protected]"] }'
202 Accepted
{
  "jobId": "6a63f1c200a1b2c3d4e5",
  "status": "pending",
  "total": 4810,
  "intake": { "candidatesFound": 5214, "duplicatesRemoved": 404, "uniqueCount": 4810, "capped": false },
  "alreadyUsed": false,
  "message": "Your free List Health Check is running — poll GET /v1/health-check until status is 'complete'."
}

GET/v1/health-check

Poll until status is complete — then the aggregate report appears: a per-category breakdown, independent quality flags, and roll-ups. summary.unhealthy is the headline number. Structurally clean addresses stay okUnverified — only the full SMTP verification can confirm a mailbox exists.

200 OK (complete)
{
  "jobId": "6a63f1c200a1b2c3d4e5",
  "status": "complete",
  "total": 4810,
  "processed": 4810,
  "intake": { "candidatesFound": 5214, "duplicatesRemoved": 404, "uniqueCount": 4810, "capped": false },
  "report": {
    "total": 4810,
    "checkedAt": "2026-06-12T09:30:00.000Z",
    "breakdown": {
      "invalidSyntax": { "count": 96, "pct": 2.0 },
      "disposable": { "count": 231, "pct": 4.8 },
      "noMxRecords": { "count": 185, "pct": 3.8 },
      "roleAccount": { "count": 231, "pct": 4.8 },
      "okUnverified": { "count": 4067, "pct": 84.6 }
    },
    "flags": {
      "freeProvider": { "count": 1920, "pct": 39.9 },
      "typo": { "count": 27, "pct": 0.6 }
    },
    "summary": {
      "undeliverable": { "count": 512, "pct": 10.6 },
      "problematic": { "count": 231, "pct": 4.8 },
      "unhealthy": { "count": 743, "pct": 15.4 },
      "deliverableUnverified": { "count": 4067, "pct": 84.6 }
    },
    "intake": { "candidatesFound": 5214, "duplicatesRemoved": 404, "uniqueCount": 4810, "capped": false }
  },
  "error": null,
  "createdAt": "2026-06-12T09:29:41.000Z",
  "finishedAt": "2026-06-12T09:30:00.000Z",
  "message": "15.4% of your 4810 addresses look undeliverable or problematic. Run a full verification to confirm which of the rest are real, reachable mailboxes."
}

Credits

Vouch is pre-paid: verifications draw down a credit balance that lives on your account, not on any API key.

  • Every new account starts with 250 free credits — granted automatically at signup, no card required.
  • 1 credit per completed verification, whatever the verdict — a definitive undeliverable is exactly as useful as a deliverable.
  • If Vouch itself errors (a 5xx), you are not charged.
  • Batches charge per unique address as it completes — duplicates are removed before verification, free. If the balance runs out mid-job, remaining rows are marked skippedand aren't charged.
  • When enforcement applies and the balance is empty, requests return 402 insufficient_credits; successful /v1/verify responses carry X-Credits-Remaining.

Check your balance and top up from the dashboard.

MCP server

@datapad-nl/vouch-mcp gives any MCP client — Claude Desktop, Claude Code, Cursor, Cline — a verify_email tool backed by this API. The server runs locally via npx; your key is sent only to the Vouch API over HTTPS.

.mcp.json
{
  "mcpServers": {
    "vouch": {
      "command": "npx",
      "args": ["-y", "@datapad-nl/vouch-mcp"],
      "env": { "VOUCH_API_KEY": "vch_live_YOUR_KEY" }
    }
  }
}

Or one command in Claude Code:

terminal
claude mcp add vouch --env VOUCH_API_KEY=vch_live_YOUR_KEY -- npx -y @datapad-nl/vouch-mcp

The tool takes { "email": "…" } and returns a one-line summary plus the same JSON verdict as /v1/verify — each call is a normal verification (1 credit). Ask your assistant “is this address real?” and it does the rest.

OpenAPI

The full machine-readable spec lives at https://vouch.fast/openapi.json (OpenAPI 3.1) — point your generator, Postman, or agent at it. There's also a plain-text overview for LLMs at /llms.txt.