An API that shows its work.
Bearer-token auth, JSON in and out, and a reason attached to every verdict. Create a key in the dashboard, then read on.
Quick start
The verdict
Four states, plus a 0–100 score and a didYouMean typo suggestion when one applies.
$ curl -X POST https://api.multivalidator.io/api/v1/verify \ -H "Authorization: Bearer mv_live_..." \ -H "Content-Type: application/json" \ -d '{"email": "sarah@bounced-host.example"}'{ "email": "sarah@bounced-host.example", "state": "undeliverable", "qualityScore": 0, "stages": { "syntax": { "pass": true, "normalizedEmail": "sarah@bounced-host.example" }, "dns": { "pass": false, "reason": "Domain has no MX or A records" }, "disposable": { "pass": true, "isDisposable": false, "domain": "bounced-host.example" } }, "didYouMean": null, "latencyMs": 214}
Webhooks
Register an endpoint and the service fires verification.completed for every finished check. Failed deliveries retry up to three times with backoff; the payload carries the same reason field as the API response.
{ "event": "verification.completed", "payload": { "email": "sarah@bounced-host.example", "state": "undeliverable", "qualityScore": 0, "reason": "Domain has no MX or A records" }}
The handshake, drawn
stage 04 · smtp — nothing is ever sentThe mailbox probe is a polite exchange that stops at RCPT TO. When the server refuses — as here, with a 550 — you get the raw code, and the verdict stops being a guess.
Authentication
Every authenticated call sends a bearer token. Keys are created in Dashboard → API keys and deleted the same way; keep them server-side, because a key is a billing address. The anonymous GET endpoint exists for one-off lookups — rate-limited and never charged.
Authorization: Bearer mv_live_...# All endpoints accept the same header.# The dashboard lists your keys and what they can access.
Response schema
Every field, drawn in place — hover either side to trace a field from the payload to its meaning.
POST /v1/verify · 200
{"email": "sarah@bounced-host.example","state": "undeliverable","qualityScore": 0,"stages": {"syntax": { "pass": true },"dns": { "pass": false, "reason": "Domain has no MX or A records" }},"reason": "mx_not_found","didYouMean": null,"latencyMs": 214}
Field map · hover to trace
- emailstring
The address as sent, unchanged.
- stateenum
deliverable · risky · undeliverable · unknown
- qualityScorenumber 0–100
Computed from the stage outcomes; a probability, not a promise.
- stagesobject
One entry per stage with pass and reason. The audit trail.
- reasonstring · optional
Present when a stage fails hard — e.g. mx_not_found.
- didYouMeanstring · nullable
Typo suggestion for the domain when one applies.
- latencyMsnumber
Time spent in the pipeline, in milliseconds.
Errors
- 200Verification complete. One credit debited.
- 400Email is required and must be a string.
- 403Insufficient credits. Purchase more from your dashboard.
- 413Request body too large.
- 429Rate limit exceeded. Headers carry the reset time.
Limits & credits
Synchronous POST
1 credit per completed check
Anonymous GET
Rate-limited, never spends credits — for quick lookups
Bulk jobs
Queue-based; progress streams from /bulk/status
Failed requests
Never cost a credit — debits only on completed checks
429 responses include the reset window in the headers. If you are hitting the anonymous limits, authenticate — that moves you onto the credit-based path and the honest math on the pricing page.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/verify | Verify a single address. Body: { "email": "..." }. Costs 1 credit for authenticated keys. |
| GET | /api/v1/verify?email=... | Anonymous, rate-limited check — no key required, never spends your credits. |
| POST | /api/v1/bulk/upload | Upload a CSV to storage for a bulk job. |
| POST | /api/v1/bulk/process | Start processing a bulk job. |
| GET | /api/v1/bulk/status?jobId=... | Poll a bulk job’s progress. |
| GET | /api/v1/bulk/download?jobId=... | Download the verified results CSV. |
| GET | /api/v1/keys | List your API keys (dashboard → API keys). |
| POST | /api/v1/keys | Create an API key. |
| DELETE | /api/v1/keys?id=... | Delete an API key. |
| POST | /api/v1/webhooks | Create a webhook endpoint for verification.completed. |
| POST | /api/v1/billing/checkout | Create a Stripe checkout session for a plan. |
| POST | /api/v1/billing/portal | Open the Stripe billing portal. |
Stop mailing ghosts.
Start with free credits. No card, no call, no “book a demo”.