Skip to content

Email validations

Check an address before you mail it: is it well-formed, does its domain accept mail, is it a throwaway or a role inbox. Use it on sign-up forms and before importing a list, so bad addresses never reach your bounce rate.

What a validation checks#

A validation is a set of lookups, not a delivery attempt. No mail is sent and the recipient's mail server is never contacted, so a deliverable result means the address is well-formed and its domain publishes MX records — not that this particular mailbox exists. A typo in the local part (jhon@gmail.com) comes back deliverable.

checks.syntaxboolean
The address is well-formed. The request body is already validated as an email address before any check runs (a malformed address is a 400), so through the API this is always true.
checks.mxboolean
The domain has at least one MX record, looked up through public resolvers (8.8.8.8, 1.1.1.1). A domain with only an A record counts as no MX, and so does a lookup that times out or fails.
checks.disposableboolean
The domain is on PostStack's list of about 5,000 disposable/temporary inbox providers (e.g. mailinator.com). Exact domain match — subdomains of a listed domain are not flagged.
checks.roleboolean
The whole local part is a role name such as info, admin, support, billing, noreply or postmaster. Exact match: info.sales@ is not a role address.
checks.freeboolean
The domain is a free mailbox provider (Gmail, Outlook, Yahoo and similar). Informational only — it never changes result or risk.

Results are cached per address for one hour, so validating the same address again within the hour returns the earlier answer without repeating the DNS lookup — including an undeliverable caused by a DNS timeout.

How the verdict is decided#

The first matching row wins:

Conditionresultrisk
No MX records (or invalid syntax)undeliverablehigh
Disposable domainriskyhigh
Role addressriskymedium
None of the abovedeliverablelow

What to do with each result

  • undeliverable — reject it on a form (“that domain doesn't receive email”), drop it from an import.
  • risky with risk: "high" — a throwaway inbox. Fine for a one-off receipt, a poor fit for a mailing list.
  • risky with risk: "medium" — a shared role inbox. It usually works, but role addresses complain and unsubscribe more, so keep them off marketing lists unless someone asked.

Endpoints#

Base URL https://api.poststack.dev. Authenticate with Authorization: Bearer sk_live_…; both full-access and sending-access API keys can validate. Rate limits apply per credential; over the limit you get 429 with a Retry-After header in seconds.

Validate an address#

Validate one address and get the verdict in the response. Limited to 60 requests per minute.

POSThttps://api.poststack.dev/email-validations
Request
const validation = await poststack.emailValidations.validate(
  'alice@example.com',
);

if (validation.result === 'undeliverable') {
  // Ask the user to check the address before you create the account.
}
{
  "email": "alice@example.com",
  "result": "deliverable",
  "checks": {
    "syntax": true,
    "mx": true,
    "disposable": false,
    "role": false,
    "free": false
  },
  "risk": "low"
}

Body parameters

emailstringrequired
The address to check. Must be a valid email address, at most 320 characters.

Response fields

emailstring
The address you sent, trimmed and lowercased.
result"deliverable" | "risky" | "undeliverable"
The verdict — see "How the verdict is decided".
risk"low" | "medium" | "high"
How risky sending to the address is, derived from the same checks.
checksobject
The individual checks: syntax, mx, disposable, role, free (all booleans).

Status codes

StatusMeaning
200The validation result.
400email is missing, not an email address, or over 320 characters, e.g. { "error": "email: Invalid email address" }.
401Missing, invalid or revoked API key.
403No team on the credential, the team is suspended, or an OAuth token lacks the email:send scope ({ "error": "insufficient_scope" }).
429More than 60 validations in a minute from this credential.
500{ "error": "Failed to validate email" }. Safe to retry.

Validate a batch#

Validate up to 100 addresses in one request. Results come back in the same order as the input. Every entry must be a valid email address — one malformed entry fails the whole request with 400, so strip obvious garbage first. Limited to 10 requests per minute.

POSThttps://api.poststack.dev/email-validations/batch
Request
const { results } = await poststack.emailValidations.validateBatch([
  'alice@example.com',
  'bob@mailinator.com',
  'info@example.org',
]);

const keep = results.filter((r) => r.result === 'deliverable');
{
  "results": [
    {
      "email": "alice@example.com",
      "result": "deliverable",
      "checks": { "syntax": true, "mx": true, "disposable": false, "role": false, "free": false },
      "risk": "low"
    },
    {
      "email": "bob@mailinator.com",
      "result": "risky",
      "checks": { "syntax": true, "mx": true, "disposable": true, "role": false, "free": false },
      "risk": "high"
    },
    {
      "email": "info@example.org",
      "result": "risky",
      "checks": { "syntax": true, "mx": true, "disposable": false, "role": true, "free": false },
      "risk": "medium"
    }
  ]
}

Body parameters

emailsstring[]required
1–100 addresses, each a valid email address of at most 320 characters.

Response fields

results[].emailstring
The address you sent, trimmed and lowercased.
results[].result"deliverable" | "risky" | "undeliverable"
The verdict — see "How the verdict is decided".
results[].risk"low" | "medium" | "high"
How risky sending to the address is, derived from the same checks.
results[].checksobject
The individual checks: syntax, mx, disposable, role, free (all booleans).

Status codes

StatusMeaning
200One result per input address, in input order.
400emails is missing, empty or over 100 entries, or an entry is not a valid address — the error names its index, e.g. { "error": "emails.3: Invalid email address" }.
401Missing, invalid or revoked API key.
403No team on the credential, the team is suspended, or an OAuth token lacks the email:send scope ({ "error": "insufficient_scope" }).
429More than 10 batch requests in a minute from this credential.
500{ "error": "Failed to validate emails" }. Safe to retry.

From other tools#

The same two calls in the Python SDK and the MCP server. You can also validate by hand on the Email validation page in the dashboard.

from poststack import PostStack

client = PostStack(api_key="sk_live_...")

one = client.email_validations.validate("alice@example.com")
many = client.email_validations.validate_batch(["alice@example.com", "bob@mailinator.com"])

Troubleshooting#

A real address came back undeliverable

The domain's MX lookup failed or returned nothing. Check with dig MX example.com. Domains that receive mail only through an A record (no MX) are reported undeliverable, and a DNS timeout is too. Because results are cached for an hour, re-validating straight away returns the same answer.

The whole batch failed with 400

One entry is not a valid address; the error names its position (emails.3 is the fourth). Remove or fix it and resend — nothing from the failed request was validated.

A deliverable address bounced

Validation cannot see whether a mailbox exists, only whether the domain accepts mail. The bounce adds the address to your suppression list if it proves the mailbox is dead.

Next steps

Was this page helpful?

Related