Skip to main content
Thirdwatchthirdwatch
Companies & leads

Audit Email Domain Health: SPF, DKIM, and DMARC Checks

Check SPF, DKIM, DMARC, and MX records across an email list before a campaign, with a reproducible Apify and Python workflow.

Editorial illustration for companies & leads
Jul 21, 2026 · 2 min read · 399 words
View the Apify scraper →

A domain can accept mail and still be poorly configured for outbound sending. That distinction matters when a campaign list mixes customer addresses, free inboxes, partner domains, and old CRM imports. A useful audit separates address quality from domain authentication instead of reducing everything to a single green check.

The Email Verifier & Validator returns syntax and mail-infrastructure status, SMTP mailbox evidence, catch-all/disposable/role flags, plus optional SPF, DKIM, and DMARC signals. This workflow isolates domain health by disabling SMTP; enable the default SMTP check when mailbox evidence is also required.

Run a domain-health audit

Submit the addresses with checkDomainHealth enabled:

import os
import requests

response = requests.post(
    "https://api.apify.com/v2/acts/thirdwatch~email-verifier/run-sync-get-dataset-items",
    params={"token": os.environ["APIFY_TOKEN"]},
    json={
        "emails": [
            "sales@vendor-one.com",
            "founder@vendor-two.com",
            "ops@vendor-three.com",
        ],
        "checkDomainHealth": True,
        "smtpCheck": False,
    },
    timeout=300,
)
rows = response.json()

Cache the result by domain. Ten thousand addresses from 200 companies need 200 domain assessments, not 10,000 copies of the same DNS data.

Interpret the fields without overclaiming

mxFound tells you whether the domain advertises a literal mail exchanger. mailInfrastructureFound also accounts for the RFC A-record fallback, while mailInfrastructureStatus distinguishes available, unavailable, and unknown. Suppress only when mail infrastructure is unavailable, not merely because MX is absent. domainHealth.spf and domainHealth.dmarc describe published sender controls. domainHealth.dkim is best-effort because DKIM selectors are not globally discoverable.

Check domainHealthChecked before interpreting those three booleans. It is true for this workflow because checkDomainHealth was enabled; when the input remains at its default false, the domain-health object is intentionally unpopulated evidence rather than a failed audit.

Use the checks as routing rules:

def route(row):
    if not row["syntaxValid"] or not row["mailInfrastructureFound"]:
        return "suppress"
    health = row.get("domainHealth") or {}
    if not health.get("dmarc"):
        return "domain-review"
    if row["isDisposable"]:
        return "suppress"
    return "eligible"

This produces an auditable reason for every decision. It is much easier to defend than a vendor score whose inputs are hidden.

Schedule the audit around list changes

Run the check when a large import lands, before a high-volume send, and monthly for dormant CRM segments. DNS settings change, but not often enough to justify checking every address on every workflow execution. Save the last checked timestamp and refresh only stale domains.

If mailbox confirmation is required, leave smtpCheck enabled. The same Actor will run a non-sending SMTP recipient check, detect catch-all domains, and return valid, invalid, or unknown in technical_status; ambiguous provider responses are never promoted to confirmed mailboxes.

Frequently asked questions

Does a passing domain-health check prove a mailbox exists?

No. MX, SPF, DKIM, and DMARC describe the domain's mail setup. They do not confirm that one recipient mailbox exists.

Why can DKIM be false when a company sends signed mail?

DKIM keys live under selector-specific DNS names. The Actor checks common selectors, so an uncommon private selector may not be found.

Related