Every email validation conversation eventually arrives at the regex. The honest version of the answer is narrow: a regex decides whether a string parses as an address, and nothing else. A signup flow needs four answers — does it parse, does the domain accept mail, is it what the user meant to type, and do you want it in your database. The regex covers the first question, where it is also the cheapest tool. The other three require DNS lookups and curated datasets, and no amount of pattern cleverness substitutes for them.

What a regex can legitimately decide

RFC 5322 defines the address grammar, and the full grammar is famously unimplementable in a practical pattern: quoted local parts, nested comments, folding whitespace. Nobody's signup form needs to accept "john..doe"@example.com, and nobody's regex does. What production systems check is RFC 5322 as practised — a pragmatic subset that accepts every address a real user types and rejects the mangled ones:

/^[^\s@]+@[^\s@]+\.[^\s@]+$/

This catches the mechanical failures: not-an-email, two@@gmail.com, spa ce@gmail.com. nobounce's own first engine layer is exactly this kind of practical syntax check — one of several reasons it rejects user@nodots, a dotless domain, before ever touching DNS:

{
  "verdict": "undeliverable",
  "reason": "syntax_invalid",
  "checked": { "syntax": true, "mx": false, "typo": true, "disposable": true }
}

Note checked.mx: false: a syntax rejection costs no DNS query, and the response says so explicitly.

If you keep a regex at all, keep it here — a client-side pre-check that prevents an obviously malformed submit from wasting a round trip. Do not chase the grammar. The edge cases the regex gets wrong are exactly the ones that don't matter, and the ones it gets right are the ones the API's first layer already decides.

The three questions a regex structurally cannot answer

Does the domain accept mail? user@gmail.cm and user@gmail.com both match any sane regex. One is an NXDOMAIN and a dead signup. The answer lives in DNS — MX records per RFC 5321, the null-MX convention of RFC 7505, and the difference between a domain that does not exist and a resolver that failed. This is a whole engineering topic on its own, covered in Why MX Validation Misses Typo Domains.

Is it what the user meant? A transposed gmial parses perfectly. This is the question regex-based validation silently fails, and it is the one that costs signups: rejecting user@gmai.com loses a real user, and accepting it hands the address to a typosquat operator. These are live results from the API:

input regex says nobounce says
user@gmail.cm accepts undeliverable, domain_not_found, suggests user@gmail.com
user@gmai.com accepts undeliverable, typosquat_mx, suggests user@gmail.com
user@yahooo.com accepts risky, likely_typo, suggests user@yahoo.com
user@gmial.com accepts unknown, dns_error — fails open, still suggests

The correction runs before DNS because the typo'd domains frequently resolve anyway; gmai.com publishes MX records pointing at mail.h-email.net. A DNS-only pipeline accepts it. And when the resolver itself fails — gmial.com is a live SERVFAIL — the API reports unknown with checked.mx: false instead of inventing a verdict, a split detailed in SERVFAIL vs NXDOMAIN in Email Validation. A regex has no equivalent of either answer; its output space is simply accepts/rejects.

Do you want it? user@mailinator.com parses. admin@gmail.com parses. Whether disposable domains and role accounts belong in your database is a policy question with a policy-shaped answer — verdict risky with the reason naming which case it is — not a pattern question.

Where the regex belongs in the pipeline

Two layers, each doing the job it is suited for:

  1. Client-side regex — reject foo and foo@ before submit. Cheap, instant, offline.
  2. Server-side API call on submit — the authority. Live DNS, typo correction with a suggestion, and the policy verdicts, all fail-open.
curl -X POST https://nobounce.dev/v1/check \
  -H "Authorization: Bearer $NOBOUNCE_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"email":"user@gmai.com"}'

The key travels in the Authorization header, never in a URL. Live checks are paid, from $1/mo — there is no free tier — while POST /demo/check resolves a frozen fixture corpus with no key so you can see every verdict and reason shape before paying. It cannot validate arbitrary live addresses.

The integration rules that make the API call worth having: any non-null suggestion, including a risky + likely_typo verdict, means offer the correction rather than reject; and the call itself must fail open so validation never becomes a hard dependency of signup. Those mechanics are covered in Suggest the Correction Instead of Rejecting the Signup.

The honest limit on both sides

A regex cannot tell you the domain exists. It also cannot tell you the mailbox exists — but neither can a DNS check. Confirming a specific mailbox would need SMTP RCPT TO probing, which nobounce never performs: it demands IP reputation to be accurate and catch-all domains at the large providers make the signal weak. That work is permanently out of scope, and any service advertising certainty about mailbox existence from pattern matching or DNS alone is overselling. Confidence is a number, not a promise, and unknown is a verdict, not an error.

So keep the regex. Give it the one job it can do — catching mechanical garbage at the keystroke layer — and hand the other three questions to something that can actually answer them.

For the full key-acquisition, verdict-handling, and verification procedure, give your coding agent /integrate.md.