← Back to blog

Email Validation at Send Time: Typo Detection, MX Lookups, and Bounce Suppression

PushMail Team··4 min read

Every hard bounce damages your sender reputation. ISPs like Gmail, Outlook, and Yahoo track your bounce rate, and if it crosses about 2%, they start throttling your delivery. Push past 5%, and your emails go straight to spam -- or get blocked entirely.

The fix is straightforward: validate addresses before you send. Check the syntax, check the domain, check if the address has bounced before. PushMail does all of this automatically, and it costs you nothing.

What PushMail checks

Every address validated through PushMail goes through five checks, in order.

RFC syntax validation. Is there an @ sign? Is the local part under 64 characters? Is the domain under 253 characters? Are there spaces, double dots, or other malformed patterns? This catches obvious garbage before any network calls happen.

Global bounce suppression. PushMail maintains a shared suppression list in Cloudflare KV. When any address hard-bounces or generates a spam complaint through any PushMail sender, it's added to this list. If user@example.com bounced for another sender three months ago, it's flagged before you waste a send. The entry stores the reason -- hard_bounce or complaint -- so you know why.

Disposable email detection. Addresses from known throwaway providers -- Mailinator, Guerrilla Mail, Tempmail, YOPmail, 10MinuteMail, and over 60 others -- are flagged as invalid. These addresses inflate your list, never convert, and often don't accept delivery. They're a pure waste of a send.

Domain typo detection. Common misspellings of major providers are caught. user@gmial.com returns valid: true but includes a suggestion field with user@gmail.com. Same for hotmial.com, outlok.com, yaho.com, icoud.com, and dozens of other variants. The typo doesn't cause a hard rejection -- the suggestion lets you prompt the user before they submit.

MX record lookup. PushMail queries the domain's MX records via Cloudflare's DNS-over-HTTPS resolver to confirm the domain actually accepts email. If there are no MX records, it falls back to checking for an A record (per RFC 5321). If neither exists, the domain can't receive mail, and the address is rejected. Results are cached in KV -- 7-day TTL for valid domains, exponential backoff for invalid ones starting at 6 minutes and doubling up to roughly 4 days.

The validation endpoint

Call POST /v1/validate with a JSON body:

curl -X POST https://pushmail.dev/api/v1/validate \
  -H "Authorization: Bearer pm_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"email": "user@gmial.com"}'

Response:

{
  "data": {
    "valid": true,
    "email": "user@gmial.com",
    "checks": {
      "syntax": true,
      "notBounced": true,
      "mxRecords": true,
      "notDisposable": true,
      "notRoleBased": true
    },
    "suggestion": "user@gmail.com"
  }
}

For a disposable address:

{
  "data": {
    "valid": false,
    "email": "test@mailinator.com",
    "checks": {
      "syntax": true,
      "notBounced": true,
      "mxRecords": true,
      "notDisposable": false,
      "notRoleBased": true
    },
    "reason": "Disposable email address"
  }
}

The checks object tells you exactly which validation failed. You might accept role-based addresses (info@, support@) for transactional email but reject them for marketing -- the granular response lets you decide.

Where validation fits in your flow

There are two places it matters: when contacts enter your system, and when you send.

At signup. Validate the email before creating the contact. If the domain has no MX records or the address is disposable, reject it at the form. The user can fix the typo or use a real address.

const res = await fetch("https://pushmail.dev/api/v1/validate", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: userEmail }),
});

const { data } = await res.json();

if (!data.valid) {
  return { error: `Invalid email: ${data.reason}` };
}

if (data.suggestion) {
  return { warning: `Did you mean ${data.suggestion}?` };
}

This is the highest-leverage point for validation. You stop bad addresses from ever entering your list.

At send time. PushMail also validates internally before every send in the queue consumer. Even if an address was valid when the contact was created, it might have bounced since then. The consumer checks the suppression list and MX records before calling SendGrid. If the address is now invalid, the send is skipped and your credits aren't deducted.

How bounce suppression works

When an email hard-bounces through SendGrid, the bounce event hits PushMail's webhook handler. The address is immediately added to the suppression list in KV at suppress:{email}. From that point on, any validation returns notBounced: false, and any send attempt is blocked before reaching SendGrid.

This is shared across all senders on the platform. If user@defunct-startup.com bounced for one customer, every other customer benefits. Spam complaints work the same way -- if a recipient marks your email as spam, the address is suppressed for future sends.

Role-based address detection

Addresses like admin@, support@, postmaster@, webmaster@, and noreply@ are flagged in the notRoleBased check. PushMail checks against over 40 common role-based prefixes.

These are not rejected outright -- they return valid: true with notRoleBased: false. Some role-based addresses are legitimate subscribers. But they're more likely to be shared mailboxes that generate complaints, so you might want to handle them differently.

Cost comparison

Validation is free on every PushMail account. No per-validation fee, no Pro plan requirement. The endpoint is rate-limited to 100 requests per minute per organization, which covers any reasonable use case.

Dedicated validation services charge per check:

ServiceCost per validation50,000 validations/month
ZeroBounce$0.008$400
NeverBounce$0.008$400
BriteVerify$0.01$500
Kickbox$0.01$500
PushMail$0$0

Those dedicated services offer features PushMail doesn't -- SMTP mailbox pinging, catch-all detection, inbox provider identification. If you need that level of verification, they're worth the cost. But for the majority of use cases -- blocking bad addresses before they tank your bounce rate -- syntax checks, MX lookups, disposable detection, and suppression list coverage handle it.

What this prevents

For a sender doing 50,000 emails per month with a typical 2-4% invalid rate:

  • 1,000-2,000 bounces prevented per month
  • Bounce rate stays under 0.5% instead of climbing toward ISP thresholds
  • No account suspensions from spike events after importing a stale CSV
  • Credits aren't wasted on sends that would never deliver

Your sender reputation stays clean. Your emails keep landing in inboxes. And you didn't have to integrate a separate service or pay per check to make it happen.