DNS Lookup Python: Diagnose Email Deliverability 2026

Diagnose email deliverability with dns lookup python. Learn socket, dnspython, and check SPF, DKIM, & DMARC records to ensure optimal email health.

Published on

Updated

DNS Lookup Python: Diagnose Email Deliverability 2026
Do not index
Do not index
Transactional emails stop arriving. Trial users never get their verification link. Cold outreach reply rates fall off a cliff. Someone runs a quick DNS lookup in Python, sees that the domain resolves, and assumes DNS isn't the issue.
That's usually the wrong conclusion.
For email, DNS isn't just about turning a hostname into an IP address. It's where mailbox providers verify who is allowed to send, where inbound mail should go, whether policy exists for spoofed mail, and whether a domain looks maintained or sloppy. When those signals break, inbox placement suffers, sender reputation weakens, and spam filtering gets more aggressive.
A basic script can fetch a record. The hard part is knowing whether that record is valid, aligned, propagated, and safe for real sending.
Table of Contents

Why Your Python Script Is Not Enough for Deliverability

A team launches a product update email. Open rates are weak. Password reset emails arrive late or not at all. Support starts hearing from users who can't onboard. Someone on the engineering side writes a small Python script, checks one TXT record, and reports that DNS “looks fine.”
That's how deliverability problems stay unresolved.
notion image

The real problem is missed revenue and trust

Email failures don't stay technical for long. They turn into missed demos, broken onboarding, abandoned carts, lower reply volume, and a sender reputation problem that gets harder to reverse with every bad send.
Domains that implement SPF, DKIM, and DMARC together reach 95 to 98 percent inbox placement and are 2.7 times more likely to land in the inbox than unauthenticated domains, according to this deliverability summary on SPF, DKIM, and DMARC. That's why a DNS lookup in Python matters to the business. It's not a networking exercise. It's a gate in front of the inbox.

What basic tutorials usually miss

Most “DNS lookup Python” content teaches A or AAAA lookups. That's fine for basic networking. It doesn't answer email questions like:
  • Is the SPF record valid and within policy limits
  • Is DKIM published on the right selector
  • Is DMARC present and enforceable
  • Are MX records pointed correctly for inbound mail
  • Is the result stale because the script trusted the local resolver
Existing Python DNS content also misses the main failure pattern in email operations. This discussion around Python DNS lookups reflects a broader gap: most examples focus on IP resolution, while deliverability work needs programmatic validation of SPF, DKIM, and DMARC with remediation context. The same source set also notes that 87% of email delivery failures stem from authentication misconfigurations.
That's the difference between fetching data and diagnosing why mail goes to spam. The record itself is only the first clue.

The Simplest DNS Lookup with Python's Socket Library

Python ships with the socket module, and that makes it the first place many developers start. For simple hostname resolution, it works. For deliverability diagnostics, it runs out of road almost immediately.

The shortest possible example

A minimal A record style lookup looks like this:
import socket

domain = "example.com"
ip = socket.gethostbyname(domain)
print(ip)
If IPv4 and IPv6 details are needed, getaddrinfo() gives more structure:
import socket

domain = "example.com"
results = socket.getaddrinfo(domain, None)

for item in results:
    family, socktype, proto, canonname, sockaddr = item
    print(family, sockaddr)
This is useful when the question is narrow: “Does this hostname resolve?”
A quick answer section for developers:
Need
socket can help
Why it matters for mail
Resolve host to IP
Yes
Useful for basic host reachability
Check MX records
No
Needed for inbound mail routing
Check TXT records
No
Needed for SPF and DMARC
Check DKIM selector TXT
No
Needed for signature validation
Choose nameserver
No practical control for this use case
Needed for propagation debugging

Why socket hits a wall for email

The minute the task becomes deliverability-focused, socket stops being enough.
It can't directly ask for MX or TXT in the way a mail operator needs. That means it can't inspect SPF policy, DMARC policy, or DKIM public keys, and it can't tell whether mailbox providers are seeing the right authentication state.
That's why teams using only socket often get false confidence. The domain resolves, but the campaign still lands in spam because the problem lives in TXT policy, selector naming, alignment, or mail routing.
For any serious DNS lookup Python workflow tied to inbox placement, socket is the baseline, not the solution.

