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.
Always call the www host. The apex mailsurity.com issues a 308 redirect to www.mailsurity.com, and curl (with -L) and many HTTP clients drop the Authorization header on that cross-host redirect, which surfaces as a confusing Invalid authorization header error.
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:
// npm i mailsurity (server-side only, keep your key secret)
import { Mailsurity } from "mailsurity";
const ms = new Mailsurity({ apiKey: process.env.MAILSURITY_API_KEY! });
// Convenience boolean, fail-open: a transient outage never blocks a signup
if (await ms.isDisposable("user@mailinator.com")) {
// reject the signup
}
// ...or the full result
const verdict = await ms.checkEmail("user@mailinator.com");
console.log(verdict.isDisposable); // trueA 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 | The verdict is returned. | N/A |
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
- Every verdict (list hit, cache hit, or full live detection) costs exactly one credit. Errored requests (4xx/5xx) aren't charged, except that a
402is itself the "out of credits" signal. - New accounts start with 500 free credits, no card required.
- Paid plans grant a monthly credit allotment that resets each billing cycle and doesn't roll over. See Pricing.
- Checking 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 client-side before you call the API, so a malformed address doesn't cost you a credit and a round trip on a
400. - Branch your logic on
isDisposable, notsource. The boolean is the contract;sourceandsignalsare for logging and tuning your own thresholds. - Treat
nullas genuinely unknown, not as false. Decide whether to allow, soft-flag, or challenge rather than hard-blocking. - Retry
5xxwith backoff, and surface402to whoever manages billing rather than retrying it. - If you re-check the same domains often, cache the verdict on your side. Verdicts are stable, and it conserves credits.
Framework guides
Wiring this into a specific stack? These guides show the exact integration point (webhook handler, Action, or API route) for the frameworks Mailsurity customers use most.
- How to block disposable emails in Next.js
- Block fake signups in Clerk and Supabase Auth
- Block disposable signups in Auth0 with a Pre User Registration Action
See all guides at /guides.
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.