Web and domains

Bulk SPF, DMARC and DKIM Checker for Domain Lists

Checking email authentication one domain at a time is slow. This Actor takes a list of domains and returns one record per domain with its SPF, DMARC, DKIM, MTA-STS and BIMI setup, an email security score, and optionally WHOIS, TLS and HTTP data.

Open the Actor on Apify Store

SPF, DMARC & DKIM Checker — Bulk Domain WHOIS, DNS & SSL. Pay per result; see the Store page for current pricing.

The source and its limits

Everything comes from public sources: DNS records over DNS-over-HTTPS (Cloudflare, with Google as fallback), registration data from RDAP (the successor to WHOIS), and each domain's own TLS and HTTP endpoints. No API keys, proxies or browser.

The dns section reads A, AAAA, MX, NS, TXT, CAA and SOA records and checks:

  • SPF: record present, syntax, lookup count and the all qualifier.
  • DMARC: policy, subdomain policy, pct, and aggregate and forensic reporting addresses.
  • DKIM: 18 common selectors probed, plus any you add.
  • MTA-STS record and policy mode, and BIMI.

How the email security score is computed

CheckPoints
SPF record present and valid (10 lookups or fewer, no unknown terms, single record)25
DMARC policy reject / quarantine / none35 / 20 / 5
At least one DKIM selector found25
MTA-STS record present10
BIMI record present5

Limits worth knowing

  • DKIM selectors cannot be listed from outside, only probed. A domain whose selector is not in the list shows no DKIM even if it is configured; add your provider's selector in dkimSelectors.
  • The SPF lookup count covers the top-level record only (includes are not followed), so it is a lower bound.
  • Subdomains are reduced to their registrable domain, and many registries redact WHOIS contact data.
  • All sections of a domain share a 20-second budget; a slow source marks its own section as an error without blocking the others.

Fields you get

FieldWhat it holds
dns.spfRecord, spfValid, spfAllQualifier, spfLookupCount, spfIssuesSPF
dns.dmarcRecord, dmarcPolicy, dmarcSubdomainPolicy, dmarcPct, hasRua, hasRufDMARC
dns.dkimSelectorsProbed, dkimSelectorsFoundDKIM
dns.mtaStsPresent, mtaStsPolicyMode, bimiRecordMTA-STS and BIMI
dns.mx, mxProviderGuessMail servers and a provider guess (Google, Microsoft, Zoho and others)
emailSecurityScore, summaryScore from 0 to 100 and a one-line summary
whois.*Registrar, registration and expiration dates, nameservers, DNSSEC (with whois)
tls.*, http.*Certificate issuer and expiry, status code, redirects, security headers, tech hints

Example output

The email-security part of one record for github.com (from the Actor's README; values illustrative, other sections omitted):

{
  "domain": "github.com",
  "dns": {
    "status": "ok",
    "hasMx": true,
    "mxProviderGuess": "google",
    "spfRecord": "v=spf1 ip4:192.30.252.0/22 include:_netblocks.google.com ... ~all",
    "spfValid": true,
    "spfAllQualifier": "~all",
    "spfLookupCount": 7,
    "spfIssues": [],
    "dmarcRecord": "v=DMARC1; p=reject; pct=100; rua=mailto:dmarc@github.com",
    "dmarcPolicy": "reject",
    "dmarcSubdomainPolicy": null,
    "dmarcPct": 100,
    "hasRua": true,
    "hasRuf": false,
    "dmarcIssues": [],
    "dkimSelectorsProbed": 18,
    "dkimSelectorsFound": [
      "google",
      "pm"
    ],
    "mtaStsPresent": true,
    "mtaStsPolicyMode": "enforce",
    "bimiRecord": null
  },
  "dmarcPolicy": "reject",
  "emailSecurityScore": 95,
  "errors": {}
}

Use cases

Deliverability audit of a client list

Run the dns section over every client domain and sort by emailSecurityScore to find missing DMARC or a soft SPF policy.

Expiry watch

Run whois and tls on a schedule and alert on low daysUntilExpiry for domains and certificates.

Lead segmentation

Combine dns and http to group domains by mail provider and tech stack hints.

How to run it

You need an Apify account and its API token (Console → Settings → API & Integrations). Each run is billed per result to your account; set a maximum total charge for the run to cap spend.

With curl

The sync endpoint starts a run, waits up to 300 seconds and returns the dataset items. SPF, DMARC and DKIM for ten domains, DNS section only:

export APIFY_TOKEN=<your Apify API token>

curl -sS -X POST \
  "https://api.apify.com/v2/acts/owlabee~spf-dmarc-dkim-checker/run-sync-get-dataset-items" \
  -H "Authorization: Bearer ${APIFY_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
  "domains": [
    "example.com",
    "github.com",
    "apify.com",
    "cloudflare.com",
    "mozilla.org",
    "wikipedia.org",
    "stripe.com",
    "shopify.com",
    "nytimes.com",
    "python.org"
  ],
  "sections": [
    "dns"
  ],
  "dkimSelectors": [],
  "maxConcurrency": 5
}'

From an AI agent (MCP)

Apify hosts an MCP server. This config exposes only this Actor to the agent:

{
  "mcpServers": {
    "owlabee-spf-dmarc-dkim-checker": {
      "url": "https://mcp.apify.com/?tools=owlabee/spf-dmarc-dkim-checker",
      "headers": {
        "Authorization": "Bearer ${APIFY_TOKEN}"
      }
    }
  }
}

Then ask, for example: Check SPF, DMARC and DKIM for these ten domains and list the ones whose DMARC policy is not reject, with their email security score.

Python, Node.js and more examples

Runnable curl.sh, python.py, node.mjs and mcp.json files for this Actor are in examples/spf-dmarc-dkim-checker on GitHub.

FAQ

Why does a domain show no DKIM when I know it has it?

DKIM selectors are not discoverable. The Actor probes 18 common ones; add yours in dkimSelectors (for example fm1 for Fastmail or krs for Klaviyo).

Does it send email or change anything?

No. It only reads public DNS, RDAP, TLS and HTTP responses.

How many domains can I check?

The list has no fixed cap. Up to 20 domains run in parallel (maxConcurrency), and the run stops cleanly at its maximum total charge.

Why is a WHOIS field empty?

Most registries redact personal data, and some country domains offer no RDAP. The Actor reports exactly what the registry returns.

What if a site has no web server?

tls.status and http.status become unavailable; the DNS and WHOIS sections are still filled.