Using dnspython for Essential Email DNS Records

The practical standard for DNS work in Python is dnspython. It was first released in 2001 by A. R. Thalley and has grown into a full toolkit with support for almost all DNS record types, including SPF, DKIM, DMARC, and MX, as described in this overview of dnspython and Python DNS programming.

Why dnspython is the right starting point

For email diagnostics, record-type control matters. So does resolver control.
When a script uses dns.resolver.resolve(), it can ask for the exact records that mailbox providers use to judge a domain. It can also target a specific nameserver instead of trusting the local operating system cache. That matters when a team has just changed DNS and needs to know whether propagation is visible from outside the machine.
For production-grade TXT lookups tied to authentication, this guide on Python DNS lookups states that socket.getaddrinfo() can't query the specific record types needed for SPF and DMARC, while dnspython configured with an explicit nameserver reaches 99.8% success rates for resolving complex TXT records.
notion image

Practical record lookups that matter for mail

A reusable resolver is the cleanest starting point:
import dns.resolver

resolver = dns.resolver.Resolver()
resolver.nameservers = ["8.8.8.8"]
A records:
answers = resolver.resolve("example.com", "A")
for r in answers:
    print(r.to_text())
AAAA records:
answers = resolver.resolve("example.com", "AAAA")
for r in answers:
    print(r.to_text())
MX records:
answers = resolver.resolve("example.com", "MX")
for r in answers:
    print(r.preference, r.exchange.to_text())
TXT records for SPF or DMARC discovery:
answers = resolver.resolve("example.com", "TXT")
for r in answers:
    txt = "".join(part.decode() if isinstance(part, bytes) else part for part in r.strings)
    print(txt)
DKIM selector lookup follows the same pattern, but the hostname changes. A common form is:
selector_host = "selector1._domainkey.example.com"
answers = resolver.resolve(selector_host, "TXT")
for r in answers:
    txt = "".join(part.decode() if isinstance(part, bytes) else part for part in r.strings)
    print(txt)
For developers who want a clear primer on how these records differ, this guide to email DNS record types explained is worth keeping nearby.

Why explicit resolvers matter

Deliverability debugging often happens during DNS changes. A local laptop may still see an old record while mailbox providers see a new one, or the opposite. That's why relying on the system resolver causes confusion.
A practical checklist:
  • Use a custom resolver when checking SPF, DKIM, and DMARC.
  • Query TXT directly instead of assuming a provider dashboard published the record correctly.
  • Check MX separately because inbound routing failures can break replies and support flows.
  • Handle exceptions like NXDOMAIN, timeout, and no-answer cases explicitly.
The goal isn't just to retrieve records. It's to retrieve the records that affect inbox placement, using the same type of DNS visibility a real deliverability investigation requires.

Parsing DNS Records for Deliverability Insights

Fetching a TXT record is easy. Deciding whether it helps or hurts deliverability is the part most scripts never reach.
A raw TXT string is just text until the script understands policy structure, included mechanisms, failure behavior, and alignment implications.
notion image

Raw TXT output is not an answer

A script might print this SPF record:
v=spf1 include:_spf.provider-a.example include:_spf.provider-b.example ~all
That output alone doesn't answer key questions:
  • Is there more than one SPF record
  • Do the includes chain into too many DNS lookups
  • Is ~all intentional or a sign of unfinished policy
  • Is the sending provider represented
  • Has a stale vendor stayed in the record after migration
The same is true for DMARC. A record such as:
v=DMARC1; p=none; rua=mailto:reports@example.com
is syntactically useful, but operationally weak if the domain is ready for enforcement and still isn't using quarantine or reject.
A strong interpretation layer should return structured findings, not just strings. That's the gap between a developer utility and a deliverability diagnostic tool. This is also why a purpose-built email authentication checker is often faster than maintaining custom parsing logic in every project.

SPF logic that breaks real sending

