XiaTools

Python Scripting to Fetch and Parse Domain TXT Records

Updated 11 Oct 2026

You can perform a python dns txt lookup by utilizing robust third-party libraries like dnspython to query domain name servers, retrieve text payloads, and parse complex security policies. TXT records store human- and machine-readable text data, making them the backbone for email authentication frameworks like SPF, DKIM, and DMARC, as well as domain ownership verification methods. Whether you are automating security audits, building monitoring dashboards, or verifying your DNS configurations programmatically, writing a dedicated Python script saves countless hours of manual terminal work.

Before writing code, it is often helpful to quickly inspect your target records via a web interface to understand what data to expect. You can use the TXT Lookup tool to instantly inspect raw text entries for any domain, helping you validate your queries before parsing them in your scripts.

Setting Up Your Python DNS Environment

Python's built-in libraries do not include native DNS resolution capabilities that easily handle advanced record types like TXT. Therefore, you need to install the industry-standard dnspython library. Open your terminal or command prompt and run the following command to install it in your active Python environment:

pip install dnspython

Once installed, you can import the resolver module inside your scripts to query nameservers directly. This library handles everything from standard lookups to custom timeout configurations and specific DNS server targeting.

Basic Python Script for DNS TXT Lookup

Writing a basic script to query TXT records requires initializing a resolver object and calling its resolve() method. Let's write a script that targets example.com and prints all raw TXT strings returned by the authoritative nameservers.

import dns.resolver

def fetch_txt_records(domain):
    try:
        # Query TXT records for the specified domain
        answers = dns.resolver.resolve(domain, 'TXT')
        print(f"Found {len(answers)} TXT record(s) for {domain}:\n")
        
        for rdata in answers:
            # rdata.strings returns a tuple of bytes objects
            # We decode each byte string to UTF-8
            full_text = ''.join([b.decode('utf-8') for b in rdata.strings])
            print(f"- {full_text}")
            
    except dns.resolver.NXDOMAIN:
        print(f"Error: The domain {domain} does not exist.")
    except dns.resolver.NoAnswer:
        print(f"Error: No TXT records found for {domain}.")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")

if __name__ == "__main__":
    target_domain = "example.com"
    fetch_txt_records(target_domain)

When you run this script, the output will display the exact strings configured in the DNS zone file. For example:

Found 2 TXT record(s) for example.com:

- v=spf1 include:_spf.example.com ~all
- Ed25519=123456789abcdef

Parsing Security Policies (SPF, DKIM, DMARC)

Retrieving text records is only the first step. Because TXT records contain vital email security data, your scripts will often need to parse them to check configurations. For instance, parsing an SPF record involves identifying mechanisms like include, ip4, ip6, and the final qualifier (~all or -all).

Here is an intermediate script that filters and parses specific security policies from the returned TXT record set:

import dns.resolver

def analyze_security_txt(domain):
    resolver = dns.resolver.Resolver()
    # Optional: Use public resolvers like Cloudflare or Google
    resolver.nameservers = ['1.1.1.1', '8.8.8.8']
    
    try:
        answers = resolver.resolve(domain, 'TXT')
        
        for rdata in answers:
            txt_content = ''.join([b.decode('utf-8') for b in rdata.strings])
            
            if txt_content.startswith('v=spf1'):
                print(f"[SPF Policy Found]")
                print(f"  Raw: {txt_content}")
                mechanisms = txt_content.split()
                print(f"  Mechanisms count: {len(mechanisms) - 1}\n")
                
            elif 'dmarc' in txt_content.lower() or txt_content.startswith('v=DMARC1'):
                print(f"[DMARC Policy Found]")
                print(f"  Raw: {txt_content}\n")
                
            elif 'dkim' in txt_content.lower() or 'p=' in txt_content:
                print(f"[Possible DKIM/Public Key Found]")
                print(f"  Raw: {txt_content}\n")
                
    except Exception as e:
        print(f"Error processing domain {domain}: {e}")

if __name__ == "__main__":
    analyze_security_txt("example.com")

Handling Split Strings (Chunking)

DNS limits a single character string inside a TXT record to 255 characters. For long keys—such as 2048-bit DKIM public keys—DNS providers automatically split the string into multiple chunks within the same record object. If you do not join these chunks correctly using b.decode('utf-8') over rdata.strings, your script will output truncated data or syntax errors when parsing cryptographic keys.

