# SPF, DKIM & DMARC Checker – Bulk Email Deliverability Audit (`gazidev/email-auth-checker`) Actor

Bulk SPF, DKIM and DMARC checker for thousands of domains: MX, SPF 10-lookup limit, DMARC policy & rua, 65 DKIM selectors, BIMI, MTA-STS, TLS-RPT, Gmail/Yahoo bulk-sender readiness, 0-100 score, A-F grade and fix list. $1 per 1,000 domains.

- **URL**: https://apify.com/gazidev/email-auth-checker.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 domain checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## SPF, DKIM & DMARC Checker – Bulk Email Deliverability Audit

**Check SPF, DKIM, DMARC, MX, BIMI, MTA-STS and TLS-RPT for thousands of domains in one run.** Paste domains, URLs or email addresses and get one row per domain with a **0–100 email authentication score**, an **A–F grade**, a **Gmail/Yahoo bulk-sender readiness** flag, a prioritized list of issues, and the **exact DNS fix** for each one.

No API key, no SMTP connections, no proxies: only public DNS (Cloudflare 1.1.1.1, Google 8.8.8.8, Quad9 or your own resolver) plus the public MTA-STS policy file. **$1 per 1,000 domains.**

### What does the Email Auth Checker do?

| Check | Details |
|---|---|
| MX | Mail servers sorted by priority, whether each MX host resolves, Null MX detection, and the email provider (Google Workspace, Microsoft 365, Zoho, Proofpoint, Mimecast and about 40 more) |
| SPF checker | Full record, parsed mechanisms, includes, `all` policy, `redirect`, a **recursive DNS-lookup count with the RFC 7208 10-lookup limit**, void lookups, broken includes, multiple records, `+all`/`?all`, deprecated `ptr` |
| DMARC checker | Policy `p`, subdomain policy `sp`, `pct`, `rua`/`ruf`, `adkim`/`aspf` alignment, fallback to the organizational domain, and **external report authorization** for rua addresses at DMARC vendors (RFC 7489 §7.1) |
| DKIM checker | Probes **~65 common selectors** (Google, Microsoft 365 selector1/2, Mailchimp k1–k3, SendGrid, Mailgun, Amazon SES, Postmark, HubSpot, Klaviyo, Brevo, Zoho, Proton, Fastmail, Zendesk…) plus your own selectors. Returns key type, key size (flags 1024-bit keys) and revoked keys. |
| MTA-STS | `_mta-sts` TXT, plus download and validation of `https://mta-sts.<domain>/.well-known/mta-sts.txt` (mode, max_age, and whether every MX host is covered) |
| TLS-RPT | `_smtp._tls` record and report address |
| BIMI | `default._bimi` record, logo SVG URL, VMC URL, and a warning when DMARC is not enforced (logos are not shown then) |
| Score & grade | Starts at 100 and subtracts points per issue (missing DMARC −30, missing SPF −25, `p=none` −15, no DKIM −15, SPF > 10 lookups −10…). A ≥ 90, B ≥ 80, C ≥ 65, D ≥ 50, F < 50 |
| Fix list | `recommendations[]` with code, severity, points, a plain-English message and the DNS fix. `topFix` holds the most important one. |

### Use cases

- **Cold email & outbound agencies:** audit every sending domain and every client domain before a campaign. Find SPF over 10 lookups, missing DKIM or DMARC at `p=none` before they hurt inbox placement.
- **Gmail & Yahoo bulk-sender requirements (2024+):** the `bulkSenderReady` column shows whether a domain has SPF, DKIM and DMARC published.
- **Lead qualification for email security / deliverability services:** run a list of prospect domains and filter by grade D/F or `dmarcPolicy = missing`. Every row includes a ready-made talking point (`topFix`).
- **Security & compliance:** spot domains open to spoofing (no DMARC enforcement, `+all`), parked domains without a `v=spf1 -all` lockdown, and weak 1024-bit DKIM keys.
- **MSPs & IT teams:** monitor client domains on a schedule and export to Google Sheets or Excel.