The biggest SPF trap is lookup expansion.
The SPF standard allows 10 DNS lookups per evaluation, and exceeding that limit causes authentication failure, according to this explanation of SPF, DKIM, DMARC, and deliverability. This is one of the most common reasons mail fails even when the published record looks normal at first glance.
A lightweight parser can at least count obvious mechanisms:
def parse_spf(spf_record: str):
    parts = spf_record.split()
    mechanisms = {
        "include": [],
        "redirect": None,
        "a": 0,
        "mx": 0,
        "exists": 0,
        "all": None,
    }

    for part in parts:
        if part.startswith("include:"):
            mechanisms["include"].append(part.split("include:", 1)[1])
        elif part.startswith("redirect="):
            mechanisms["redirect"] = part.split("redirect=", 1)[1]
        elif part == "a" or part.startswith("a:"):
            mechanisms["a"] += 1
        elif part == "mx" or part.startswith("mx:"):
            mechanisms["mx"] += 1
        elif part.startswith("exists:"):
            mechanisms["exists"] += 1
        elif part.endswith("all"):
            mechanisms["all"] = part

    return mechanisms
That isn't a full SPF evaluator, but it helps flag risk.
Common SPF mistakes to avoid:
  • Multiple SPF records on the same domain.
  • Too many includes after stacking providers over time.
  • Using SPF as documentation instead of a strict sender authorization policy.
  • Forgetting old vendors that are still authorized to send.

Reading DMARC like an operator

DMARC tells receiving servers what to do when alignment fails.
A simplified parser can extract the tags:
def parse_dmarc(dmarc_record: str):
    tags = {}
    for item in dmarc_record.split(";"):
        item = item.strip()
        if "=" in item:
            key, value = item.split("=", 1)
            tags[key.strip()] = value.strip()
    return tags
What the core p= values mean:
DMARC policy
Meaning
Deliverability implication
p=none
Monitor only
Good for visibility, weak for enforcement
p=quarantine
Treat failing mail suspiciously
Stronger signal to mailbox providers
p=reject
Refuse failing mail
Strongest protection when alignment is correct
If DKIM is broken or misaligned, inbox placement can drop. This deliverability write-up on common failures notes that missing or broken DKIM signatures alone can reduce inbox placement by 10 to 15%, and that bounce rates above 2% are treated as a poor list-management signal.
That's why parsing must lead to action. “DMARC exists” isn't enough. The useful answer is whether the policy is monitor-only, partially enforced, fully enforced, aligned with actual sending, and safe for the current mail flow.

Advanced Techniques for Bulk Audits and Performance

One domain is easy. A client portfolio, reseller fleet, or AI-driven outbound system is not.
At that point, the problem stops being “How do these records look?” and becomes “How can the system check them fast, reliably, and without blocking everything else?”

When one domain becomes hundreds

Sequential DNS querying turns slow quickly. For agency audits and internal platform checks, asynchronous execution is the practical move.
A typical pattern uses asyncio with a DNS library designed for concurrent work. The implementation details vary, but the operational goal stays the same:
  • Run lookups concurrently so a single slow domain doesn't stall the whole batch.
  • Separate query logic from interpretation logic so parsing can evolve without rewriting the network layer.
  • Return structured results for each domain, including timeouts and no-answer states.
DNS latency isn't trivial. In Python network work, DNS resolution often accounts for over 50ms of total connection time, and performance guidance on measuring domain lookup latency recommends alerting when DNS latency exceeds that 50ms threshold.
That threshold matters for email systems too. Slow DNS lookups delay message processing, health checks, and verification workflows that depend on live mail infrastructure.

Operational guardrails that matter

Bulk lookup systems fail in boring ways unless the basics are handled well.
A practical operating table looks like this:
Concern
What to implement
Why it matters
Timeouts
Per-query timeout and retry policy
Prevents one broken nameserver from hanging the audit
Caching
Short-lived cache for repeated domains
Reduces redundant lookups during portfolio scans
Error typing
Distinguish NXDOMAIN, timeout, and no-answer
Different failure modes need different remediation
Rate control
Limit concurrency when needed
Helps avoid noisy bursts against resolvers
Developers managing many domains should also think beyond record validity and into ownership hygiene. This guide on domain portfolio management is useful when DNS checks have to fit into a broader operational process.
For AI agents, these concerns matter even more. Agents can write copy and trigger sends, but they shouldn't send blindly. They need live DNS and authentication checks before volume moves, otherwise they automate deliverability mistakes at scale.

