# Domain Intelligence: WHOIS, DNS, SPF & DMARC Check (`f0rty7even/domain-intelligence`) Actor

One row per domain: registrar, registration and expiry dates, domain age, nameservers and DNS host, MX records and mail provider, plus a full SPF, DKIM, DMARC and MTA-STS audit with a plain answer on whether the domain can be spoofed.

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

## Pricing

from $2.00 / 1,000 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/platform/actors/running/actors-in-store#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

## Domain Intelligence: WHOIS, DNS, SPF and DMARC Check

Give it a list of domains. Get back one row each covering who registered it and when, where its DNS and mail point, and whether anyone can forge email from it.

Two jobs, one lookup. For sales it is firmographics: domain age, registrar, DNS host and whether a company runs Google Workspace or Microsoft 365. For security it is a spoofing audit: SPF, DKIM, DMARC and MTA-STS, with a plain verdict instead of a wall of raw records.

**$3 per 1,000 domains.** Unregistered domains are reported and never charged.

***

### What you get for every domain

**Registration**

| Field | Example |
|---|---|
| `registrar` | `Amazon Registrar, Inc.` |
| `createdAt`, `expiresAt`, `updatedAt` | `2009-06-02T17:14:10+00:00` |
| `domainAgeDays` | `6277` |
| `expiresInDays` | `3219` |
| `isNewlyRegistered` | `false` (under 90 days old) |
| `isExpiringSoon` | `false` (under 30 days left) |
| `domainStatus` | `["client transfer prohibited"]` |
| `isDnssecSigned` | `true` |
| `registrantOrganization`, `registrantCountry` | where the registry publishes them |

**DNS and mail**

| Field | Example |
|---|---|
| `nameservers` | `["ns-1225.awsdns-25.org", ...]` |
| `dnsProvider` | `Amazon Route 53` |
| `aRecords` | `["76.76.21.21"]` |
| `hasMxRecord`, `mxHosts` | `true`, `["aspmx.l.google.com"]` |
| `mailProvider` | `Google Workspace` |

**Email authentication**

| Field | Example |
|---|---|
| `emailSecurityGrade` | `A` to `F` |
| `spoofProtection` | `protected`, `partially_protected` or `spoofable` |
| `spoofReason` | `DMARC policy is reject` |
| `isSpoofable` | `false` |
| `spfRecord`, `spfPolicy`, `spfLookupCount` | `v=spf1 ... -all`, `fail`, `4` |
| `dmarcRecord`, `dmarcPolicy`, `dmarcSubdomainPolicy`, `dmarcPercentage` | `reject`, `none`, `100` |
| `dmarcReportsTo` | `["dmarc@northwind.co"]` |
| `hasDkim`, `dkimSelector` | `true`, `google` |
| `hasMtaSts` | `true` |
| `issues` | `["monitoring_only_policy"]` |

***

### The spoofing verdict, and why SPF alone is not enough

The question worth answering is not "does this domain have SPF". It is "can somebody send mail as this domain and have it delivered".

SPF alone does not stop that. SPF tells a receiving server which hosts are allowed to send, and then says nothing about what to do when the check fails. **DMARC is the part that instructs the receiver to reject.** A domain with a perfect SPF record and no DMARC is still spoofable, and that combination is extremely common.

| Verdict | When |
|---|---|
| `protected` | DMARC policy is `reject`, applied to all mail |
| `partially_protected` | DMARC is `quarantine`, or `reject` at less than 100% |
| `spoofable` | DMARC is missing, or present but set to `none`, which enforces nothing |

`spoofReason` always says which of these applied, in a sentence you can paste into a report.

The `issues` array flags the specific problems worth fixing: `monitoring_only_policy`, `no_aggregate_reporting`, `permissive_all_mechanism`, `too_many_dns_lookups`, `partial_enforcement`, `no_dkim_found`, `expiring_soon`.

**The SPF lookup count matters more than it looks.** SPF allows ten DNS lookups. Go over and receivers treat the record as an error and ignore it, which silently undoes the whole setup. Counting them is one line of output and a frequent real finding.

***

### Registration data comes from RDAP, not WHOIS

RDAP is the IANA-designated replacement for WHOIS: the same registry data delivered as structured JSON over HTTPS rather than free text over port 43.

That matters for accuracy. Every WHOIS library on earth is a pile of per-registry text parsing that breaks when a registry changes its output. RDAP returns typed fields, so dates are dates and the registrar is the registrar.

Some registries publish less than others. Several country registries, `.uk` among them, return status and nameservers but no registration date, and most registrant contact details are redacted worldwide under GDPR. Those fields come back `null` rather than guessed at.

***

### Common uses

**Pre-send domain hygiene.** Before a campaign, check that your own domains and your clients' are actually protected. `emailSecurityGrade` and `issues` turn into a fix list.