### Input example

```json
{
  "domains": ["apify.com", "https://www.github.com/apify", "jane@stripe.com"],
  "dkimSelectors": ["s1", "mailo"],
  "lookupTarget": "rootDomain",
  "fetchMtaStsPolicy": true,
  "resolver": "cloudflare"
}
```

You can also paste a long list or a whole CSV into **Bulk list**, or give a **file URL** (TXT or CSV, for example a published Google Sheet). URLs and emails are reduced to the domain, and duplicates are removed. Use **As entered** to check a sending subdomain such as `mail.example.com`.

### Output example

One row per domain. The flat columns fit Excel or Google Sheets, and full details sit in nested objects (`spf`, `dmarc`, `dkim`, `mx`, `mtaStsDetails`, `recommendations`). Example (shortened):

```json
{
  "domain": "apify.com",
  "status": "OK",
  "score": 95,
  "grade": "A",
  "bulkSenderReady": "yes",
  "emailProvider": "Google Workspace",
  "mxHosts": "1 aspmx.l.google.com, 5 alt1.aspmx.l.google.com, ...",
  "spfPolicy": "-all (fail)",
  "spfLookups": 10,
  "spfIncludes": "_spf.google.com, mailgun.org, amazonses.com, 19497222.spf05.hubspotemail.net",
  "dmarcPolicy": "reject",
  "dmarcPct": 100,
  "dmarcRua": "mailto:dmarc-reports@apify.com",
  "dkimSelectors": "google (rsa 2048), intercom (rsa 1024)",
  "mtaSts": true,
  "tlsRpt": true,
  "bimi": true,
  "issues": ["dkim_weak_key"],
  "topFix": "Rotate to a 2048-bit RSA key."
}
```

Statuses: `OK`, `NO_MX` (the domain exists but has no MX), `NXDOMAIN`, `TIMEOUT`/`SERVFAIL` and `INVALID_INPUT`. Only `OK` and `NO_MX` rows are charged.

### Pricing

**$0.001 per domain checked ($1 per 1,000 domains)**, plus a tiny per-run start fee. Everything is included: DKIM probing, MTA-STS policy fetch, report authorization and the score. Non-existent domains, DNS failures and invalid inputs are free.

| Actor (Apify Store, Oct 2026) | Price per domain |
|---|---|
| **This Actor** | **$0.001** |
| thoob/dns-bulk-lookup | $0.003 |
| ryanclinton/dns-record-lookup | $0.003 |
| drumlinlabs/email-auth-auditor | $0.004 |
| q_services/email-deliverability-auditor | $0.001 + $0.005 per run |
| weiseer/email-deliverability-checker | $0.01 |
| nexgendata/email-dmarc-auditor | $0.05 + $0.01 per run |

You can cap spending with the run's **maximum total charge**: the Actor stops cleanly when the limit is reached.

### FAQ

