Skip to main content

Email Address Syntax Validator API (RFC 5322, RFC 5321, HTML)

REAL DATA

Validate email syntax against RFC 5322, RFC 5321 transport limits or the HTML input profile — local/domain split and the exact rule that failed. No DNS, ever.

Authoritative reference data or standards computation

Base URL
/api/email-syntax
Capabilities
1 route
Last updated
July 29, 2026
Data source

GET /api/email-syntax/validate

Splits the address, classifies both halves and reports per-profile conformance with the exact failing production. Nothing here touches DNS or SMTP.

Live requestRuns against the public API
No key required
GET/api/email-syntax/validate?email=user%2Btag%40example.com

Request parameters

The address to check, up to 320 characters (the theoretical 64 + 1 + 255 maximum). Whitespace is never trimmed, because leading and trailing whitespace is itself a syntax question — which also means a literal "+" must be percent-encoded as %2B (user%2Btag@example.com), since a query string decodes "+" as a space and the space is what would be checked.

Evaluate only this profile. Omit to evaluate all three and compare them.

Permit quoted-string local parts such as "john..doe"@example.org in the two RFC profiles. The HTML profile never allows them.

Permit bracketed address literals such as [192.0.2.1] and [IPv6:2001:db8::1] in the two RFC profiles. The HTML profile never allows them.

Apply the RFC 6531/RFC 6532 UTF8-non-ascii extensions to the two RFC profiles. The HTML profile is ASCII-only regardless.

Permit obs-local-part and obs-domain in the RFC 5322 profile. RFC 5321 and the HTML profile have no obsolete forms.

Cross-check the rightmost DNS label against the bundled IANA root zone snapshot and report it in tldDelegated. It never changes a profile verdict.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/email-syntax/validate?email=user%2Btag%40example.com"
const res = await fetch("https://randomapi.dev/api/email-syntax/validate?email=user%2Btag%40example.com");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/email-syntax/validate?email=user%2Btag%40example.com").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/email-syntax/validate?email=user%2Btag%40example.com"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 7
email string required

The address to check, up to 320 characters (the theoretical 64 + 1 + 255 maximum). Whitespace is never trimmed, because leading and trailing whitespace is itself a syntax question — which also means a literal "+" must be percent-encoded as %2B (user%2Btag@example.com), since a query string decodes "+" as a space and the space is what would be checked.

allowed: 1 – 320
example: email=user+tag@example.com
profile enum

Evaluate only this profile. Omit to evaluate all three and compare them.

allowed: rfc5322 | rfc5321 | html
example: profile=html
allowQuoted boolean

Permit quoted-string local parts such as "john..doe"@example.org in the two RFC profiles. The HTML profile never allows them.

default: true
example: allowQuoted=false
allowDomainLiteral boolean

Permit bracketed address literals such as [192.0.2.1] and [IPv6:2001:db8::1] in the two RFC profiles. The HTML profile never allows them.

default: true
example: allowDomainLiteral=false
allowUnicode boolean

Apply the RFC 6531/RFC 6532 UTF8-non-ascii extensions to the two RFC profiles. The HTML profile is ASCII-only regardless.

default: false
example: allowUnicode=true
allowObsolete boolean

Permit obs-local-part and obs-domain in the RFC 5322 profile. RFC 5321 and the HTML profile have no obsolete forms.

default: false
example: allowObsolete=true
requireDelegatedTld boolean

Cross-check the rightmost DNS label against the bundled IANA root zone snapshot and report it in tldDelegated. It never changes a profile verdict.

default: false
example: requireDelegatedTld=true
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=input,normalized
exclude list

Return all fields except these (comma-separated).

example: exclude=warnings
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 12
input string

The address exactly as supplied, byte for byte — surrounding whitespace is never trimmed.

example: user+tag@example.com

normalized string nullable

Address with comments and folding whitespace removed and the ASCII letters of a DNS domain lower-cased; whitespace inside a quoted string is preserved byte for byte, and the local part is never case-folded (RFC 5321 section 2.4). Null when either half does not parse.

example: user+tag@example.com

localPart string nullable

Raw text before the separator: the rightmost "@" outside a quoted string and outside a comment whose two halves both parse, or the rightmost such "@" when none of them do. Null when there is no separator.

example: user+tag

localPartForm string nullable

