# DNS & Email Security Checker - SPF, DMARC, DKIM (`antishock/dns-email-security-checker`) Actor

Check domains in bulk for DNS and email authentication: A, AAAA, MX, NS, TXT records plus SPF, DMARC and DKIM with policy parsing, an email security score and inferred DNS and mail provider. For deliverability audits, security assessments and B2B technology targeting.

- **URL**: https://apify.com/antishock/dns-email-security-checker.md
- **Developed by:** [Ryan Zinburg](https://apify.com/antishock) (community)
- **Categories:** Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 result exporteds

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

## DNS & Email Security Checker - SPF, DMARC, DKIM & MX in Bulk

Check any list of domains for their **DNS configuration and email authentication posture**: A and AAAA records, nameservers, MX hosts, SPF, DMARC and DKIM, plus a guess at which DNS and mail provider they use.

No API key, no proxy, no rate limits: it queries DNS directly.

### What you get per domain

| Field | Example |
|---|---|
| `domain` | example.com |
| `resolves` | whether the domain has address records at all |
| `ipv4`, `ipv6` | A and AAAA records |
| `nameservers`, `dnsProvider` | ns1.example.net, Cloudflare |
| `mxHosts`, `hasMx` | \["10 aspmx.l.google.com"] |
| `hasNullMx` | domain explicitly declares it accepts no mail (RFC 7505) |
| `mailProvider` | Google Workspace, Microsoft 365, Proofpoint, ... |
| `spfRecord`, `hasSpf`, `spfPolicy` | the record and whether it ends in `-all` or `~all` |
| `dmarcRecord`, `hasDmarc`, `dmarcPolicy` | the record and `none`, `quarantine` or `reject` |
| `dmarcReportingAddress` | the `rua=` address collecting DMARC reports |
| `dkimSelectorsFound`, `hasDkim` | which common selectors publish a key |
| `emailSecurityScore` | 0 to 3, one point each for SPF, DMARC and DKIM |
| `txtRecords` | all TXT records, useful for verification tokens |
| `soaEmail` | the zone's hostmaster address |

### Input

- **domains** - the domains to check, separated by commas, spaces or newlines
- **dkimSelectors** - which DKIM selectors to probe. Defaults to the common ones: `google`, `selector1`, `selector2`, `k1`, `default`, `dkim`, `mail`
- **maxResults** - cap on how many domains to process

### Example input

```json
{
  "domains": "example.com, stripe.com, github.com",
  "dkimSelectors": "google,selector1,selector2,k1,default"
}
```

### Use cases

- **Email deliverability audits** - find which domains in a portfolio still lack SPF, DMARC or DKIM
- **Security assessments** - a DMARC policy of `p=none` means the domain can be spoofed with no enforcement, and this reports it in one column
- **Lead generation for security and email vendors** - a list of companies with `emailSecurityScore` below 2 is a qualified prospect list
- **Vendor and technology mapping** - `mailProvider` and `dnsProvider` reveal whether a company runs Google Workspace, Microsoft 365 or something else, which is strong B2B targeting signal
- **M\&A and due diligence** - assess the DNS and email hygiene of an acquisition target from outside
- **Phishing defence preparation** - identify your own domains that are not yet protected

### Why these three records matter

SPF says which servers may send for a domain. DKIM signs the messages. DMARC tells receivers what to do when the first two fail, and where to report it. A domain with SPF only is still trivially spoofable in practice, because nothing instructs receivers to act.

`dmarcPolicy` is therefore the single most informative field here: `reject` means enforcement, `quarantine` means partial, `none` means monitoring only, and a missing record means nothing at all.

### Notes

- DKIM keys live under selector names that are chosen per sending service, so absence of a selector is not proof that DKIM is unused. The list of probed selectors covers the common providers; add your own if you know them.
- `mailProvider` and `dnsProvider` are inferred from hostname patterns and stay empty for self-hosted or unusual setups.
- `txtRecords` often contains domain verification tokens for SaaS products, which is another useful technology signal.
- A `hasNullMx` domain publishes an empty MX on purpose to say it receives no email. Treat those as unreachable rather than misconfigured.
- DKIM detection requires a real public key in the record. A selector publishing `v=DKIM1; p=` with an empty key is a revoked selector, not protection, and is reported as absent.
- Everything here is public DNS data; the actor sends no mail and connects to no mail server.

# Actor input Schema

## `domains` (type: `string`):

Domains to check, separated by commas, spaces or newlines.

## `dkimSelectors` (type: `string`):

Selectors to probe for DKIM keys, comma separated.

## `maxResults` (type: `integer`):

How many domains to process.

## Actor input object example

```json
{
  "domains": "stripe.com, github.com, example.com",
  "dkimSelectors": "google,selector1,selector2,k1,default,dkim,mail",
  "maxResults": 1000
}
```

# Actor output Schema

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

Scraped records in the default dataset.

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

// Run the Actor and wait for it to finish
const run = await client.actor("antishock/dns-email-security-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": "stripe.com, github.com, example.com" }

# Run the Actor and wait for it to finish
run = client.actor("antishock/dns-email-security-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": "stripe.com, github.com, example.com"
}' |
apify call antishock/dns-email-security-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,antishock/dns-email-security-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/PFNv0WVChMPTmvmw7/builds/EZo5d3yaKxvRRq1K8/openapi.json
