# SPF Record Checker - Validate SPF & Count DNS Lookups (`eliai/spf-record-checker`) Actor

Validate SPF for up to 200 domains per run: parsed mechanisms, DNS lookup count vs the RFC 7208 limit of 10, all-qualifier and plain-English issues. $0.0008 per domain, no start fee, failures never charged — cheaper than measured incumbents ($0.001+/item). For deliverability and security audits.

- **URL**: https://apify.com/eliai/spf-record-checker.md
- **Developed by:** [Broke to Built](https://apify.com/eliai) (community)
- **Categories:** Developer tools, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.64 / 1,000 spf-checked domains

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## SPF Record Checker

Validate any domain's SPF record and catch the failure everyone misses: the 10-DNS-lookup limit. Check one domain or up to 200 per run, online, by API, or as an agent tool via Apify MCP.

This actor fetches the `v=spf1` TXT record, parses every mechanism, counts DNS lookups against RFC 7208's hard limit of 10 (exceed it and receivers return PermError — your SPF silently stops working), and flags permissive or missing `all` qualifiers. Useful for deliverability audits, cold-email domain setup, security reviews, and lead generation (find domains with broken SPF).

### What you get

- **hasSpf** — is a `v=spf1` record published?
- **record** — the raw SPF string
- **mechanisms** — every term parsed: qualifier (`+`/`-`/`~`/`?`), type (`ip4`, `include`, `mx`, …), value, and whether it costs a DNS lookup
- **lookupCount** — total DNS lookups vs the RFC 7208 limit of 10
- **allQualifier** — how unmatched senders are treated (`-all` fail, `~all` softfail, `?all` neutral, `+all` open)
- **issues** — plain-English flags: no record, multiple records (an RFC error), over the lookup limit, `+all`/`?all`, missing `all`
- **Fail-soft**: an invalid domain or a DNS error never fails the run — it returns `{ok: false, error}` and is **never charged**.

### Input

```json
{ "domains": ["github.com", "microsoft.com"], "maxDomains": 50 }
```

Or a single domain: `{ "domain": "google.com" }`

### Output (real run)

```json
{
  "domain": "github.com",
  "ok": true,
  "hasSpf": true,
  "record": "v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com include:_netblocks.google.com ... ~all",
  "mechanisms": [
    { "qualifier": "+", "type": "ip4", "value": "192.30.252.0/22", "lookup": false },
    { "qualifier": "+", "type": "include", "value": "spf.protection.outlook.com", "lookup": true }
  ],
  "lookupCount": 8,
  "allQualifier": "~",
  "issues": []
}
```

### Pricing

**$0.0008 per domain checked.** No start fee. Invalid domains and DNS failures are never charged.

Measured against store incumbents (2026-08-07): andok/dmarc-spf-dkim-validator charges $0.001 per item, blackfalcondata/email-deliverability-checker $0.00005 start + $0.001 per item, davidbenittah/dmarc-rfc9989-auditor $0.005 start + $0.005 per item. A 100-domain sweep here costs $0.08.

### Honest limits

- Lookup counting is static: it counts lookup-type mechanisms in the top-level record (`include`, `a`, `mx`, `ptr`, `exists`, `redirect`). It does not recursively resolve nested includes, so a record that nests its way over the limit can pass here — the top-level count is what most audits need first.
- DNS-only: no test emails are sent; this validates the published record, not your mail server's behavior.
- One record per domain: when multiple SPF records exist (an RFC violation, flagged in issues), the first is parsed.

### FAQ

**Why does the 10-lookup limit matter?**
RFC 7208 caps SPF evaluation at 10 DNS lookups. Past that, receivers return PermError and your SPF effectively fails — a very common silent misconfiguration after adding several SaaS senders.

**What's the difference between ~all and -all?**
`-all` tells receivers to fail unmatched senders outright; `~all` marks them suspicious (softfail). `?all` and especially `+all` provide no real protection and are flagged.

**Does it check DKIM or DMARC too?**
No — this is a focused SPF tool. Pair it with our DMARC Record Checker and MX Record Checker for the full email-auth picture.

**Can I paste full URLs?**
Yes. `https://www.example.com/path` is cleaned to `example.com` automatically; duplicates are removed.

**Why did some rows come back `ok: false`?**
Those inputs were not valid domains or their DNS lookup failed transiently. They are recorded for completeness and never charged.

### Use from code or AI agents

```bash
curl -s "https://api.apify.com/v2/acts/EliAI~spf-record-checker/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -X POST -H 'Content-Type: application/json' \
  -d '{"domains": ["yourdomain.com", "competitor.com"]}'
```

Agents: connect [Apify MCP](https://mcp.apify.com) and call the `EliAI/spf-record-checker` tool.

- **Capability:** fetch, parse and lint the SPF record of one or many domains, returned as JSON
- **Required input:** `domain` (string) or `domains` (array)
- **Returns:** one JSON record per domain; `lookupCount` + `issues` summarize the verdict
- **Bounded:** `maxDomains` caps the run; failures isolate per domain
- **Side effects:** none

# Actor input Schema

## `domain` (type: `string`):

A single domain to check the SPF record for (e.g. google.com).

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

Multiple domains to check in one run. Add one domain per line.

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

Maximum number of domains to check per run (safety cap).

## Actor input object example

```json
{
  "domain": "google.com",
  "domains": [
    "github.com",
    "microsoft.com"
  ],
  "maxDomains": 50
}
```

# Actor output Schema

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

Every item this run produced, as JSON.

## `resultsCsv` (type: `string`):

The same items as a spreadsheet-ready CSV.

# 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 = {
    "domain": "google.com",
    "domains": [
        "github.com",
        "microsoft.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("eliai/spf-record-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 = {
    "domain": "google.com",
    "domains": [
        "github.com",
        "microsoft.com",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("eliai/spf-record-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 '{
  "domain": "google.com",
  "domains": [
    "github.com",
    "microsoft.com"
  ]
}' |
apify call eliai/spf-record-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,eliai/spf-record-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/6DiE7eGtHGQXbLvyv/builds/fwOERv7JShjhm12fb/openapi.json
