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
}
FieldTypeMeaning
emailstringThe address, trimmed and lowercased.
domainstringThe part after @ (punycode for Unicode domains). Left out if the format is bad.
verdictstringvalid, risky or invalid (see below).
reasonstringShort human-readable reason for the verdict.
syntaxbooleanThe address is well-formed.
disposablebooleanThe domain is a known temporary-inbox service.
rolebooleanThe local part reaches a team or a robot (admin@, info@, support@…).
mxstring[]Mail servers (MX), best first. Empty if none were found or the format is bad.
accepts_mailbooleanThe domain can receive mail: it has MX records, or an address record (implicit MX).
suspectbooleanNot 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.
signalstring | nullWhat raised suspect: shared_mx, or null when not suspect.
did_you_meanstringThe big provider this domain looks like a typo of (gamil.com: gmail.com). Empty when no suggestion. It doesn't change the verdict.
free_providerbooleanThe 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 disposable and role to 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
StatusBodyMeaning
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 address

Go (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
}