Python Validate Email: Check Email Addresses via SMTP
The 30-line version of email validation is easy. This guide gives you that script — then everything the tutorials skip, learned from running an SMTP validation API in production.
How SMTP validation works
Validating an email without sending anything relies on the SMTP protocol itself. You look up the domain's MX record (which server receives its mail), connect to that server, and walk through the start of a real delivery: HELO, MAIL FROM, then RCPT TO with the address you're testing. The server's answer to RCPT tells you whether the mailbox exists — and then you disconnect without sending a message.
The Python code
You'll need dnspython (pip install dnspython); smtplib is in the standard library.
import dns.resolver
import smtplib
resolver = dns.resolver.Resolver()
resolver.timeout = 3
resolver.lifetime = 5
def get_mx_record(domain):
try:
mx_records = resolver.resolve(domain, "MX")
if mx_records:
return str(mx_records[0].exchange).strip(".")
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer,
dns.resolver.NoNameservers, dns.resolver.LifetimeTimeout):
return None
def smtp_check(email, mx_record, helo_host, mail_from):
server = None
try:
server = smtplib.SMTP(timeout=10)
server.connect(mx_record)
server.helo(helo_host)
server.mail(mail_from)
code, _ = server.rcpt(email)
return code
except (smtplib.SMTPException, OSError):
return None
finally:
if server is not None:
try:
server.quit()
except Exception:
pass
email = "someone@example.com"
domain = email.split("@", 1)[1]
mx = get_mx_record(domain)
if mx is None:
print("invalid: domain has no MX record")
else:
code = smtp_check(email, mx, "mail.yourdomain.com", "check@yourdomain.com")
if code == 250:
print("valid: server accepts the mailbox")
elif code in (550, 551):
print("invalid: server rejects the mailbox")
else:
print(f"unknown: response {code}")Reading the server's answers
| SMTP response | What it means |
|---|---|
| 250 | Mailbox accepted — the address is deliverable (unless the domain is catch-all, see below) |
| 550 / 551 | Mailbox rejected — the address doesn't exist |
| 4xx (e.g. 450, 451) | Temporary failure — often greylisting; retry after a delay before judging |
| No response / timeout | Unknown — server offline, connection blocked, or your IP filtered |
What the simple script doesn't tell you
The code above is correct — and it will still mislead you at scale. These are the six lessons that separate a tutorial script from a production validator.
Catch-all domains lie to you
Some servers answer 250 to every address, real or fake. Before trusting a 250, probe the domain with a randomly generated address — if that gets accepted too, the domain is catch-all and the result is 'risky', not 'valid'.
Greylisting punishes the impatient
Many servers deliberately answer 4xx to first-time senders and only accept the retry. If you treat a 450 as invalid, you'll throw away real addresses. Production systems queue and retry with delays.
Port 25 is blocked almost everywhere
Residential connections and most cloud providers (AWS, GCP, Azure) block outbound port 25. You need a VPS from a provider that opens it, and even then, deliverability of your checks depends on the next point.
Your checker needs mail-server-grade DNS
Receiving servers inspect who's asking. A checker whose IP has no PTR (reverse DNS) matching its HELO hostname gets filtered or fed misleading answers. Set up PTR, SPF and a real hostname before trusting results.
Rate-limit per mail server, not globally
Hammering one MX with rapid RCPT checks gets you throttled or blacklisted. Space out checks per receiving server and reuse connections politely instead of reconnecting for every address.
Long-running checkers leak resources
A script that validates 50 emails hides bugs that a service validating thousands per day exposes: unclosed SMTP connections accumulate file descriptors until DNS itself starts failing with 'Too many open files'. Close every connection deterministically — we learned this one in production.
Frequently Asked Questions
Skip the infrastructure, keep the accuracy
Everything above — catch-all probes, retries, IP reputation, rate limits — runs behind MatchKraft's validation API as a single HTTP call. Free plan: 100 validations per month.
Try the validation API for free