Every verdict shows its work.
From syntax to SMTP handshake, each stage runs with a method you can read and a reason you can audit — returned on every single verification.
Syntax
RFC-5321/5322 parsing, normalization, length limits. Zero network calls.
DNS / MX resolution
A live lookup for MX and A records. No records, no mailbox: mx_not_found.
Disposable detection
Known throwaway providers matched at the domain level before any probe.
SMTP mailbox probe
A polite MAIL FROM / RCPT TO exchange — nothing is ever sent. Hosts may refuse; when they do, you get the raw response code.
Spam-trap heuristics
Flags for trap-pattern addresses before they poison your sender reputation.
Role-account detection
info@, sales@, support@ — addresses that rotate, get shared, or go unread.
Free-provider flag
gmail.com, outlook.com and friends bounce at higher rates; flagged as context, not a verdict.
Verdict + score
deliverable / risky / undeliverable / unknown, a 0–100 score from the stage outcomes, and a didYouMean typo suggestion when one applies.
Stages run in parallel where safe — the SMTP probe only fires when syntax and DNS pass.
DNS / MX — stage 02, drawn
A live lookup, drawn as the tree it is. Records found, the probe proceeds; none found, the verdict stops here with mx_not_found.
SMTP — stage 04, drawn
The mailbox probe is a polite exchange that stops at RCPT TO. Nothing is ever sent — and when a server refuses, you get the raw code.
Typos — the suggestion stage
The domain fix list (gamil, gmial, gmai, hotmial…) produces a didYouMean suggestion. We flag it; you decide — the verdict never silently rewrites the address.
One call, every reason.
Every verdict ships with the stage results that produced it. If you disagree with a verdict, you can see exactly which rule fired — and re-run the row.
$ 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}
Four verdicts, in practice.
Illustrative rows — the shape of what comes back, with the reason that shipped on each one.
| Address | State | Score | Reason |
|---|---|---|---|
| sarah@bounced-host.example | undeliverable | 0.0 | mx_not_found |
| jordan@gmil.com | risky | 61.0 | did_you_mean |
| info@starter.example | risky | 48.0 | role_address |
| user@10minutemail.example | undeliverable | 0.0 | disposable_domain |
| amy@verisend.example | deliverable | 97.0 | smtp_accept |
| contact@relay-guarded.example | unknown | — | probe_refused |
Sample data for illustration — the shape is real, the rows are not. The score is a probability computed from the stages, never a guarantee of deliverability.
Where those scores come from
The 0–100 score is the sum of seven stage weights. A greylisted probe, for example, earns 80 of the 100 points and lands risky — the marker below shows exactly where that verdict sits.
Known limits
Catch-all servers can accept a probe for an address that doesn't exist. We flag the server; we can't read its mailboxes from the outside.
Gmail, Outlook and similar providers throttle SMTP probing. A refused probe returns risky/unknown with the response code — never a confident invalid.
A score is a probability, not a promise — deliverability depends on the receiver's filtering on the day you send.
Re-running a row costs another credit — and can legitimately land differently. Providers throttle probes, greylists clear, and the reason code on each row tells you exactly which rule fired and why the answer changed.
Stop mailing ghosts.
Start with free credits. No card, no call, no “book a demo”.