MatchKraft Logo
MatchKraft
Email VerifierEmail FinderDomain ScanPricingTestimonialsServices
Log inSign up for free
Email VerificationJuly 10, 2026

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.

Validate Emails in Python via SMTP — MatchKraft blog post cover

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 responseWhat it means
250Mailbox accepted — the address is deliverable (unless the domain is catch-all, see below)
550 / 551Mailbox rejected — the address doesn't exist
4xx (e.g. 450, 451)Temporary failure — often greylisting; retry after a delay before judging
No response / timeoutUnknown — 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

Product

  • Email Verifier
  • Email Finder
  • Domain Scan
  • Pricing
  • Services

Resources

  • Testimonials
  • API Reference
  • Blog
  • Compare
  • Industries
  • Data Sources

Company

  • About
  • Contact

Legal

  • Privacy
  • Terms
MatchKraft

Built for deliverability and scale

© 2026 MatchKraft. All rights reserved.