# Email Domain Health Check: SPF, DKIM, DMARC, MX, BIMI (`lathewright/email-domain-health-check`) Actor

Returns a domain's email setup from public DNS: MX and provider, SPF with lookup count, DMARC policy, DKIM selectors, MTA-STS, TLS-RPT, BIMI and DNSSEC, with a 0 to 100 score, letter grade and plain-English fixes. No personal data. For deliverability and AI agents.

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

## Pricing

$4.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

## Email Domain Health Check: SPF, DKIM, DMARC, MX, BIMI

Audit any domain's email authentication from public DNS (SPF, DKIM, DMARC, MX, MTA-STS, BIMI), for deliverability consultants, sales-ops teams, IT admins and AI agents. It costs $0.004 per domain, so 500 domains cost $2.

Audit any domain's email setup in one call, over DNS only: MX records and the mail provider behind them, the SPF record with its lookup count and `all` policy, the DMARC policy and reporting addresses, DKIM keys for 30+ common selectors (or your own), MTA-STS and TLS-RPT, BIMI and DNSSEC. Each domain gets a 0 to 100 score, a letter grade, the list of problems and plain-English fixes.

Built and maintained by **Lathewright**. Lathewright is operated by AI systems under human ownership. A person reviews and approves everything we publish, send and sell. **$0.004 per domain.** No personal data: only public DNS records are read.

### Who uses it

- **Deliverability and sales-ops teams**: audit sending domains before a campaign; alert when a client's DMARC drops back to `p=none` or SPF exceeds 10 lookups.
- **Agencies and MSPs**: run all client domains monthly and send the report rows to a sheet.
- **Lead qualification**: a domain with no MX cannot receive mail; a domain with `p=reject` and BIMI is a mature sender.
- **AI agents**: "Is acme.io set up correctly for email? What should they fix first?"

### Example tasks

Paste any of these into the Input tab, or send the same JSON through the API. Prices are the per-result prices listed under Pricing.

#### 1. Check three domains

```json
{
  "domains": ["apify.com", "github.com", "example.com"]
}
```

What comes back: One row per domain with `grade`, `score`, `mxProvider`, `canReceiveEmail`, `spf`, `dmarc`, `dkim`, `issues` and `recommendations`. $0.012 for three.

#### 2. Test your own DKIM selectors

```json
{
  "domains": ["example.com"],
  "dkimSelectors": ["selector1", "selector2", "k1"]
}
```

What comes back: The `dkim` result covers the selectors you listed instead of the default set.

#### 3. Force a specific DNS resolver

```json
{
  "domains": ["example.org"],
  "resolver": "google"
}
```

What comes back: Same row, resolved through Google DNS first, with Cloudflare as the fallback.

### Input

```json
{ "domains": ["acme.io", "sales@example.com", "github.com"], "resolver": "cloudflare" }
```

| Field | Type | Notes |
|---|---|---|
| `domains` | array | Domains or email addresses (one per line) |
| `dkimSelectors` | array | Selectors to probe (default covers Google, Microsoft 365, Mailchimp, SendGrid, SES, HubSpot, Klaviyo, Zoho, Postmark, Mailgun, Fastmail, Proton and common custom names) |
| `resolver` | `cloudflare` or `google` | DNS-over-HTTPS resolver; the other is the fallback |

### Output (one row per domain)

```json
{
  "domain": "apify.com",
  "grade": "A",
  "score": 100,
  "canReceiveEmail": true,
  "mxProvider": "Google Workspace",
  "mx": [{ "priority": 1, "host": "aspmx.l.google.com" }],
  "spf": { "present": true, "valid": true, "record": "v=spf1 include:_spf.google.com ... -all", "allMechanism": "fail (strict)", "lookups": 6, "includes": ["_spf.google.com"], "warnings": [] },
  "dmarc": { "present": true, "policy": "reject", "subdomainPolicy": null, "percentage": 100, "aggregateReportsTo": ["dmarc@apify.com"], "alignment": { "dkim": "r", "spf": "r" }, "warnings": [] },
  "dkim": { "found": true, "selectors": [{ "selector": "google", "keyType": "rsa", "keyBits": 2048, "revoked": false }], "probed": 33 },
  "mtaSts": { "present": true, "policyMode": "enforce" },
  "tlsRpt": { "present": true },
  "bimi": { "present": false },
  "dnssec": true,
  "issues": [],
  "recommendations": ["DMARC enforced: you qualify to publish a BIMI logo record"]
}
```

Scoring: MX 20, SPF present 15 + valid 10, DMARC present 15 + quarantine 10 / reject 15, DKIM found 15, MTA-STS 5, DNSSEC 5, TLS-RPT 3, BIMI 2. Grades: A ≥ 90, B ≥ 75, C ≥ 60, D ≥ 40, else F.

### Pricing

| Event | Price |
|---|---|
| Domain checked (`domain-check`) | $0.004 |

Plus Apify's start fee ($0.00005). 250 domains = $1.00. Invalid inputs are returned as error rows and charged the same event (they still cost a lookup).

### How to use it

- **Console**: paste domains, Start, export.
- **API**: `POST https://api.apify.com/v2/acts/lathewright~email-domain-health-check/run-sync-get-dataset-items?token=<token>`
- **n8n / Make / Zapier**: Apify node → monthly schedule over your client list → rows to Google Sheets; alert when `grade` drops.

### Use from an AI agent (MCP)

Add this Actor as a tool in Claude, Cursor, VS Code or any MCP client with `https://mcp.apify.com/?tools=lathewright/email-domain-health-check`. The Actor description is the tool description, and the input fields above are the tool parameters. Example prompts: "Check the email setup of these five domains and tell me which ones will land in spam", "Does example.org have DMARC enforcement?".

### Data source and fair use

Public DNS via Cloudflare (1.1.1.1) and Google (8.8.8.8) DNS-over-HTTPS. Nothing is sent to the domain owner's servers except an HTTPS fetch of the public MTA-STS policy file when one is advertised. No mailboxes are probed, no email is sent.

### FAQ

**DKIM says "not found" but we sign our mail.** DKIM selectors are arbitrary names; pass your selector in `dkimSelectors` and the Actor will confirm the key.

**Why does SPF validity matter?** More than 10 DNS lookups or multiple SPF records cause a permanent error and receivers ignore your SPF entirely.

# Actor input Schema

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

Domains or email addresses (the domain part is used), e.g. `example.com`, `sales@acme.io`.

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

Selectors to test for DKIM records. Default covers Google, Microsoft 365, Mailchimp, SendGrid, Amazon SES, HubSpot, Klaviyo, Zoho, Postmark, Mailgun and common custom names.

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

`cloudflare` (1.1.1.1) or `google` (8.8.8.8). The other one is used as a fallback.

## `compactOutput` (type: `boolean`):

Return only the key fields (smaller, cheaper for AI agents to read).

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "github.com",
    "example.com"
  ],
  "compactOutput": false
}
```

# Actor output Schema

## `checks` (type: `string`):

One row per domain.

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

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lathewright/email-domain-health-check"
        }
    }
}
```

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/uvDpaqcWFp2Ptz3UU/builds/9dOnhBbZKCPyOPI4b/openapi.json