Comparison of DNS Query Methods in Python

Method / Library Pros Cons Best Use Case
dnspython Feature-rich, supports custom nameservers, robust error handling Requires external installation (pip install) Production scripts, security auditing, complex automation
socket module Built into Python standard library, zero dependencies Very limited DNS parsing, lacks low-level control for TXT chunks Basic connectivity checks only
subprocess (dig/nslookup) Leverages native OS tools Platform-dependent, requires parsing raw CLI output strings Quick local debugging scripts

Advanced Querying: Custom Nameservers and Timeouts

In enterprise environments or when debugging propagation issues, querying your local Internet Service Provider (ISP) nameserver is insufficient. You need to query authoritative nameservers directly. dnspython makes this straightforward by allowing you to instantiate a custom resolver instance.

import dns.resolver

def query_authoritative_server(domain, nameserver_ip):
    resolver = dns.resolver.Resolver()
    resolver.nameservers = [nameserver_ip]
    resolver.timeout = 5.0
    resolver.lifetime = 5.0
    
    try:
        answers = resolver.resolve(domain, 'TXT')
        for rdata in answers:
            print(''.join([b.decode('utf-8') for b in rdata.strings]))
    except dns.resolver.Timeout:
        print("The DNS query timed out.")
    except Exception as e:
        print(f"Query failed: {e}")

if __name__ == "__main__":
    # Querying using a documentation IP representing a nameserver
    query_authoritative_server("example.com", "192.0.2.1")

Common Mistakes and How to Fix Them

When writing automation scripts for DNS lookups, developers frequently encounter a few specific pitfalls:

  • Ignoring Chunked Strings: Treating rdata as a single string instead of iterating through rdata.strings results in broken DKIM keys. Always join the decoded byte segments.
  • Not Catching Specific Exceptions: Failing to handle NXDOMAIN or NoAnswer exceptions will cause your script to crash when encountering unregistered domains or missing records. Always wrap your resolve calls in try-except blocks.
  • Hitting Rate Limits: Querying public resolvers too rapidly in a loop can trigger rate limits or temporary IP bans. Implement time.sleep() delays between requests when scanning multiple domains.
  • Name Resolution Caching: dnspython caches results by default in certain contexts. If you are tracking real-time DNS propagation changes, ensure you disable or bypass the cache in your resolver configuration.

Quick Checklist for Python DNS TXT Scripts

  • Installed dnspython via pip install dnspython in your virtual environment.
  • Implemented error handling for NXDOMAIN, NoAnswer, and Timeout exceptions.
  • Joined string chunks correctly using rdata.strings to support long DKIM records.
  • Configured custom timeout and lifetime values to prevent hanging threads.
  • Verified record outputs against an independent validation tool.

Frequently asked questions

Why do TXT records get split into multiple strings in Python?

DNS protocol specifications limit individual text strings inside TXT records to 255 characters. When a record exceeds this length, such as a 2048-bit DKIM cryptographic key, the DNS server splits the data into multiple chunks. Libraries like dnspython expose these chunks as a tuple of byte strings, which you must manually join in your code.

Can I perform a DNS TXT lookup using only Python's standard library?

Python's standard library includes the `socket` module, but it does not provide native functions to query specific DNS record types like TXT. While you can perform basic forward and reverse lookups, handling TXT records reliably requires third-party packages like dnspython or invoking system tools via subprocess.

How can I query a specific public DNS resolver like Cloudflare or Google?

You can configure custom nameservers by instantiating a resolver object and setting its nameservers property. For example, setting `resolver.nameservers = ['1.1.1.1', '8.8.8.8']` ensures your script queries Cloudflare and Google servers instead of relying on your local network configuration.

What causes a dns.resolver.NoAnswer exception?

This exception occurs when the target domain exists and responds to queries, but has no TXT records published in its zone file. Ensure your script gracefully catches this exception so it can continue processing other domains in a batch list.

Are there rate limits when running automated DNS scripts?

Yes. If your script queries public resolvers or third-party authoritative nameservers too quickly in a loop, the remote servers may rate-limit your IP address or drop your packets. It is best practice to include short time delays between requests when auditing multiple domains.

Related articles

Free tools