**Why is DKIM "not found" for a domain that signs its mail?** DNS cannot list DKIM keys. The Actor checks about 65 common selectors, but many senders use custom ones (Gmail's current selectors are not public, for example). Open a sent email, read the `s=` value in the `DKIM-Signature` header, and add it to **Your DKIM selectors**.

**Does it verify mailboxes or connect to mail servers?** No. It does not open SMTP connections and does not send email. It only reads public DNS records and the public MTA-STS policy file.

**Does it check blacklists?** No. Public DNSBLs such as Spamhaus do not allow commercial queries through public resolvers.

**How fast is it?** About 200 domains in under a minute with the defaults (≈75 DNS queries per domain).

**Can I check a subdomain?** Yes. Set **What to check** to *As entered*. DMARC falls back to the organizational domain as receivers do.

**Is the score an official standard?** No. It is a transparent checklist; every deduction is listed in `recommendations` with its points.

### Use with AI agents (Apify MCP)

The Actor works as a tool in the [Apify MCP server](https://mcp.apify.com): an agent can call `gazidev/email-auth-checker` with `{"domains": ["example.com"]}` and read `grade`, `issues` and `topFix` to explain a domain's deliverability problems or write the DNS records to fix them. You can also call it through the Apify API, a Zapier/Make/n8n integration, or on a schedule.

### Related Actors

- [DNS Lookup – Bulk DNS Records](https://apify.com/gazidev/bulk-dns-lookup): all record types, reverse DNS and DNS change monitoring.
- [Email Syntax & MX Validator](https://apify.com/gazidev/email-syntax-mx-validator): validate email address lists.

# Actor input Schema

## `domains` (type: `array`):

Domains to audit, e.g. `example.com`. URLs (`https://www.example.com/page`) and email addresses (`jane@example.com`) are accepted and reduced to the domain. Duplicates are removed automatically.

## `bulkText` (type: `string`):

Paste a long list: one domain/email/URL per line, comma or semicolon separated, or a whole CSV export (the first cell that looks like a domain, URL or email in each row is used).

## `sourceFileUrl` (type: `string`):

Public URL of a .txt or .csv file with domains or emails (e.g. a Google Sheets 'Publish to web' CSV link).

## `dkimSelectors` (type: `array`):

Extra DKIM selectors to check, e.g. `s1`, `mailo`, `scph0922` (the part before `._domainkey`). Find yours in the `DKIM-Signature: s=` header of a sent email. They are checked in addition to ~65 common selectors (Google, Microsoft 365, Mailchimp, SendGrid, Mailgun, Amazon SES, HubSpot, Klaviyo, Brevo, Zoho, Proton...).

## `onlyCustomSelectors` (type: `boolean`):

Skip the common-selector list and check only the selectors above (faster for large lists where you know the selector).

## `checkDkim` (type: `boolean`):

DKIM keys cannot be listed via DNS, so 'not found' means 'not under the checked selectors'.

## `lookupTarget` (type: `string`):

'Root domain' (default) checks the registrable domain (www.example.com → example.com), which is what email uses. 'As entered' checks the exact host name, e.g. a sending subdomain like `mail.example.com`.

## `fetchMtaStsPolicy` (type: `boolean`):

When `_mta-sts` TXT exists, download https://mta-sts.<domain>/.well-known/mta-sts.txt and check mode, max_age and that all MX hosts are covered.

## `checkReportAuthorization` (type: `boolean`):

For rua addresses on another domain (e.g. a DMARC report vendor), verify the `<domain>._report._dmarc.<vendor>` record (RFC 7489 §7.1).

## `resolver` (type: `string`):

Public resolver used for all queries.

## `customResolvers` (type: `array`):

Used only when DNS resolver = Custom, e.g. `208.67.222.222`.

## `maxDomains` (type: `integer`):

Stop after this many unique domains (0 = no limit).

## `maxConcurrency` (type: `integer`):

How many domains are checked at the same time.

## `timeoutSecs` (type: `integer`):

Maximum time per DNS query (one retry on timeout) and per MTA-STS policy download.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "github.com",
    "stripe.com"
  ],
  "onlyCustomSelectors": false,
  "checkDkim": true,
  "lookupTarget": "rootDomain",
  "fetchMtaStsPolicy": true,
  "checkReportAuthorization": true,
  "resolver": "cloudflare",
  "maxDomains": 0,
  "maxConcurrency": 30,
  "timeoutSecs": 8
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `details` (type: `string`):

No description

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "apify.com",
        "github.com",
        "stripe.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/email-auth-checker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "domains": [
        "apify.com",
        "github.com",
        "stripe.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("gazidev/email-auth-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "apify.com",
    "github.com",
    "stripe.com"
  ]
}' |
apify call gazidev/email-auth-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gazidev/email-auth-checker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/JzEMkqfo6TOr4AFJD/builds/yDUvPOUStQv9ScDum/openapi.json
