API docs
GET /v1/check?email=
The request
GET https://mailvet.dev/v1/check?email=jane@example.com
Authorization: Bearer mv_live_…The free list is a file you have to fetch, host and keep current. MailVet gives you the same list refreshed daily, plus a signal for domains not on it yet, from one API call.
One call checks the format, the mail servers (MX), disposable domains and role addresses, and flags typos of big providers. No SMTP probing, so it can’t tell whether a mailbox exists.
Call it from your server, never from the browser. Anything in front-end code is public: your key would leak and anyone could spend your checks.
The response
{
"email": "jane@mailinator.com",
"domain": "mailinator.com",
"verdict": "risky",
"reason": "mailinator.com is a disposable (throwaway) email service.",
"syntax": true,
"disposable": true,
"role": false,
"mx": ["mail.mailinator.com"],
"accepts_mail": true,
"suspect": false,
"signal": null,
"did_you_mean": "",
"free_provider": false
}| Field | Type | Meaning |
|---|---|---|
email | string | The address, trimmed and lowercased. |
domain | string | The part after @ (punycode for Unicode domains). Left out if the format is bad. |
verdict | string | valid, risky or invalid (see below). |
reason | string | Short human-readable reason for the verdict. |
syntax | boolean | The address is well-formed. |
disposable | boolean | The domain is a known temporary-inbox service. |
role | boolean | The local part reaches a team or a robot (admin@, info@, support@…). |
mx | string[] | Mail servers (MX), best first. Empty if none were found or the format is bad. |
accepts_mail | boolean | The domain can receive mail: it has MX records, or an address record (implicit MX). |
suspect | boolean | Not on the list, but the domain shares mail servers with known disposable services. It does not change the verdict, and disposable stays false. False for domains not yet sampled: coverage is partial and growing. |
signal | string | null | What raised suspect: shared_mx, or null when not suspect. |
did_you_mean | string | The big provider this domain looks like a typo of (gamil.com: gmail.com). Empty when no suggestion. It doesn't change the verdict. |
free_provider | boolean | The domain is a major free mailbox provider, such as gmail.com or outlook.com. |
Verdicts
suspect is a signal only: a suspect domain gets the verdict the other checks give it. Decide yourself whether to act on it.
- valid Well-formed, the domain receives mail, not disposable, not a role address.
- risky A disposable domain, a role address (info@, admin@…), or DNS couldn’t be reached. Check
disposableandroleto decide: most signup forms block disposable addresses. - invalid Bad format, or the domain has no mail server. Reject it.
Quota, limits and errors
Each successful check (200) counts once against your monthly quota, which resets on the 1st (UTC). Errors don’t count. Every response carries:
X-Quota-Limit: 5000
X-Quota-Remaining: 4812| Status | Body | Meaning |
|---|---|---|
| 400 | {"error":…} | The email parameter is missing. |
| 401 | {"error":…} | Missing, unknown or revoked key. |
| 429 | {"error":"quota_exceeded"} | This month’s checks are used up. Upgrade or wait for the 1st. |
| 429 | {"error":"rate_limited"} | More than 10 requests a second on one key. Back off and retry. |
Examples
Put your key in an environment variable (MAILVET_KEY) on the server.
curl
curl -s "https://mailvet.dev/v1/check?email=jane@mailinator.com" \
-H "Authorization: Bearer $MAILVET_KEY"Node (fetch)
const res = await fetch(
"https://mailvet.dev/v1/check?email=" + encodeURIComponent(email),
{ headers: { Authorization: `Bearer ${process.env.MAILVET_KEY}` } },
);
if (!res.ok) throw new Error(`mailvet ${res.status}`);
const check = await res.json();
if (check.verdict !== "valid") {
// reject the signup, or ask for another address
}Python (requests)
import os, requests
r = requests.get(
"https://mailvet.dev/v1/check",
params={"email": email},
headers={"Authorization": f"Bearer {os.environ['MAILVET_KEY']}"},
timeout=10,
)
r.raise_for_status()
check = r.json()
if check["verdict"] != "valid":
... # reject the signup, or ask for another addressGo (net/http)
q := url.Values{"email": {email}}
req, _ := http.NewRequest("GET", "https://mailvet.dev/v1/check?"+q.Encode(), nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("MAILVET_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
var check struct {
Verdict string `json:"verdict"`
Reason string `json:"reason"`
}
if err := json.NewDecoder(res.Body).Decode(&check); err != nil {
return err
}