# Email Auth Auditor: SPF, DKIM, DMARC & MTA-STS with Fixes (`neonninja/email-auth-auditor`) Actor

Audit SPF, DKIM, DMARC, MTA-STS, TLS-RPT and BIMI for any list of domains. Get a grade, plain-English issues, Gmail/Yahoo/Microsoft bulk-sender readiness and the exact DNS records to publish.

- **URL**: https://apify.com/neonninja/email-auth-auditor.md
- **Developed by:** [Nick Young](https://apify.com/neonninja) (community)
- **Categories:** Developer tools, Marketing, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 domain auditeds

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

## Email Authentication Auditor: SPF, DKIM, DMARC & MTA-STS + Fixes

Paste a list of domains and get, for each one, a **grade**, a list of **problems in plain English**, a **Gmail / Yahoo / Microsoft bulk-sender readiness** verdict, and the **exact DNS records to publish** to fix them.

Most DNS checkers stop at "here is your SPF record". This Actor tells you what is wrong with it and what to change.

### What it checks

| Check | What you get |
|---|---|
| **SPF** | Syntax errors, multiple records, the 10-DNS-lookup limit counted recursively through every include, void lookups, broken includes, `+all` / `?all` / missing `all`, deprecated `ptr`, and whether your mailbox provider is authorised |
| **DMARC** | Missing or duplicate records, policy strength (`none` / `quarantine` / `reject`), `pct`, subdomain policy, reporting addresses, external report authorisation, and inheritance from the parent domain |
| **DKIM** | Probes 30+ common selectors plus the ones for your detected provider and any you add. Reports key type, **key length** (flags 1024-bit and weaker keys), test mode, revoked keys, and Microsoft 365 CNAMEs with no key behind them |
| **MTA-STS** | TXT record, downloads the policy file over HTTPS, checks the mode and that **every MX host is covered** (a mismatch in enforce mode silently blocks inbound mail) |
| **TLS-RPT** | Presence and reporting address |
| **BIMI** | Record, logo URL format, certificate, and whether your DMARC policy is strict enough for logos to show |
| **MX and provider** | Detects Google Workspace, Microsoft 365, Zoho, Fastmail, Proton, iCloud, GoDaddy, Namecheap, Yandex, OVHcloud, Rackspace, Amazon WorkMail and common security gateways |
| **Unused domains** | Domains that send and receive no mail get lock-down advice (`v=spf1 -all`, `p=reject`, null MX) instead of irrelevant DKIM warnings |
| **DNSSEC** | Whether the zone validates |

Everything is read from public DNS over HTTPS (Cloudflare with Google as fallback). The Actor never sends email and never logs in anywhere.

### Who uses it

- **MSPs and IT consultants** checking every client domain in one run, and getting the records to paste into each DNS panel.
- **Agencies and cold-email teams** confirming sending domains meet Google, Yahoo and Microsoft rules before a campaign.
- **Deliverability and DMARC consultants** finding prospects whose domains have no DMARC or a broken SPF record.
- **Security and M\&A teams** reviewing email-spoofing exposure across a portfolio of domains.
- **AI agents** via the Apify API or MCP server: the output is structured JSON with stable issue IDs.

### Input

```json
{
  "domains": ["acme.com", "https://www.example.org", "jane@startup.io"],
  "reportEmail": "dmarc@acme.com",
  "dkimSelectors": ["mycustomselector"],
  "checkMtaStsPolicy": true,
  "maxConcurrency": 5
}
```

Only `domains` is required. URLs and email addresses are converted to domains, and duplicates are removed. `reportEmail` is placed in the DMARC and TLS-RPT records the Actor suggests.

### Output

One item per domain. The **Overview** view shows the summary columns; the **Fixes** view shows the records to publish. A shortened real example:

```json
{
  "domain": "github.com",
  "grade": "B",
  "score": 85,
  "summary": "B (85/100). Top issue: SPF uses 10 of 10 DNS lookups.",
  "domainType": "receives-mail",
  "mailProvider": "Microsoft 365",
  "spfStatus": "valid",
  "dmarcPolicy": "quarantine",
  "dkimSelectorsFound": ["cm", "google", "k1", "k2", "k3", "s1", "s2", "selector1", "zendesk1", "zendesk2"],
  "mtaStsMode": "none",
  "bulkSenderReadiness": "ready",
  "issueCounts": { "critical": 0, "high": 0, "medium": 0, "low": 5, "info": 1 },
  "issues": [
    { "id": "spf-near-lookup-limit", "severity": "low", "title": "SPF uses 10 of 10 DNS lookups", "detail": "One more include, or a provider adding a nested include, will break SPF." },
    { "id": "dkim-1024", "severity": "low", "title": "DKIM key \"cm\" is 1024 bits", "detail": "Still accepted, but 2048-bit keys are the current recommendation. Rotate when convenient." }
  ],
  "recommendedRecords": [
    { "type": "TXT", "host": "_mta-sts.github.com", "value": "v=STSv1; id=20261031110757", "purpose": "Announce an MTA-STS policy. Change the id whenever the policy file changes." }
  ]
}
```

Each item also contains the full `spf`, `dmarc`, `dkim`, `mtaSts`, `tlsRpt` and `bimi` objects, with the raw records and every value parsed. A run summary is saved to the key-value store as `SUMMARY`.

#### Grades

Every domain starts at 100. Each issue subtracts points by severity: critical 30, high 15, medium 8, low 3. A is 90 and above, B 80, C 65, D 50, and F below 50.

#### Bulk-sender readiness

- `ready`: valid SPF, a DMARC record and at least one DKIM key were found.
- `dkim-unverified`: SPF and DMARC are fine, but no DKIM key was found on the selectors checked. DKIM may still exist under a custom selector.
- `not-ready`: SPF or DMARC is missing or broken.
- `not-applicable`: the domain sends no mail.

DNS can show whether the records exist. Alignment and one-click unsubscribe also count toward the Gmail, Yahoo and Microsoft rules, and those can only be checked on real messages.

### Pricing

You pay per domain successfully audited. Invalid inputs and domains that do not exist are free. The price is shown on the Actor's pricing tab. Set a maximum cost per run and the Actor stops cleanly when it is reached.

### Limits and honest caveats

- **DKIM selectors cannot be listed from DNS.** The Actor checks the common ones. If yours is custom, read the `s=` value from a `DKIM-Signature` header and add it under *Extra DKIM selectors*.
- Suggested SPF records include your detected mailbox provider only. Add an include for every other service that sends as you before publishing.
- Tighten DMARC to `quarantine` or `reject` only after reports show your legitimate mail passes.
- This is a technical check, not a compliance certification.

### FAQ

**Does it send test emails?** No. It only reads public DNS records and, for MTA-STS, one public HTTPS file.

**How fast is it?** Typically under a second per domain. Hundreds of domains take a few minutes.

**Can I schedule it?** Yes. Use an Apify schedule to re-audit your domains weekly and get notified when a grade drops.

# Actor input Schema

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

Domains to audit. You can paste domains, website URLs or email addresses; they are normalised and de-duplicated.

## `reportEmail` (type: `string`):

Optional. Used as the rua= address in the DMARC and TLS-RPT records this Actor suggests. Defaults to dmarc-reports@<domain> and tls-reports@<domain>.

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

Optional. DKIM selectors to check in addition to the 30+ common ones (find yours in the s= field of a DKIM-Signature header).

## `checkMtaStsPolicy` (type: `boolean`):

When a domain publishes MTA-STS, download its policy over HTTPS and check it covers every MX host.

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

How many domains to audit at the same time.

## `dnsResolver` (type: `string`):

Primary resolver. The other one is used automatically as a fallback.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "example.com"
  ],
  "checkMtaStsPolicy": true,
  "maxConcurrency": 5,
  "dnsResolver": "cloudflare"
}
```

# Actor output Schema

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

Grade, mail provider, SPF, DMARC, DKIM and MTA-STS status and the top issue for each domain.

## `fixes` (type: `string`):

Issue counts and the exact DNS records to publish for each domain.

## `summary` (type: `string`):

Counts of audited, invalid and non-existent domains and the grade distribution.

# 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",
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neonninja/email-auth-auditor").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",
        "example.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neonninja/email-auth-auditor").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",
    "example.com"
  ]
}' |
apify call neonninja/email-auth-auditor --silent --output-dataset

```

## MCP server setup

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

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/R3ck6T034bM0IVuiZ/builds/g5VEvYmlTvXdoM6tC/openapi.json
