Plainwork

Plainwork / MailGate Email Check API / Email validation API reference and score

Email validation API reference: endpoints, fields, scores and errors

Everything the MailGate Email Check API returns, how the 0 to 100 score is worked out, and what each error means. Two endpoints, JSON in and out, CORS open, no signup for the free tier (20 checks a day per IP).

Base URL

https://pw-d20261007a2.plainwork-apps.workers.dev

GET /check

Checks one address. Pass email as a query parameter. Add your key as x-api-key header or ?key= for the paid tier. The x-tier response header says free or paid.

curl "https://pw-d20261007a2.plainwork-apps.workers.dev/[email protected]"
{
  "email": "[email protected]",
  "valid_syntax": true,
  "local": "jane",
  "domain": "gmial.com",
  "disposable": false,
  "free_provider": false,
  "role_account": false,
  "plus_tag": false,
  "did_you_mean": "[email protected]",
  "mx": "a-only",
  "mx_hosts": [],
  "deliverable_domain": true,
  "score": 45,
  "verdict": "risky"
}

POST /bulk (paid key)

Send {"emails": [...]} with up to 100 addresses. Extra addresses beyond 100 are ignored. The response is {"count": n, "results": [...]} in request order, each result shaped like /check.

curl -X POST "https://pw-d20261007a2.plainwork-apps.workers.dev/bulk" \
  -H "x-api-key: YOUR_KEY" -H "content-type: application/json" \
  -d '{"emails":["[email protected]","[email protected]"]}'

Fields

FieldMeaning
valid_syntaxPasses length limits (254 total) and allowed-character rules for the local part and domain.
disposableDomain is on the throwaway list. See the disposable domain list.
role_accountLocal part is a shared mailbox such as info or support; a +tag is ignored. See role-based addresses.
free_providerGmail, Outlook and similar consumer providers.
plus_tagLocal part contains +.
did_you_meanSuggested address when the domain is a known typo. See common domain typos.
mx / mx_hostsDNS result for the domain: mail hosts, or a-only when there is no MX but an A record exists.
deliverable_domainThe domain has working MX (not a null MX) or falls back to an A record.
score, verdictSee below.

How the score works

A valid address starts at 100. If the domain cannot receive mail the score is 0, or 60 when the DNS lookup itself failed (so a flaky resolver does not reject real users). Then: disposable minus 70, role account minus 20, known typo minus 40, A-record-only minus 15. The floor is 0.

VerdictScoreSuggested action
deliverable80 to 100Accept.
risky40 to 79Accept with a prompt: show the typo suggestion, or ask to confirm a role address.
undeliverable0 to 39Reject, or ask for another address (disposable, or domain that cannot receive mail).
invalid0Bad syntax; reason is bad_syntax.

Errors

StatuserrorCause
400missing ?email= or bad JSONRequired input absent.
401invalid_or_inactive_keyThe key is wrong or the subscription ended.
402paid_key_required/bulk called without a key.
429free_limit_reached20 free checks used today from this IP.
404not_foundUnknown path.

Example in JavaScript

const r = await fetch("https://pw-d20261007a2.plainwork-apps.workers.dev/check?email=" + encodeURIComponent(email));
const c = await r.json();
if (c.verdict === "undeliverable" || c.verdict === "invalid") reject();
else if (c.did_you_mean) ask("Did you mean " + c.did_you_mean + "?");

What it does not do

It checks the domain, never the mailbox, and it does not connect to mail servers, so it cannot prove an inbox exists. The disposable list is static (284 domains) and will miss new throwaway services. Calls are not logged beyond a per-IP daily counter for the free tier. Try it live on the free tester, or get unlimited checks and bulk for $5/month on the product page. A machine-readable OpenAPI 3 description ships in the open-source repo.

More from the catalogue

Other small tools

See everything →