dot-atom | quoted-string | obsolete, in RFC 5322's own alternation order. `obsolete` means obs-local-part and nothing stronger. Null when the local part does not parse.

example: dot-atom

domain string nullable

Raw text after the separating "@". Null when no separator was found.

example: example.com

domainForm string nullable

dns | domain-literal-ipv4 | domain-literal-ipv6 | domain-literal-other. The last value covers bracketed literals that are not an IP address under the strict reading documented below — a dotted quad with leading zeroes and an IPv6 address missing its `IPv6:` tag both land there. Null when the domain does not parse.

example: dns

isIdn boolean

True when the DNS domain contains at least one non-ASCII character (a U-label).

example: false

punycodeDomain string nullable

IDNA A-label form of a Unicode domain, produced by the same host parser browsers use, and populated whether or not allowUnicode is set. Null when the domain is already ASCII, is an address literal, or cannot be converted.

example: xn--e1afmkfd.xn--p1ai

lengths object

{ localOctets, domainOctets, totalOctets } — UTF-8 octets of the comment-stripped halves, and localOctets + 1 + domainOctets. When the address has no separator, both halves are 0 and totalOctets is the octet length of the whole input.

example: {"localOctets":8,"domainOctets":11,"totalOctets":20}

profiles object

One entry per evaluated profile ({ conforms, failingRule, reference, detail }); failingRule names the ABNF production or documented limit, reference names the clause. Contains all three keys unless `profile` narrows it.

example: {"rfc5322":{"conforms":true,"failingRule":null,"reference":null,"detail":null},"rfc5321":{"conforms":true,"failingRule":null,"reference":null,"detail":null},"html":{"conforms":true,"failingRule":null,"reference":null,"detail":null}}

tldDelegated boolean nullable

Whether the rightmost DNS label is in the bundled IANA root zone snapshot. Null unless requireDelegatedTld=true and a DNS domain is present. It never changes a profile verdict and says nothing about the domain being registered.

example: true

warnings string[]

Address-specific advisories: obsolete syntax, quoted strings, address literals, comments, internationalization, an undelegated TLD, and profile disagreement. Empty for an unremarkable address.

example: []

Documented examples

