# Domain Intelligence Enricher - DNS, Email, WHOIS, TLS & Tech (`herbcoder/domain-intelligence-enricher`) Actor

Bulk-enrich domains with DNS records, email provider, SPF/DKIM/DMARC grade, registrar and domain age (RDAP), TLS certificate expiry, security headers and CMS/framework hints. No proxies, no API keys. Pay per domain.

- **URL**: https://apify.com/herbcoder/domain-intelligence-enricher.md
- **Developed by:** [HerbCode LLC](https://apify.com/herbcoder) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 results

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

## Domain Intelligence Enricher - DNS, Email Security, WHOIS/RDAP, TLS and Tech Stack

Paste a list of domains (or URLs, or email addresses) and get back a complete technical profile of each one in a single row: who hosts their email, whether their SPF/DKIM/DMARC are set up properly (with an A-F grade), when the domain was registered and when it expires, whether the TLS certificate is valid and how long it has left, what the website runs on, and whether the domain is parked or dead.

Everything comes from **authoritative, public sources only** - DNS, the registries' RDAP services (the modern WHOIS), the TLS handshake and the site's own homepage. No proxies, no API keys, no rate-limited third-party services, so it is fast (about 50 domains per minute per run at default concurrency) and cheap.

### What you can do with it

- **Lead qualification / CRM enrichment** - email provider (Google Workspace vs Microsoft 365 vs self-hosted) and tech stack (Shopify, WordPress, HubSpot, Next.js...) are strong firmographic signals. Feed a list of prospect domains, get back segments.
- **Cold-email list hygiene** - drop domains that do not accept mail (`email.acceptsMail = false`), are parked, or do not resolve, before you send.
- **Email deliverability and security audits** - SPF `-all` vs `~all`, DMARC policy and reporting addresses, DKIM selectors present, MTA-STS, BIMI, DNSSEC. Sell or embed the A-F grade.
- **Domain portfolio monitoring** - expiry dates, registrar changes, EPP status locks, certificate expiry (`tls.daysToExpiry`) across hundreds of domains on a schedule.
- **Security research / attack surface** - nameservers, hosting provider hints, security headers (HSTS, CSP, X-Frame-Options...), certificate SANs.
- **Domain investing** - `rdap.registered = false` plus no DNS means the name is probably available; `domainAgeDays` and registrar for the rest.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `domains` | array of strings | required | Domains, URLs or email addresses. `https://www.example.com/x` and `jane@example.com` both become `example.com`. Duplicates removed. |
| `checkDns` | boolean | `true` | A, AAAA, CNAME, MX, NS, TXT. |
| `checkEmail` | boolean | `true` | Provider, SPF, DMARC, DKIM, MTA-STS, BIMI, grade. |
| `checkRdap` | boolean | `true` | Registrar, dates, status, nameservers, DNSSEC via RDAP. |
| `checkTls` | boolean | `true` | Certificate on port 443. |
| `checkHttp` | boolean | `true` | Homepage fetch: redirects, headers, title, technologies. |
| `dkimSelectors` | array | 20 common selectors | Selectors probed at `<selector>._domainkey.<domain>`. |
| `dnsResolver` | `cloudflare` / `google` / `system` | `cloudflare` | DNS-over-HTTPS resolver. |
| `concurrency` | integer | `8` | Domains in parallel (max 25). |
| `timeoutSecs` | integer | `12` | Per-request timeout. |
| `maxDomains` | integer | `0` (all) | Hard cap on rows = hard cap on cost. |

Example input:

```json
{
    "domains": ["apify.com", "https://www.github.com/apify", "jane@stripe.com", "example.org"],
    "checkHttp": true
}
```

### Output

One dataset item per domain. Sections you turned off are `null`; individual check failures are listed in `errors` instead of failing the row.

```json
{
    "domain": "apify.com",
    "input": "apify.com",
    "resolves": true,
    "dns": {
        "a": ["13.32.99.86", "13.32.99.116"],
        "aaaa": [],
        "cname": [],
        "mx": [{ "priority": 1, "exchange": "aspmx.l.google.com" }, { "priority": 5, "exchange": "alt1.aspmx.l.google.com" }],
        "ns": ["ns-1099.awsdns-09.org", "ns-1745.awsdns-26.co.uk"],
        "txt": ["v=spf1 include:_spf.google.com include:servers.mcsv.net -all", "google-site-verification=..."]
    },
    "email": {
        "acceptsMail": true,
        "provider": "Google Workspace",
        "mxHosts": ["aspmx.l.google.com", "alt1.aspmx.l.google.com"],
        "spf": { "present": true, "record": "v=spf1 include:_spf.google.com include:servers.mcsv.net -all", "allMechanism": "-all", "includes": ["_spf.google.com", "servers.mcsv.net"], "lookups": 2 },
        "dmarc": { "present": true, "record": "v=DMARC1; p=reject; rua=mailto:dmarc@apify.com", "policy": "reject", "subdomainPolicy": "reject", "pct": 100, "rua": ["mailto:dmarc@apify.com"], "ruf": [] },
        "dkim": { "selectorsChecked": 20, "selectorsFound": ["google"], "wildcard": false },
        "mtaSts": true,
        "bimi": true,
        "security": { "score": 100, "grade": "A" }
    },
    "rdap": {
        "registered": true,
        "registrar": "Amazon Registrar, Inc.",
        "registrarIanaId": "468",
        "registrantOrg": null,
        "registrantCountry": null,
        "createdAt": "2009-06-02T14:52:47Z",
        "updatedAt": "2025-05-03T01:18:23Z",
        "expiresAt": "2035-06-02T14:52:47Z",
        "status": ["client transfer prohibited"],
        "nameservers": ["ns-1099.awsdns-09.org", "ns-1745.awsdns-26.co.uk"],
        "dnssec": true,
        "source": "https://rdap.verisign.com/com/v1/domain/apify.com"
    },
    "domainAgeDays": 6320,
    "tls": {
        "reachable": true,
        "valid": true,
        "error": null,
        "protocol": "TLSv1.3",
        "subject": "apify.com",
        "issuer": "Amazon",
        "validFrom": "2026-01-16T00:00:00.000Z",
        "validTo": "2027-01-16T23:59:59.000Z",
        "daysToExpiry": 117,
        "sans": ["apify.com", "*.apify.com"],
        "fingerprint256": "AB:CD:..."
    },
    "http": {
        "reachable": true,
        "status": 200,
        "finalUrl": "https://apify.com/",
        "redirected": false,
        "finalHost": "apify.com",
        "responseMs": 412,
        "server": "AmazonS3",
        "poweredBy": null,
        "contentType": "text/html; charset=utf-8",
        "title": "Apify: Full-stack web scraping and data extraction platform",
        "metaDescription": "Cloud platform for web scraping, browser automation, and data for AI...",
        "generator": null,
        "language": "en",
        "technologies": ["AWS CloudFront", "HubSpot", "Next.js", "Google Tag Manager", "Intercom"],
        "securityHeaders": { "strictTransportSecurity": "max-age=63072000", "contentSecurityPolicy": null, "xFrameOptions": "SAMEORIGIN", "xContentTypeOptions": "nosniff", "referrerPolicy": null, "permissionsPolicy": null },
        "parked": false
    },
    "errors": [],
    "checkedAt": "2026-09-21T17:02:11.104Z"
}
```

The Overview tab shows the most useful columns (provider, email grade, DMARC policy, registrar, registered/expiry dates, TLS days left, HTTP status, title, technologies); the full nested record is available as JSON, CSV (flattened), Excel or via the API.

#### Email security grade

| Signal | Points |
|---|---|
| SPF record present | 20 (+10 for `-all`, +5 for `~all`) |
| DMARC record present | 20 (+20 for `p=reject`, +12 for `p=quarantine`) |
| At least one DKIM selector found | 15 |
| MTA-STS policy record | 10 |
| DNSSEC signed (from RDAP) | 5 |

A = 85+, B = 65+, C = 45+, D = 25+, F below.

### Pricing

Pay per event:

| Event | Price | What it means |
|---|---|---|
| Actor start | $0.005 | Once per run. |
| `domain-result` | $0.004 (= $4 per 1,000 domains) | One per domain written to the dataset, regardless of how many checks you enable. |

1,000 domains with every check enabled cost about $4.01. Comparable single-purpose Actors charge $3 per 1,000 for DNS + WHOIS only; this one adds email security grading, TLS, HTTP and technology detection in the same row.

### Limits and notes

- RDAP coverage: all gTLDs (.com, .net, .org, .io, .ai, .app, ...) and most ccTLDs publish RDAP. A few ccTLDs (for example .de, .es, .au use RDAP; .ch, .ru, .jp currently do not) return `errors: ["rdap: ..."]` and `rdap: null`. Registrant name/org/country is usually redacted for privacy.
- DKIM can only be detected for selectors you probe; the default list covers Google, Microsoft, Mailchimp/Mandrill, Postmark, Proton, Fastmail, Zendesk and common custom names. Add your own in `dkimSelectors`.
- Technology detection is fingerprint-based (script URLs, markup, headers) - it identifies common CMSs, frameworks, analytics and hosting, not every library on the page.
- `email.acceptsMail` means MX records exist; it does not perform SMTP mailbox verification.
- Uses DNS-over-HTTPS (Cloudflare or Google) with a fallback to the system resolver, so results are consistent regardless of where the Actor runs.

### FAQ

**Is this allowed?** Yes. DNS, RDAP and TLS are public protocols designed to be queried; the HTTP check fetches only the homepage with a normal browser-like request. No personal data is collected beyond what registries publish (and they redact most of it).

**How fast is it?** Each domain needs ~30 small requests, run in parallel. Expect roughly 50-100 domains per minute at concurrency 8-16.

**Can I run it from an AI agent?** Yes - use it through the Apify MCP server or the API; the flat top-level fields (`resolves`, `email.provider`, `email.security.grade`, `rdap.expiresAt`, `tls.daysToExpiry`, `http.technologies`) are designed to be easy to reason over.

# Changelog

This Actor's version history is a separate document: https://apify.com/herbcoder/domain-intelligence-enricher/changelog.md

# Actor input Schema

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

Domains to enrich. URLs (https://www.example.com/page) and email addresses (jane@example.com) are accepted and reduced to the domain. Duplicates are removed.

## `checkDns` (type: `boolean`):

A, AAAA, CNAME, MX, NS and TXT records.

## `checkEmail` (type: `boolean`):

Email provider (Google Workspace, Microsoft 365, ...), SPF, DMARC, DKIM selectors, MTA-STS, BIMI and an A-F email security grade.

## `checkRdap` (type: `boolean`):

Registrar, creation/expiry dates, domain age, EPP status codes, nameservers and DNSSEC from the registry's RDAP service.

## `checkTls` (type: `boolean`):

Certificate validity, issuer, expiry (days left), SANs and TLS protocol on port 443.

## `checkHttp` (type: `boolean`):

Fetch the homepage: final URL after redirects, status, server, title, meta description, security headers, CMS/framework/analytics hints, parked-domain detection.

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

Selectors checked at <selector>.\_domainkey.<domain>. The defaults cover Google, Microsoft, Mailchimp/Mandrill, Postmark, Proton, Fastmail, Zendesk and common custom names.

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

DNS-over-HTTPS resolver used for lookups (falls back to the system resolver on failure).

## `concurrency` (type: `integer`):

Domains processed in parallel.

## `timeoutSecs` (type: `integer`):

Maximum time for each individual DNS / RDAP / TLS / HTTP request.

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

Process at most this many domains (0 = all). Caps your cost.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "github.com",
    "stripe.com",
    "example.org"
  ],
  "checkDns": true,
  "checkEmail": true,
  "checkRdap": true,
  "checkTls": true,
  "checkHttp": true,
  "dkimSelectors": [
    "default",
    "google",
    "selector1",
    "selector2",
    "k1",
    "k2",
    "mail",
    "dkim",
    "s1",
    "s2",
    "mandrill",
    "pm",
    "protonmail",
    "fm1",
    "zendesk1",
    "mailo",
    "krs",
    "smtp",
    "sig1",
    "everlytickey1"
  ],
  "dnsResolver": "cloudflare",
  "concurrency": 8,
  "timeoutSecs": 12,
  "maxDomains": 0
}
```

# Actor output Schema

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

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("herbcoder/domain-intelligence-enricher").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",
        "stripe.com",
        "example.org",
    ] }

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

```

## MCP server setup

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

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/UWVeBRN2QVfQn7dat/builds/2Un5M0bNnkNOHbfGd/openapi.json
