Developer docs
Email Verification API
One endpoint, bearer-token auth, JSON in and out. Send an email address, get a disposable / legitimate verdict in milliseconds — from whatever stack you already run.
Introduction
The Mailsurity API tells you, in a single request, whether an email address is disposable — a throwaway, burner, or temporary inbox — or a legitimate address worth keeping in your funnel. It is a JSON-over-HTTPS API: one endpoint, bearer-token auth, no SDK required.
Every verdict flows through a layered cascade — curated allow/deny lists, a global disposable-domains list, a 30-day verdict cache, and finally live heuristics (MX records, local-part entropy, brand similarity, mail-server reputation) with an AI classifier for borderline cases. The response tells you which layer produced the answer via the source field.
Base URL
All requests go to a single HTTPS base. There is no versioned path prefix.
HTTPS is required; plain-HTTP requests are rejected. All request and response bodies are application/json encoded as UTF-8.
Authentication
Authenticate every request with your team's API key as a bearer token in the Authorization header. Keys are issued per team and can be revoked at any time from the dashboard.
- Treat your key like a password — keep it server-side, in an environment variable, never in client-side code or a public repo.
- Requests with a missing or malformed header get
401 Unauthorized; an unrecognized key also returns401. - Rotate a key by issuing a new one and revoking the old one from API Keys. Revocation is immediate.
Quickstart
Send a POST with one JSON field — email — and read isDisposable off the response. Pick your language:
curl -X POST https://mailsurity.com/api/check-email \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "user@mailinator.com"}'A throwaway address comes back like this:
{
"email": "user@mailinator.com",
"isDisposable": true,
"source": "list"
}Check an email
Evaluates a single email address and returns a disposable verdict. This is the core, metered endpoint — each successful call costs one credit (see Credits & limits).
Request
Headers
| Header | Required | Value |
|---|---|---|
Authorization | Yes | Bearer YOUR_API_KEY |
Content-Type | Yes | application/json |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | The full email address to evaluate. Must be syntactically valid (local@domain.tld). Only the domain is used for the verdict — the local part is never stored. |
A minimal request body:
{ "email": "user@mailinator.com" }Response
A 200 OK returns a JSON verdict. Which fields are present depends on how the verdict was resolved — email, isDisposable, and source are always there; the rest are added only when a live detection runs.
| Field | Type | Always | Description |
|---|---|---|---|
email | string | Yes | Echo of the submitted address. |
isDisposable | boolean | null | Yes | true if the address is disposable, false if legitimate, null when the verdict is genuinely unknown. |
source | string | Yes | Which cascade layer answered. See Verdict sources. |
classification | string | No | disposable, legitimate, or unknown. Present on live/cached verdicts. |
confidence | number | No | Model confidence from 0 to 1. Present on live/cached verdicts. |
reason | string | No | Human-readable explanation of the verdict. |
signals | object | No | Feature breakdown behind the verdict (see below). Present on live/cached verdicts. |
matchedRule | object | No | Present only when one of your team allow/deny rules matched — { kind, value }. |
The signals object
| Field | Type | Description |
|---|---|---|
mxValid | boolean | The domain publishes valid MX (mail-exchange) records. |
entropy | number | Shannon entropy of the local part — high values suggest a randomly generated inbox. |
levenshteinMatch | string | null | Nearest known throwaway-mail brand if the domain is a close lookalike, else null. |
mxClusterSuspicious | boolean | The domain shares mail servers with known disposable providers. |
Verdict sources
The source field reports which layer of the detection cascade produced the verdict, in precedence order (earlier layers win):
| source | Meaning |
|---|---|
allowlist | Matched one of your team allow rules — forced to legitimate. |
denylist | Matched one of your team deny rules — forced to disposable. |
legit-list | A curated, globally known-legitimate domain (e.g. gmail.com). |
list | Matched the curated global disposable-domains list. |
cache | A cached result from a recent live detection of this domain. |
heuristic | Resolved live from MX, entropy, brand similarity, and cluster reputation. |
llm | A borderline case escalated to the AI classifier. |
Response examples
Disposable — resolved live (full payload)
{
"email": "user@mailinator.com",
"isDisposable": true,
"classification": "disposable",
"confidence": 0.94,
"reason": "high-entropy local part; MX cluster flagged disposable",
"source": "heuristic",
"signals": {
"mxValid": true,
"entropy": 3.7,
"levenshteinMatch": "mailinator",
"mxClusterSuspicious": true
}
}Legitimate — fast legit-list hit
{
"email": "jane@gmail.com",
"isDisposable": false,
"source": "legit-list"
}Team rule match
When a team allow/deny rule matches, it overrides every other layer:
{
"email": "partner@trusted-partner.com",
"isDisposable": false,
"source": "allowlist",
"matchedRule": { "kind": "domain", "value": "trusted-partner.com" }
}Errors
Errors use standard HTTP status codes and return a JSON body of the shape { "error": "message" }.
| Status | Meaning | Example body |
|---|---|---|
200 | Success — verdict returned. | — |
400 | Bad request — the email is missing or not a valid address. | { "error": "Invalid email format" } |
401 | Unauthorized — missing/malformed header or unrecognized key. | { "error": "Invalid token" } |
402 | Payment required — the team is out of credits. | { "error": "Insufficient credits" } |
500 | Server error — something failed on our side; safe to retry. | { "error": "Internal server error" } |
402 still applies even when a team allow-rule would have matched. Top up or wait for your cycle to renew.Credits & limits
- One credit per successful call. Every verdict — list hit, cache hit, or full live detection — costs exactly one credit. Errored requests (4xx/5xx) are not charged, except that a
402is itself the "out of credits" signal. - Free tier: 500 credits granted once at signup, no card required.
- Paid plans grant a monthly credit allotment that resets each billing cycle and does not roll over. See Pricing.
- Need to check thousands of addresses at once? Use the Bulk Check CSV uploader instead of looping the API — it's billed per unique domain.
Best practices
- Validate syntax first. Reject obviously malformed input client-side so you don't spend a credit (and a round-trip) on a
400. - Branch on
isDisposable, notsource. The boolean is the contract;sourceandsignalsare for logging and tuning your own thresholds. - Treat
nullgracefully. Anullverdict means "unknown" — decide whether to allow, soft-flag, or challenge rather than hard-blocking. - Handle
402and retry5xx. Back off and retry on500; surface402to whoever manages billing. - Cache on your side if you re-check the same domains often — verdicts are stable, and it conserves credits.
Support
Questions, edge cases, or a domain you think we're scoring wrong? Reach us through the contact page, and check the API status page for live health.