Build-generated requests and complete responses
7
An ordinary address under all three profiles
GET /api/email-syntax/validate?email=user%2Btag%40example.com
{
  "data": {
    "input": "user+tag@example.com",
    "normalized": "user+tag@example.com",
    "localPart": "user+tag",
    "localPartForm": "dot-atom",
    "domain": "example.com",
    "domainForm": "dns",
    "isIdn": false,
    "punycodeDomain": null,
    "lengths": {
      "localOctets": 8,
      "domainOctets": 11,
      "totalOctets": 20
    },
    "profiles": {
      "rfc5322": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "rfc5321": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "html": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      }
    },
    "tldDelegated": null,
    "warnings": []
  },
  "meta": {
    "endpoint": "email-syntax",
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Consecutive dots: RFC 5322 says no, the browser says yes
GET /api/email-syntax/validate?email=john..doe%40example.com
{
  "data": {
    "input": "john..doe@example.com",
    "normalized": null,
    "localPart": "john..doe",
    "localPartForm": null,
    "domain": "example.com",
    "domainForm": "dns",
    "isIdn": false,
    "punycodeDomain": null,
    "lengths": {
      "localOctets": 9,
      "domainOctets": 11,
      "totalOctets": 21
    },
    "profiles": {
      "rfc5322": {
        "conforms": false,
        "failingRule": "dot-atom-text",
        "reference": "RFC 5322 section 3.2.3",
        "detail": "The local part 'john..doe' contains two consecutive dots; dot-atom-text = 1*atext *(\".\" 1*atext) requires at least one atext character on both sides of every dot."
      },
      "rfc5321": {
        "conforms": false,
        "failingRule": "Local-part",
        "reference": "RFC 5321 section 4.1.2",
        "detail": "The local part is not a Dot-string or a Quoted-string; Dot-string = Atom *(\".\" Atom) requires at least one atext character on both sides of every dot, and a Quoted-string must be fully enclosed in double quotes."
      },
      "html": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      }
    },
    "tldDelegated": null,
    "warnings": [
      "The three profiles disagree about this address (rfc5322: fails, rfc5321: fails, html: conforms). This is exactly how a browser and a mail server end up disagreeing about the same address."
    ]
  },
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Quoted local part accepted by RFC 5322 only
GET /api/email-syntax/validate?email=%22john..doe%22%40example.org
{
  "data": {
    "input": "\"john..doe\"@example.org",
    "normalized": "\"john..doe\"@example.org",
    "localPart": "\"john..doe\"",
    "localPartForm": "quoted-string",
    "domain": "example.org",
    "domainForm": "dns",
    "isIdn": false,
    "punycodeDomain": null,
    "lengths": {
      "localOctets": 11,
      "domainOctets": 11,
      "totalOctets": 23
    },
    "profiles": {
      "rfc5322": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "rfc5321": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "html": {
        "conforms": false,
        "failingRule": "atext",
        "reference": "WHATWG HTML — valid e-mail address",
        "detail": "The local part '\"john..doe\"' contains '\"' (U+0022); the HTML profile allows only atext characters and \".\" before the \"@\" — no quoted strings, comments or non-ASCII."
      }
    },
    "tldDelegated": null,
    "warnings": [
      "The local part uses a quoted string. RFC 5322 and RFC 5321 both permit it, the HTML input profile does not, and many mail providers reject it at signup.",
      "The three profiles disagree about this address (rfc5322: conforms, rfc5321: conforms, html: fails). This is exactly how a browser and a mail server end up disagreeing about the same address."
    ]
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
IPv4 address literal
GET /api/email-syntax/validate?email=user%40%5B192.0.2.1%5D
{
  "data": {
    "input": "user@[192.0.2.1]",
    "normalized": "user@[192.0.2.1]",
    "localPart": "user",
    "localPartForm": "dot-atom",
    "domain": "[192.0.2.1]",
    "domainForm": "domain-literal-ipv4",
    "isIdn": false,
    "punycodeDomain": null,
    "lengths": {
      "localOctets": 4,
      "domainOctets": 11,
      "totalOctets": 16
    },
    "profiles": {
      "rfc5322": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "rfc5321": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "html": {
        "conforms": false,
        "failingRule": "label",
        "reference": "WHATWG HTML — valid e-mail address",
        "detail": "The domain '[192.0.2.1]' contains '[' (U+005B); label = let-dig [ [ ldh-str ] let-dig ] allows only ASCII letters, digits and interior hyphens — brackets, underscores, whitespace and non-ASCII are all rejected."
      }
    },
    "tldDelegated": null,
    "warnings": [
      "The domain is an address literal rather than a domain name. RFC 5321 section 2.3.4 says hosts SHOULD NOT be identified by numerical addresses, and most public mail systems refuse them.",
      "The three profiles disagree about this address (rfc5322: conforms, rfc5321: conforms, html: fails). This is exactly how a browser and a mail server end up disagreeing about the same address."
    ]
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Internationalized address with SMTPUTF8 enabled
GET /api/email-syntax/validate?email=%D0%B8%D0%B2%D0%B0%D0%BD%40%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80.%D1%80%D1%84&allowUnicode=true
{
  "data": {
    "input": "иван@пример.рф",
    "normalized": "иван@пример.рф",
    "localPart": "иван",
    "localPartForm": "dot-atom",
    "domain": "пример.рф",
    "domainForm": "dns",
    "isIdn": true,
    "punycodeDomain": "xn--e1afmkfd.xn--p1ai",
    "lengths": {
      "localOctets": 8,
      "domainOctets": 17,
      "totalOctets": 26
    },
    "profiles": {
      "rfc5322": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "rfc5321": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "html": {
        "conforms": false,
        "failingRule": "atext",
        "reference": "WHATWG HTML — valid e-mail address",
        "detail": "The local part 'иван' contains 'и' (U+0438); the HTML profile allows only atext characters and \".\" before the \"@\" — no quoted strings, comments or non-ASCII."
      }
    },
    "tldDelegated": null,
    "warnings": [
      "The domain is an internationalized U-label domain. Delivery needs SMTPUTF8 (RFC 6531) or the A-label form 'xn--e1afmkfd.xn--p1ai', and the HTML profile rejects it outright.",
      "The local part contains non-ASCII characters, so every hop from submission to final delivery must support SMTPUTF8 (RFC 6531); there is no punycode equivalent for a local part.",
      "The three profiles disagree about this address (rfc5322: conforms, rfc5321: conforms, html: fails). This is exactly how a browser and a mail server end up disagreeing about the same address.""generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Valid syntax, undelegated top-level domain
GET /api/email-syntax/validate?email=a%40b.invalidtld&requireDelegatedTld=true
{
  "data": {
    "input": "a@b.invalidtld",
    "normalized": "a@b.invalidtld",
    "localPart": "a",
    "localPartForm": "dot-atom",
    "domain": "b.invalidtld",
    "domainForm": "dns",
    "isIdn": false,
    "punycodeDomain": null,
    "lengths": {
      "localOctets": 1,
      "domainOctets": 12,
      "totalOctets": 14
    },
    "profiles": {
      "rfc5322": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "rfc5321": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      },
      "html": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      }
    },
    "tldDelegated": false,
    "warnings": [
      "The top-level label 'invalidtld' is not in the bundled IANA root zone snapshot (version 2026062302, 2026-06-24), so this domain cannot resolve on the public Internet. Syntax is unaffected."
    ]
  },
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Only what an HTML form would accept
GET /api/email-syntax/validate?email=user%40example.com&profile=html
{
  "data": {
    "input": "user@example.com",
    "normalized": "user@example.com",
    "localPart": "user",
    "localPartForm": "dot-atom",
    "domain": "example.com",
    "domainForm": "dns",
    "isIdn": false,
    "punycodeDomain": null,
    "lengths": {
      "localOctets": 4,
      "domainOctets": 11,
      "totalOctets": 16
    },
    "profiles": {
      "html": {
        "conforms": true,
        "failingRule": null,
        "reference": null,
        "detail": null
      }
    },
    "tldDelegated": null,
    "warnings": []
  },
  "meta": {
    "endpoint": "email-syntax",
    "route": "/validate",
    "params": {
      "email": "user@example.com",
      "profile": "html",
      "allowQuoted": true,
      "allowDomainLiteral": true,
      "allowUnicode": false,
      "allowObsolete": false,
      "requireDelegatedTld": false
    },
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}

About this API

Coverage & behavior

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.)

Use it for

  • Explain to a frontend team why type=email accepts an address the SMTP layer will reject
  • Enforce the RFC 5321 64/255/254-octet limits before storing a signup address
  • Decide deliberately whether your own validator accepts quoted-string, domain-literal or obsolete forms
  • Flag signup domains whose top-level label is absent from the IANA root zone

Frequently asked questions

Why does my browser accept an email address that my backend rejects?

Because they implement different grammars. <input type="email"> uses the WHATWG HTML ABNF, which allows .. in the local part but forbids quoted strings, comments and domain literals; RFC 5322 does the opposite. This endpoint returns both verdicts side by side in profiles.

Is john..doe@example.com a valid email address?

It depends on the profile, which is the whole point. RFC 5322 and RFC 5321 reject it because dot-atom-text = 1*atext *("." 1*atext) requires at least one character between dots, but the HTML profile accepts it because its production is 1*( atext / "." ).

What is the maximum length of an email address?

64 octets for the local part and 255 for the domain (RFC 5321 sections 4.5.3.1.1 and 4.5.3.1.2), and 254 octets for the whole address — derived from the 256-octet Path limit in section 4.5.3.1.3 minus its two angle brackets. All three are reported in lengths, counted in UTF-8 octets.

Does this endpoint check whether the mailbox actually exists?

No. There is no MX lookup, no SMTP connection, no mailbox probe and no disposable-domain list anywhere in it. A syntactically valid address can still bounce, and a rejected one can be perfectly deliverable.

How do I check that the top-level domain really exists?

Set requireDelegatedTld=true. The rightmost label is compared against the bundled IANA root zone snapshot and reported in tldDelegated, separately from syntax — an undelegated TLD never changes a profile's conforms value, and a delegated one does not mean the domain is registered.

Can it validate internationalized (SMTPUTF8) addresses?

Yes, with allowUnicode=true, which applies the RFC 6531 and RFC 6532 UTF8-non-ascii extensions to the two RFC profiles. The A-label is reported in punycodeDomain whether or not the flag is set, and the HTML profile rejects non-ASCII regardless, because its ABNF was never extended.

Standards & references