Syntax only. This endpoint performs no MX lookup, no SMTP probe, no mailbox-existence check and no disposable-domain detection, and it makes no deliverability claim of any kind. A syntactically perfect address can still bounce, and an address your regex rejects can be perfectly deliverable. Never paste other people's addresses into a URL: query strings end up in browser history, proxy logs and Referer headers no matter whose server they hit.
/validate evaluates one address against three explicit profiles at once and names the ABNF production or documented limit that rejected it in each:
| Profile |
Grammar |
What makes it different |
rfc5322 |
RFC 5322 section 3.4.1 addr-spec |
dot-atom, quoted-string, nested comments (CFWS) and domain-literals; no length limits at all |
rfc5321 |
RFC 5321 section 4.1.2 Mailbox plus the section 4.5.3.1 limits |
no comments anywhere, LDH domain labels only, and the 64/255/254-octet limits |
html |
WHATWG HTML valid e-mail address |
what <input type="email"> actually enforces: no quoted strings, no comments, no literals, no non-ASCII — but john..doe@example.com passes |
That last row is the reason a frontend and a backend disagree about the same address. The HTML production is 1*( atext / "." ) "@" label *( "." label ), so doubled, leading and trailing dots are all fine in the local part and there is no 64-octet cap — while "john..doe"@example.org, which RFC 5322 accepts, fails immediately because " is not an atext character. The HTML specification calls itself a willful violation of RFC 5322, and this endpoint implements the violation rather than pretending it away.
Length limits. RFC 5321 section 4.5.3.1.1 caps a local part at 64 octets and section 4.5.3.1.2 caps a domain at 255. The familiar 254-octet total is derived: section 4.5.3.1.3 limits a reverse-path or forward-path to 256 octets including its punctuation, and Path = "<" [ A-d-l ":" ] Mailbox ">" spends two of them on the angle brackets (the optional source route is the part in the middle, so the brackets are all that is left when it is absent). RFC 5321 never prints "254" itself — the verified erratum 1690 to RFC 3696 reaches the same conclusion, that "the upper limit on address lengths should normally be considered to be 254". All three limits are counted in octets of UTF-8, which is why a single internationalized character can cost four.
Obsolete syntax gets its own flag. obs-local-part = word *("." word) and obs-domain = atom *("." atom) are valid RFC 5322 and rejected by most real mail systems, so collapsing them into a single boolean would be dishonest. allowObsolete (default false) controls them; localPartForm reports obsolete independently of any verdict.
Internationalization. RFC 5322 and RFC 5321 are ASCII-only as written. allowUnicode=true applies both extensions to those two profiles: RFC 6532 section 3.2 adds UTF8-non-ascii to VCHAR, ctext, atext, qtext, text and dtext — the VCHAR line is what makes "a\é" a legal quoted-pair — and RFC 6531 section 3.3 adds it to atext and qtextSMTP and extends sub-domain =/ U-label. RFC 6531 pointedly does not extend quoted-pairSMTP = %d92 %d32-126, so "a\é"@example.com conforms under RFC 5322 and still fails under RFC 5321 with failingRule: "quoted-pairSMTP" whatever allowUnicode is set to; that asymmetry is real and this endpoint names it rather than smoothing it over. The HTML profile has no extension at all and rejects non-ASCII whatever you set.
U-labels are validated by converting them with the same host parser browsers use, and the A-label is returned in punycodeDomain. That is IDNA lookup processing, not registration processing: a label that only becomes valid after mapping — an ignorable character, an uppercase letter, a compatibility form — is accepted here exactly as a browser would accept it, and the mapped result is what punycodeDomain shows. A registry may still refuse to register it. The 63-octet DNS label limit is applied to the A-label, because that is the form that goes on the wire: RFC 5890 section 2.3.2.1 says the more restrictive of the two forms governs and explicitly exempts the U-label's own octet count. So a 90-octet U-label whose A-label is 36 octets passes, while a 58-octet U-label whose A-label is 64 octets fails with sub-domain — the same rule, and the same failure, the ASCII path applies.
Delegation is not syntax. requireDelegatedTld=true cross-checks the rightmost label against the bundled IANA root zone snapshot (version 2026062302, 2026-06-24) and fills in tldDelegated. It never flips conforms, and tldDelegated: true means only that the label exists in the root zone on our pinned date — not that the domain is registered, resolvable or accepting mail.
Scope limits, stated plainly. Line folding is out of scope: an address here is a single unfolded line, and a CR or LF fails every profile. The obsolete control-character productions (obs-qtext, obs-ctext, obs-dtext) are never accepted; allowObsolete covers obs-local-part and obs-domain only. Address literals use the strict, universally implemented reading of RFC 5321's deliberately loose ABNF: IPv4 octets carry no leading zeroes (Snum = 1*3DIGIT admits 01 as printed — the detail says so and names the deviation rather than pretending the ABNF rejects it) and a compressed IPv6 literal holds at most eight groups. Standardized address-literal tags are checked for shape only — Standardized-tag = Ldh-str, which may begin with a digit or a hyphen and must end alphanumeric — and never against IANA's tag registry. The one deliberate exception: bracketed content that is itself a valid IPv6 address is reported as a missing IPv6: tag, because reading [2001:db8::1] as a General-address-literal tagged 2001 would dress up the exact mistake the sender made. A query string decodes + as a space, so send user%2Btag@example.com; when a space is what broke the parse and a + would have fixed it, that is called out in meta.warnings. The only email values that produce a 400 are an empty one and one over 320 characters; every other address, however malformed, comes back as a structured record. (An out-of-range value for one of the flag parameters is still an ordinary parameter-level 400.)