© 2026 Email Awesome. All rights reserved.
Claim & start

Python can validate email syntax and examine a domain's mail-routing records. Those checks do not prove that a particular mailbox exists, belongs to the person entering it, or will accept the next message. This guide builds two small functions with explicit outcomes so an application can preserve that uncertainty.
A signup form needs fast, actionable feedback. An import job can allow more time for domain checks. A login form should not lock an existing user out merely because a fresh DNS lookup fails. Keep those workflows separate instead of running the same network test on every request.
Use a maintained parser for syntax, a bounded resolver for domain evidence, and a separate confirmation flow when control of the address matters. Store the time and reason for each result. Do not collapse all three into a single verified=true flag.
Install the dependency with python -m pip install email-validator. The following function deliberately performs no network lookup. It accepts text, removes surrounding whitespace and returns the library's normalized address.
from email_validator import validate_email, EmailNotValidError
def check_syntax(value):
if not isinstance(value, str):
return {"syntax_ok": False, "reason": "Expected text"}
try:
result = validate_email(value.strip(), check_deliverability=False)
except EmailNotValidError as error:
return {"syntax_ok": False, "reason": str(error)}
return {"syntax_ok": True, "normalized": result.normalized,
"mailbox_checked": False}
assert check_syntax("alex+news@example.com")["syntax_ok"]
assert not check_syntax("alex@@example.com")["syntax_ok"]
assert not check_syntax(None)["syntax_ok"]The assertions check formatting behavior only. The example.com addresses are test inputs, not claims about real recipients. Preserve the local part returned by the library; do not invent provider-specific rules that merge distinct customer identities.
The library supports DNS checks and a reusable caching resolver. Its routing logic can consider A/AAAA fallback when MX is absent; a missing MX record alone is not a universal rejection rule. See the maintainer's documentation for supported behavior and options.
from email_validator import validate_email, caching_resolver
from email_validator import EmailSyntaxError, EmailUndeliverableError
resolver = caching_resolver(timeout=3)
def check_domain(value):
if not isinstance(value, str):
return {"state": "syntax_error"}
try:
result = validate_email(value.strip(), check_deliverability=True,
dns_resolver=resolver)
except EmailSyntaxError:
return {"state": "syntax_error"}
except EmailUndeliverableError as error:
return {"state": "domain_check_failed", "reason": str(error)}
# A temporary DNS problem may return no MX evidence without raising.
return {"state": "domain_route_found" if getattr(result, "mx", None)
else "domain_check_inconclusive",
"normalized": result.normalized, "mailbox_checked": False}The three-second resolver setting is an example DNS budget, not a guaranteed end-to-end response time. A background job also needs its own deadline, concurrency limit and bounded retries. Keep an inconclusive result pending rather than treating a timeout as a nonexistent mailbox.
domain_route_found is an application label in this example. It is not an Email Awesome verification result. The function makes no SMTP connection and sets mailbox_checked to false even after finding a route.
An SMTP acceptance response can still be inconclusive on an accept-all domain or precede a later delivery failure. A rejection can reflect policy rather than a nonexistent recipient. Do not convert an isolated SMTP response into proof of ownership, consent or inbox placement.
For the policy behind these distinctions, read what syntax checks cannot establish. For the same separation in a JavaScript backend, use the Node.js validation example.
Explore the related Email Awesome workflow and review its current setup before implementing it.
Check the most Frequently Asked Questions
What is the best Python library for email validation?
The email-validator package supports syntax validation, normalization and optional domain checks. Choose according to the application's accepted-address policy and network budget. Its DNS checks do not verify a specific mailbox or establish ownership.
How do I validate email syntax in Python using Regex?
Use re.fullmatch for a complete-string pattern check, but document the pattern's accepted forms and counterexamples. A short regex can still accept malformed addresses or exclude legitimate ones. For application syntax handling, consider a maintained parser such as email-validator; neither approach proves mailbox existence.
How does Python check if an email domain exists?
A DNS lookup can distinguish domain-level conditions and inspect mail routing. MX records describe routing rather than a specific mailbox; SMTP also defines fallback behavior when MX is absent. Preserve temporary DNS errors as inconclusive instead of marking the address invalid automatically.
Can Python verify an email without sending a message?
Python can check syntax and DNS without sending a message. SMTP checks can add server evidence, but acceptance does not conclusively prove mailbox existence and rejection can reflect policy. When address control matters, use a separate requested confirmation flow.
Why is my Python SMTP script failing on Yahoo or Gmail?
Connection restrictions, routing problems, temporary deferrals and provider policies can make SMTP checks fail or remain inconclusive. Inspect the error and use bounded retry rules. A verification API is also subject to limitations and does not guarantee that these restrictions can be bypassed.