A Complete Deliverability Diagnostic Workflow in Python

A good troubleshooting flow doesn't start with whichever record is easiest to query. It follows the order that isolates root cause fastest.
notion image

The order that makes troubleshooting faster

Use this sequence when emails land in spam, bounce unexpectedly, or disappear from onboarding flows.
  1. Check SPF Confirm there is one SPF record, that the syntax is valid, and that the sending providers are represented.
  1. Check DKIM Verify that the expected selector exists in DNS and that the signing domain aligns with the mail flow.
  1. Check DMARC Read the policy and decide whether it's monitor-only or enforced. p=none, p=quarantine, and p=reject mean very different things operationally.
  1. Check MX Make sure inbound mail routing is present and sensible. Broken inbound routing hurts reply handling, support, and return-path workflows.
  1. Check PTR or reverse DNS Confirm that outbound infrastructure identifies itself cleanly. Mismatched reverse DNS can make a domain look poorly maintained.
  1. Check blacklist status If a domain or IP is listed, technical authentication alone won't solve the inbox problem.
  1. Test SMTP and IMAP connectivity This matters when the issue is infrastructure reachability rather than policy syntax.

What each result means

A small checklist helps teams classify what they found:
  • Authentication broken means mailbox providers may distrust the sender.
  • Authentication present but weak means mail may pass inconsistently.
  • MX missing or wrong means replies and inbound workflows may fail.
  • PTR inconsistent means the sending host may look suspicious.
  • Blacklist issues mean reputation damage is already in play.
  • Connectivity issues mean the mail stack may not be reachable at all.
This is also where non-DNS context matters. Bounce rate interpretation, complaint handling, sending behavior, and reputation monitoring all affect whether technical fixes produce inbox gains. Teams thinking broadly about reputation should read more about protecting your SaaS business online, especially when sender trust and domain trust are tightly linked.
For AI agents, the same workflow should return structured output, not prose only. An agent needs fields like status, severity, why_it_matters, and next_fix, so it can decide whether to pause sending, open a ticket, or recommend a remediation path.

Frequently Asked Questions About Python DNS Lookups

What is the best Python library for email-related DNS lookups?
For deliverability work, dnspython is the practical choice because it can query MX and TXT records directly. That's necessary for SPF, DKIM, and DMARC checks.
Why doesn't socket.getaddrinfo() solve email troubleshooting?
It helps with host resolution, but email problems usually live in authentication and routing records. Those require TXT and MX queries, not just A or AAAA resolution.
How should a script handle DNS propagation?
Use an explicit resolver, query live records repeatedly over time, and treat mismatched answers as a propagation issue until the record stabilizes. Don't trust one machine's local cache.
What should a Python script validate in SPF first?
Start with existence, single-record presence, syntax, and lookup complexity. A published SPF record can still fail if it expands into too many DNS lookups.
Can AI agents run these checks automatically?
Yes, but they need structured results and live data. An agent should be able to detect authentication failures, routing issues, and reputation risks before it sends or scales a campaign.
Is a DNS lookup enough to explain why emails go to spam?
No. DNS is a major layer, but inbox placement also depends on sender reputation, blacklist status, infrastructure quality, and sending behavior.
Email deliverability issues usually aren't random. They come from authentication gaps, DNS mistakes, blacklist problems, routing errors, or infrastructure signals that look suspicious to mailbox providers. The fastest way to stop guessing is to run live checks that explain what's broken and what to fix next.
mailX by Mailwarm is a free suite of DNS lookup, email deliverability, and network tools built for humans and AI agents. It runs live checks across SPF, DKIM, DMARC, BIMI, MX, SMTP and IMAP connectivity, blacklist status, DNS records, domain configuration, and mail infrastructure, then returns clear explanations and exact remediation steps. To go beyond raw DNS output, run a free deliverability audit, check your SPF record, run a DMARC check, or connect mailX to your AI agent through MCP. No signup. No data stored. Instant results.

Most senders lose 30–70% of their emails to spam without knowing it.

Get a free expert audit of your domain, email authentication, and infrastructure. Identify hidden issues and fix them fast.

Book Your Free Deliverability Audit

Written by

Daniel Nwankwo

Community and Content manager