Automating Email Deliverability Checks with Python

For any application sending email, from transactional alerts to marketing campaigns, three DNS records are critical gatekeepers: SPF (Sender Policy Framework), DKIM (DomainKeys Identified Mail), and DMARC (Domain-based Message Authentication, Reporting & Conformance). These records collectively determine whether your emails reach the inbox or are flagged as spam. Manually checking these can be tedious, especially when managing multiple domains or frequent changes. Fortunately, you can automate this process using Python and the dnspython library, eliminating the need for third-party APIs.

Prerequisites: Installing dnspython

The only external dependency required for this task is dnspython. Install it easily using pip:

pip install dnspython

Checking SPF Records

SPF records are stored in TXT records within your domain's DNS settings. A valid SPF record begins with the identifier v=spf1. The record specifies which mail servers are authorized to send email on behalf of your domain. A typical SPF record might look like v=spf1 include:_spf.google.com ~all, indicating that Google's mail servers are permitted, and other servers should be treated with caution (~all).

The Python function to retrieve SPF records queries for TXT records associated with the domain. It iterates through the results, identifying those that start with v=spf1. This direct DNS lookup avoids external services.

import dns.resolver

def get_spf(domain):
    try:
        records = dns.resolver.resolve(domain, 'TXT')
        spf_records = [str(r.strings[0].decode('utf-8')) for r in records if r.strings and r.strings[0].decode('utf-8').startswith('v=spf1')]
        return spf_records
    except dns.resolver.NoAnswer:
        return []
    except dns.resolver.NXDOMAIN:
        print(f"Domain {domain} does not exist.")
        return []
    except Exception as e:
        print(f"An error occurred while fetching SPF for {domain}: {e}")
        return []

Verifying DKIM Records

DKIM adds a digital signature to outgoing emails, allowing receivers to verify that the email originated from the claimed domain and hasn't been tampered with. DKIM records are also TXT records, but they are typically found under a specific subdomain, usually starting with default._domainkey or a custom selector like selector1._domainkey. The record contains a public key used for verification.

To check DKIM, you need to know the selector used. The function below attempts to find a common selector (default) and checks for the DKIM record. If not found, it indicates a potential issue with DKIM setup.

def get_dkim(domain, selector='default'):
    dkim_record_name = f"{selector}._domainkey.{domain}"
    try:
        records = dns.resolver.resolve(dkim_record_name, 'TXT')
        dkim_records = [str(r.strings[0].decode('utf-8')) for r in records if r.strings]
        return dkim_records
    except dns.resolver.NoAnswer:
        return []
    except dns.resolver.NXDOMAIN:
        print(f"DKIM record {dkim_record_name} not found.")
        return []
    except Exception as e:
        print(f"An error occurred while fetching DKIM for {domain}: {e}")
        return []

Interpreting DMARC Records

DMARC builds upon SPF and DKIM. It's a policy that tells receiving mail servers what to do if an email fails SPF and/or DKIM checks. DMARC records are also TXT records, located at _dmarc.yourdomain.com. The record specifies a policy (e.g., p=none, p=quarantine, or p=reject) and reporting addresses.

A p=none policy means no action is taken, but reports are sent. p=quarantine suggests emails should be marked as spam, and p=reject instructs receivers to reject the emails outright. The Python code queries for the _dmarc TXT record and parses its content.

def get_dmarc(domain):
    dmarc_record_name = f"_dmarc.{domain}"
    try:
        records = dns.resolver.resolve(dmarc_record_name, 'TXT')
        dmarc_records = [str(r.strings[0].decode('utf-8')) for r in records if r.strings]
        return dmarc_records
    except dns.resolver.NoAnswer:
        return []
    except dns.resolver.NXDOMAIN:
        print(f"DMARC record {dmarc_record_name} not found.")
        return []
    except Exception as e:
        print(f"An error occurred while fetching DMARC for {domain}: {e}")
        return []

Putting It All Together

To use these functions, you would call them with your domain name. It's crucial to check that SPF records are present and correctly configured, DKIM is enabled and yielding valid public keys, and DMARC is set up with a clear policy, ideally starting with p=none for monitoring before moving to stricter policies.

Here’s a simple example of how to integrate these checks:

if __name__ == "__main__":
    target_domain = "example.com" # Replace with your domain

    print(f"--- Checking DNS records for {target_domain} ---")

    spf_results = get_spf(target_domain)
    if spf_results:
        print("\nSPF Records Found:")
        for spf in spf_results:
            print(f"- {spf}")
    else:
        print("\nNo SPF records found or domain does not exist.")

    dkim_results = get_dkim(target_domain)
    if dkim_results:
        print("\nDKIM Records Found:")
        for dkim in dkim_results:
            print(f"- {dkim}")
    else:
        print("\nNo DKIM records found for the default selector.")

    dmarc_results = get_dmarc(target_domain)
    if dmarc_results:
        print("\nDMARC Records Found:")
        for dmarc in dmarc_results:
            print(f"- {dmarc}")
    else:
        print("\nNo DMARC records found.")

The Importance of Each Record

SPF acts like a whitelist for mail servers. It answers the question: "Is this server allowed to send email for this domain?" Without a proper SPF record, mail servers might reject emails simply because they can't verify the sender's legitimacy.

DKIM provides cryptographic verification. Think of it as a tamper-evident seal on your emails. It ensures that the message content hasn't been altered in transit and confirms the sender's domain. This adds a layer of trust that SPF alone cannot provide.

DMARC is the policy layer that unifies SPF and DKIM. It tells receivers how to handle emails that fail authentication and provides a mechanism for reporting. DMARC is crucial for understanding who is actually sending email using your domain (legitimately or not) and for enforcing a consistent authentication policy. Without DMARC, even with SPF and DKIM in place, you lack clear instructions on handling authentication failures and visibility into potential spoofing attempts.

What This Means for Developers and Operations

By integrating these Python checks into your deployment pipelines or monitoring systems, you can proactively identify misconfigurations before they impact email deliverability. This script is invaluable for anyone managing email infrastructure, ensuring that transactional emails reach users and marketing campaigns aren't lost to spam filters. The ability to perform these checks programmatically is key for maintaining a robust and trustworthy email sending practice.