**Security assessments and pitches.** A list of a prospect's domains, run through this, produces a defensible finding: these three are spoofable and here is why. This is the single most common quick win in an email-security engagement.

**Vendor and supply-chain review.** Check whether the companies you exchange invoices with can be impersonated, which is exactly the vector behind invoice fraud.

**Lead qualification.** `mailProvider` and `dnsProvider` tell you what a prospect runs before you call. Anyone selling into the email, DNS or security stack qualifies on those two fields.

**Domain portfolio monitoring.** `expiresInDays` and `isExpiringSoon` catch a renewal you were about to miss across hundreds of domains.

**Fraud and abuse triage.** `isNewlyRegistered` is a strong signal: a domain registered nine days ago that is already sending you invoices deserves a second look.

***

### Pricing

**$0.003 per domain, so $3 per 1,000.** One charge per domain that exists.

Not charged when a domain is not registered, which the run reports with `error: domain_not_registered`. Duplicates are removed before anything runs, and URLs and email addresses are reduced to their domain first, so the same company supplied three ways costs once.

Tier discounts apply automatically on paid Apify plans, down to $2 per 1,000.

***

### Honest limits

**DKIM cannot be enumerated.** There is no DNS query that lists a domain's DKIM selectors. This probes the selectors the major providers use and reports which one answered, so `hasDkim: true` is proof and `hasDkim: false` means "not found among the common selectors", not "not configured". `dkimSelector` shows exactly what was found.

**Registrant details are usually redacted.** GDPR removed most contact data from public registration records. What survives is generally the organisation on business domains and nothing on personal ones.

**A grade is a summary, not an audit.** It scores the presence and strength of SPF, DMARC, DKIM and MTA-STS. It does not read your DMARC reports or check that your sending sources are actually aligned.

***

### Frequently asked questions

**How do I check if a domain has SPF and DMARC?**
Put the domain in. `spfRecord` and `dmarcRecord` return the raw records, and `spfPolicy` and `dmarcPolicy` return the interpreted versions.

**Can someone spoof my domain?**
`isSpoofable` answers that directly and `spoofReason` explains it. In short: without DMARC set to quarantine or reject, yes.

**What is a good DMARC policy?**
`p=reject` with `pct=100` and an `rua` address collecting reports. `p=none` monitors and blocks nothing, which is why it is flagged as an issue here rather than treated as a pass.

**How do I find out when a domain expires?**
`expiresAt` and `expiresInDays`, where the registry publishes them.

**Can I use this to check who a company's email provider is?**
Yes. `mailProvider` is derived from the MX records and names Google Workspace, Microsoft 365, Proofpoint, Mimecast, Zoho and around 25 others.

**Is this WHOIS?**
It is the modern equivalent. RDAP is the official successor and returns the same registry data as structured JSON.

**Do you query my domains from my IP?**
No. DNS goes out from Apify's infrastructure and RDAP lookups go through the proxy you configure.

***

### Works well with

- [Bulk Email Verifier](https://apify.com/f0rty7even/email-verifier): verify individual addresses at those domains
- [Website Lead Extractor](https://apify.com/f0rty7even/website-lead-extractor): turn the same domains into emails, phones and postal addresses
- [Website Tech Stack Detector](https://apify.com/f0rty7even/tech-stack-detector): what those sites are built and sold on

***

### Notes

Reads public DNS records and public registry data only. Nothing is sent to the domains themselves and no mail is transmitted.

# Actor input Schema

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

Hostnames, full URLs or email addresses; anything is reduced to its domain. Paste one per line, comma separated, or a whole spreadsheet column.

## `includeRegistration` (type: `boolean`):

Fetch the registrar, registration and expiry dates, domain age and registrant details over RDAP, the modern replacement for WHOIS. Turn off for a DNS-only run, which is faster.

## `probeDkim` (type: `boolean`):

DKIM selectors cannot be listed from DNS, so this tries the selectors the major providers use and reports which one answered. A domain signing with a private selector will show as not found.

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

Hard stop after this many domains, as a safety net on large inputs. Leave empty to check everything you supplied.

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

How many domains to check at once.

## `proxyConfiguration` (type: `object`):

Proxy used for the RDAP registration lookups. DNS queries do not go through it.

## Actor input object example

```json
{
  "domains": [
    "northwind.co",
    "https://contoso.com",
    "jane@fabrikam.io"
  ],
  "includeRegistration": true,
  "probeDkim": true,
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per domain. Export as JSON, JSONL, CSV or Excel.

# 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",
        "hetzner.com",
        "paypal.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("f0rty7even/domain-intelligence").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",
        "hetzner.com",
        "paypal.com",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("f0rty7even/domain-intelligence").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",
    "hetzner.com",
    "paypal.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call f0rty7even/domain-intelligence --silent --output-dataset

```

## MCP server setup

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

```

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/SLju8E4kHls8Dyil5/builds/EvJM0gSe5ftTeAXeA/openapi.json
