# Domain Intel — WHOIS (RDAP), DNS, Email Security, SSL (`yadroo/domain-intel`) Actor

Domain intelligence for security, due diligence and AI agents: registrar, dates and status via RDAP, DNS over HTTPS, SPF/DMARC/DKIM audit, certificate history and subdomains from CT logs, website security headers and tech, risk flags. No API key.

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

## Pricing

from $3.50 / 1,000 result items

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?

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

One JSON profile per domain: registration data from RDAP (the WHOIS successor — registrar, dates, EPP status, DNSSEC, abuse contact), DNS records over DoH, a full **email-security audit** (MX provider, SPF, DMARC, DKIM, MTA-STS, BIMI), certificate history and **sub-domain discovery** from Certificate Transparency, plus a website probe with security-header grade and tech detection. Every row ends with machine-readable risk `flags`. Built for security teams, domain portfolio managers, sales-ops enrichment and AI agents.

No API key, no proxy, no browser — only public sources (IANA/registry RDAP, Google/Cloudflare DoH, crt.sh, the site itself). Made by Yadroo.

### Use cases

- **Vendor / counterparty due diligence** — domain age, registrar, redacted registrant, DMARC posture and HTTPS hygiene in one row before you sign or pay.
- **Phishing & brand-abuse triage** — `newly_registered`, `no_dmarc`, `spf_permissive`, look-alike domains resolved and profiled in bulk.
- **Portfolio renewal monitoring** — run weekly on your own domains; alert on `expires_soon`, `transfer_unlocked`, `on_hold`, `dnssec_off`.
- **Email deliverability audits** — SPF lookup count, `all` qualifier, DMARC policy/pct/rua, DKIM selectors present, MTA-STS — for every client domain of an agency or MSP.
- **Attack-surface / sub-domain inventory** — every hostname that ever got a certificate (`subdomains`), issuers used, wildcard certs.
- **Lead enrichment** — mail provider (Google Workspace / Microsoft 365…), CDN, CMS/framework, tag manager, HubSpot/Intercom presence to segment prospects.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `domains` | string\[] | — | Domains, hosts or URLs; normalized (`https://www.Example.com/x` → `example.com`, IDN → punycode). Invalid entries, IP addresses and internal names (`localhost`, `*.local`, `*.internal`…) are skipped with a warning; duplicates collapsed. Max 500 — how many finish in one run depends on the modules (see Limits). |
| `includeRdap` | boolean | `true` | Registrar, IANA id, abuse email/phone, registration / expiry / update dates, `domainAgeDays`, `daysToExpiry`, EPP `status`, `dnssec`, `nameservers`. Sub-domains are walked up to the registered name. |
| `includeDns` | boolean | `true` | Records for `dnsRecordTypes` via DoH → `dns`, `dnsStatus`, `ipAddresses`. |
| `dnsRecordTypes` | string\[] | `A, AAAA, CNAME, MX, NS, TXT, SOA, CAA` | Any of `A AAAA CNAME MX NS TXT SOA CAA SRV PTR DS DNSKEY TLSA HTTPS SVCB NAPTR`. |
| `dnsResolver` | `google` | `cloudflare` | `google` | Public DoH resolver. |
| `includeEmailSecurity` | boolean | `true` | MX + provider, SPF & DMARC parsed with issues, DKIM selector probe, MTA-STS, BIMI → `email`, plus the shortcuts `spfRecord`, `dmarcPolicy`, `mailProvider`, `spfAllQualifier`, `spfLookupTerms`, `dmarcPct`, `dkimSelectorsFound`, `mtaSts`. |
| `dkimSelectors` | string\[] | 15 common selectors | Probed at `<selector>._domainkey.<domain>`; max 30. See Reference. |
| `includeCerts` | boolean | `true` | CT history from crt.sh → `certificates`, `certificatesTotal`, `certificateIssuers`, `latestCertificate`. The slowest module (see Limits): turn it off for big lists. |
| `certsLimit` | integer 0–1000 | `50` | Newest certificates kept (deduplicated). |
| `certsExcludeExpired` | boolean | `false` | Only valid certs (faster; historic sub-domains disappear). |
| `includeSubdomains` | boolean | `true` | Hostnames under the domain seen in certificates → `subdomains`, `subdomainsFound`. Needs `includeCerts` (with certificates off it does nothing). |
| `subdomainsLimit` | integer 0–10000 | `500` | Cap on the `subdomains` array. |
| `includeHttp` | boolean | `true` | Website probe → `http` (status, final URL, redirect chain, title, server, HTTPS upgrade, security headers A–F, technologies), plus the shortcuts `httpStatus`, `websiteTitle`, `securityGrade`, `technologies`, `finalUrl`, `missingSecurityHeaders`, `responseMs`. Never connects to private or reserved addresses (see Limits). |
| `httpTimeoutSecs` | integer 3–60 | `15` | Timeout of each probe request: the HTTPS homepage with its redirects, the plain-HTTP fallback, the HTTP→HTTPS upgrade check. |
| `expiresWithinDays` | integer | `30` | Threshold for the `expires_soon` flag. |
| `newlyRegisteredDays` | integer | `90` | Threshold for the `newly_registered` flag. |
| `fields` | string\[] | — | Keep only these top-level fields, in this order. `domain` and `fetchedAt` are always kept (first, unless you list them). Letter case is corrected; unknown names are ignored with a warning in `SUMMARY.warnings` (a nested path like `email.provider` → use the top-level `mailProvider`). A list with no known name, or only fields of switched-off modules, fails the run before any domain is looked up (only the run start is charged). |
| `requestDelayMs` | integer 0–5000 | `200` | Minimum gap between two requests to the same RDAP server or crt.sh. Lower values are raised to each service's published per-IP limit: crt.sh and rdap.org 1 s, registry RDAP servers 0.5 s. DNS-over-HTTPS queries are paced separately (at most 20 at a time, 100/s). |

At least one module (`includeRdap`, `includeDns`, `includeEmailSecurity`, `includeCerts`, `includeHttp`) must be on; with all of them off a row would hold only the domain name, so such an input fails the run before any domain is looked up (only the run start is charged).

### Reference

#### Risk flags (`flags`)

| flag | meaning |
|---|---|
| `expired`, `expires_soon` | registration already past / within `expiresWithinDays` |
| `newly_registered` | younger than `newlyRegisteredDays` — phishing/fraud signal |
| `on_hold`, `pending_delete` | EPP status contains *hold* / *redemption* / *pendingDelete* |
| `transfer_unlocked` | no `clientTransferProhibited`/`serverTransferProhibited` — hijack risk |
| `dnssec_off` | delegation not signed |
| `registrant_redacted` | no registrant name/org in RDAP (GDPR redaction or privacy proxy) |
| `rdap_unavailable` | TLD without RDAP or lookup failed (see `rdapError`) |
| `nxdomain`, `no_web_records` | name does not exist / no A, AAAA or CNAME |
| `no_mx` | domain cannot receive mail |
| `no_spf`, `spf_permissive` (`+all`/`?all`), `spf_softfail` (`~all`), `spf_too_many_lookups` (> 10) | SPF posture |
| `no_dmarc`, `dmarc_monitor_only` (`p=none`) | DMARC posture |
| `http_unreachable`, `no_https`, `http_not_redirected`, `no_hsts` | website posture |
| `wildcard_cert` | a currently valid wildcard certificate exists |

#### EPP status codes seen in `status`

`clientTransferProhibited`, `clientDeleteProhibited`, `clientUpdateProhibited`, `clientRenewProhibited`, `serverTransferProhibited`, `serverDeleteProhibited`, `serverUpdateProhibited`, `clientHold`, `serverHold`, `redemptionPeriod`, `pendingDelete`, `pendingTransfer`, `pendingRenew`, `inactive`, `ok`/`active`. Definitions: [ICANN EPP status codes](https://www.icann.org/resources/pages/epp-status-codes-2014-06-16-en).

#### Security-header grade

Points: HSTS max-age ≥ 180 d = 2 (any HSTS = 1), CSP = 2, X-Frame-Options or CSP `frame-ancestors` = 1, `X-Content-Type-Options: nosniff` = 1, Referrer-Policy = 1, Permissions-Policy = 1. Grade: A ≥ 7, B ≥ 5, C ≥ 3, D ≥ 1, else F. `http.security.missing` lists what to add.

#### Default DKIM selectors and who uses them

| selector | provider | selector | provider |
|---|---|---|---|
| `google` | Google Workspace | `selector1`, `selector2` | Microsoft 365 |
| `k1` | Mailchimp / Mandrill | `mandrill` | Mandrill |
| `pm` | Postmark | `krs` | Klaviyo |
| `mailgun`, `smtp`, `mail`, `s1`, `dkim`, `default` | Mailgun / generic | `zoho` | Zoho Mail |
| `amazonses` | Amazon SES | | |

#### RDAP coverage

All gTLDs (.com, .net, .org, .app, .dev…) and the ccTLDs whose registry is in the [IANA bootstrap](https://data.iana.org/rdap/dns.json) (e.g. `.ai`, `.tv`, `.uk`). Registries without RDAP — e.g. `.io`, `.co`, `.us`, `.kz`, `.ru`, `.de`, `.ch`, `.jp` (checked in cloud runs on 2026-10-02) — return `rdapError` and the `rdap_unavailable` flag; DNS, email, certificates and HTTP still work for them. A run with only `includeRdap` on gets no row for such a domain (listed in `SUMMARY.errors`, not charged). Technologies detected: Cloudflare, AWS CloudFront, Vercel, Netlify, GitHub Pages, Akamai, Fastly, nginx, Apache, LiteSpeed, IIS, Express, PHP, ASP.NET, Next.js, React, WordPress, Shopify, Wix, Squarespace, Drupal, 1C-Bitrix, Tilda, Google Tag Manager, HubSpot, Intercom.

### Examples

**Full profile of a few domains (default)**

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

**Email deliverability audit for client domains (fast, no certs/http)**

```json
{ "domains": ["client1.com", "client2.co.uk", "client3.de"], "includeCerts": false, "includeHttp": false, "dnsRecordTypes": ["MX", "TXT"], "fields": ["email", "spfRecord", "dmarcPolicy", "flags"] }
```

**Renewal & hijack watch on your own portfolio (weekly schedule)**

```json
{ "domains": ["brand.com", "brand.net", "brand.io"], "includeDns": false, "includeEmailSecurity": false, "includeCerts": false, "includeHttp": false, "expiresWithinDays": 60, "fields": ["registrar", "expiresAt", "daysToExpiry", "status", "dnssec", "flags"] }
```

**Sub-domain inventory / attack surface**

```json
{ "domains": ["target.com"], "includeRdap": false, "includeEmailSecurity": false, "includeHttp": false, "certsLimit": 0, "subdomainsLimit": 5000 }
```

**Phishing triage of look-alike domains**

```json
{ "domains": ["paypa1-secure.com", "micros0ft-login.net"], "newlyRegisteredDays": 180, "dnsResolver": "cloudflare", "httpTimeoutSecs": 10 }
```

### Output

One item per domain that at least one enabled module could answer for (trimmed), in the order of your list. A domain that does not exist (RDAP 404 / DNS NXDOMAIN and nothing else found) or for which every lookup failed is not an item: it is listed in the `SUMMARY` record (`notFound`, `errors`) and in the run's status message, and is not charged. Neither is a domain the run had no time left for (`notProcessed`, see Limits).

```json
{
  "domain": "apify.com", "inputDomain": "apify.com", "fetchedAt": "2026-09-13T08:03:20.888Z", "registrableDomain": "apify.com",
  "registrar": "Amazon Registrar, Inc.", "registrarIanaId": "468",
  "registrarAbuseEmail": "trustandsafety@support.aws.com", "registrarAbusePhone": "+1.2024422253",
  "registrantName": null, "registrantOrg": null, "registrantCountry": null,
  "registeredAt": "2009-06-02T17:14:10Z", "expiresAt": "2035-06-02T17:14:10Z", "updatedAt": "2026-05-16T16:53:04Z",
  "domainAgeDays": 6312, "daysToExpiry": 3184, "status": ["client transfer prohibited"],
  "nameservers": ["ns-1225.awsdns-25.org", "ns-449.awsdns-56.com"], "dnssec": true, "rdapUrl": "https://rdap.verisign.com/com/v1/domain/apify.com",
  "dns": { "A": ["3.164.68.53"], "AAAA": ["2600:9000:278c:3200:9:a03e:6540:93a1"], "CNAME": [], "MX": ["1 aspmx.l.google.com"], "NS": ["ns-449.awsdns-56.com"], "TXT": ["v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com -all"], "SOA": ["ns-1225.awsdns-25.org. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"], "CAA": ["0 issue \"letsencrypt.org\""] },
  "dnsStatus": "NOERROR", "ipAddresses": ["3.164.68.53", "2600:9000:278c:3200:9:a03e:6540:93a1"],
  "email": {
    "mx": [{ "priority": 1, "host": "aspmx.l.google.com" }], "provider": "Google Workspace",
    "spf": { "record": "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com -all", "valid": true, "allQualifier": "-all", "includes": ["_spf.google.com", "mailgun.org", "amazonses.com"], "ip4": [], "ip6": [], "lookupTerms": 5, "issues": [] },
    "dmarc": { "record": "v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc-reports@apify.com; ri=604800", "valid": true, "policy": "reject", "subdomainPolicy": "reject", "pct": 100, "rua": ["mailto:dmarc-reports@apify.com"], "ruf": [], "adkim": null, "aspf": null, "issues": [] },
    "dkimSelectorsFound": ["google"], "dkim": { "google": true, "selector1": false }, "mtaSts": "v=STSv1; id=29919a352ef9476a", "bimi": "v=BIMI1;l=https://apify.com/...", "txtVerifications": ["google-site-verification", "openai-domain-verification"]
  },
  "spfRecord": "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com -all", "dmarcPolicy": "reject",
  "mailProvider": "Google Workspace", "spfAllQualifier": "-all", "spfLookupTerms": 5, "dmarcPct": 100, "dkimSelectorsFound": ["google"], "mtaSts": "v=STSv1; id=29919a352ef9476a",
  "certificates": [{ "id": 28984295741, "issuer": "C=US, O=Certainly, CN=Certainly Intermediate R1", "issuerOrg": "Certainly", "commonName": "blog.apify.com", "names": ["blog.apify.com"], "notBefore": "2026-08-17T11:18:03", "notAfter": "2026-09-16T11:18:02", "expired": false, "wildcard": false, "url": "https://crt.sh/?id=28984295741" }],
  "certificatesTotal": 513, "certificateIssuers": { "Let's Encrypt": 413, "Amazon": 54, "ZeroSSL": 24, "Certainly": 9 }, "latestCertificate": { "...": "same shape" },
  "subdomains": ["api.apify.com", "blog.apify.com", "community.apify.com", "console.apify.com"], "subdomainsFound": 35,
  "http": { "ok": true, "https": true, "redirectsToHttps": true, "status": 200, "finalUrl": "https://apify.com/", "redirectChain": [], "title": "Apify: The largest marketplace of trusted tools for AI", "server": null, "poweredBy": null, "contentType": "text/html; charset=utf-8", "responseMs": 3436, "technologies": ["AWS CloudFront", "Next.js", "Google Tag Manager", "HubSpot"], "security": { "hsts": { "present": true, "maxAge": 15768000, "includeSubDomains": false, "preload": false }, "csp": true, "xFrameOptions": "SAMEORIGIN", "xContentTypeOptions": true, "referrerPolicy": null, "permissionsPolicy": false, "score": 6, "grade": "B", "missing": ["Referrer-Policy", "Permissions-Policy"] }, "error": null },
  "httpStatus": 200, "websiteTitle": "Apify: The largest marketplace of trusted tools for AI", "securityGrade": "B", "technologies": ["AWS CloudFront", "Next.js", "Google Tag Manager", "HubSpot"],
  "finalUrl": "https://apify.com/", "missingSecurityHeaders": ["Referrer-Policy", "Permissions-Policy"], "responseMs": 3436,
  "flags": ["registrant_redacted", "wildcard_cert"],
  "sourceUrls": { "rdap": "https://rdap.verisign.com/com/v1/domain/apify.com", "dns": "https://dns.google/resolve?name=apify.com", "crtsh": "https://crt.sh/?q=%25.apify.com" },
  "url": "https://apify.com/"
}
```

| Field group | Fields |
|---|---|
| Identity | `domain` (normalized), `inputDomain`, `fetchedAt`, `registrableDomain` (name RDAP answered for), `url`, `sourceUrls` |
| Registration (RDAP) | `registrar`, `registrarIanaId`, `registrarAbuseEmail`, `registrarAbusePhone`, `registrantName`, `registrantOrg`, `registrantCountry`, `registeredAt`, `expiresAt`, `updatedAt`, `domainAgeDays`, `daysToExpiry`, `status[]`, `nameservers[]`, `dnssec`, `rdapUrl`, `rdapError` (only when RDAP gave nothing; the other RDAP columns are then empty) |
| DNS | `dns{type: string[]}`, `dnsStatus`, `ipAddresses[]` |
| Email | `email.mx[]`, `email.provider`, `email.spf{record, valid, allQualifier, includes, ip4, ip6, mechanisms, redirect, lookupTerms, issues}`, `email.dmarc{record, valid, policy, subdomainPolicy, pct, rua, ruf, adkim, aspf, issues}`, `email.dkimSelectorsFound[]`, `email.dkimSelectorsChecked[]`, `email.dkim{}`, `email.mtaSts`, `email.bimi`, `email.txtVerifications[]`, plus top-level shortcuts `spfRecord`, `dmarcPolicy`, `mailProvider`, `spfAllQualifier`, `spfLookupTerms`, `dmarcPct`, `dkimSelectorsFound[]`, `mtaSts` |
| Certificates | `certificates[]{id, issuer, issuerOrg, commonName, names, notBefore, notAfter, expired, wildcard, url}`, `certificatesTotal`, `certificateIssuers{}`, `latestCertificate`, `subdomains[]`, `subdomainsFound`, `certsError` (only when crt.sh gave nothing) |
| Website | `http{ok, https, redirectsToHttps, status, finalUrl, redirectChain, title, server, poweredBy, contentType, responseMs, technologies, security{…}, error}`, plus top-level shortcuts `httpStatus`, `websiteTitle`, `securityGrade`, `technologies[]`, `finalUrl`, `missingSecurityHeaders[]`, `responseMs` |
| Risk | `flags[]` (see Reference) |

Dataset views: **Overview**, **Email security**, **Website** — they show the top-level columns, so they are filled for every row whose module is on. The dataset schema carries a title, description and example for every field (for agents and MCP).

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~domain-intel/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" -d '{"domains":["apify.com"],"fields":["registrar","expiresAt","dmarcPolicy","securityGrade","flags"]}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/domain-intel').call({ domains: ['apify.com', 'example.org'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/domain-intel").call(run_input={"domains": ["apify.com"], "includeCerts": False})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

MCP: connect your agent to `https://mcp.apify.com` and call the `yadroo/domain-intel` tool with the same JSON — ideal for "check this vendor's domain before we pay" workflows.

### Pricing

Pay per event: **$0.001 per run start + $0.005 per domain** (one dataset item per domain regardless of modules; non-existent, failed and not-processed domains are not charged). Bronze −10 %, Silver −20 %, Gold and above −30 % on the per-domain price; the start event is the same on every plan; platform usage is included. 10 domains ≈ $0.051 on the free plan. Modules change speed, not price. Your "Maximum cost per run" is respected: the run stops when it is reached and says so in its status ("Stopped at your spending limit: N rows delivered").

### Limits & FAQ

- **Speed and how many domains fit in one run** — up to 5 domains are looked up at the same time, and each domain's modules run side by side. Measured in the cloud on 2026-10-02 with the default 15-minute timeout: **every module on** — crt.sh sets the pace (it answered about 45 % of queries with errors after 30–90 s that day): 40 domains took 12 minutes, so about 45 domains fit in one run; **certificates off** (RDAP + DNS + email + website) — 125 domains in about 3.4 minutes, so about 450 fit; **RDAP + DNS + email** — 125 domains in about 2.7 minutes, so all 500 fit (about 11 minutes). Email security is the slow part without certificates: about 26 DNS queries per domain (15 of them DKIM selectors — trim `dkimSelectors` to go faster). For more domains, split the list or raise the run timeout.
- **Timeout** — the run stops starting new domains shortly before its timeout, saves every finished row and ends SUCCEEDED with "Stopped before the run timeout: N rows saved". Domains it had no time for are listed in `SUMMARY.notProcessed` (not charged): run them again, or raise the run timeout.
- **Rate limits (polite by design)** — crt.sh publishes 60 requests per minute per IP and rdap.org 10 per 10 seconds per IP: the actor never starts more than one request per second to either (4 crt.sh / 2 rdap.org requests at a time at most, served first come, first served), registry RDAP servers get at most two requests per second (4 at a time), and a `Retry-After` from any of them pauses that service for every domain of the run. DoH resolvers allow ~1,500 queries/s per IP; the actor stays under 100/s. `requestDelayMs` can only make this slower.
- **Partial results** — failed modules are reported per row (`rdapError`, `certsError`, `http.error`) and the row is still saved. `SUMMARY` in the default key-value store holds `{ domains, items, notFound[], notFoundCount, errors[{input, error}], errorCount, notProcessed[], notProcessedCount, warnings[], modules, delivered, stoppedBy, status }`; the run fails only if no domain could be looked up at all.
- **RDAP gaps** — registries without RDAP return no registration data (see Reference). Registrant details are usually redacted under GDPR; `registrant_redacted` is normal, not suspicious on its own.
- **Sub-domains** — only names that appeared in public certificates; internal hosts without TLS certs are not visible. A domain whose crt.sh answer is larger than 12 MB (tens of thousands of certificates) gets `certsError`; `certsExcludeExpired` makes the answer smaller.
- **Website probe** — one GET of the homepage with a plain user agent (first 200 KB read); bot-protected sites may answer 403 — that is recorded, not retried aggressively. The probe only connects to public addresses: IP addresses and internal names are refused as input, and a site or redirect that points at a private, loopback, link-local (cloud metadata) or other reserved address is not requested (`http.error` says so).
- **Roadmap** — WHOIS fallback for non-RDAP ccTLDs, IP-to-ASN enrichment of `ipAddresses` (see ip-intel), typosquat generation.

***

Made by **Yadroo**. Related actors: [ip-intel](https://apify.com/yadroo/ip-intel) (geo/ASN/abuse for the IPs found here), [sanctions-screen](https://apify.com/yadroo/sanctions-screen), [github-repo-intel](https://apify.com/yadroo/github-repo-intel), [npm-package-intel](https://apify.com/yadroo/npm-package-intel), [ens-resolver](https://apify.com/yadroo/ens-resolver).

# Actor input Schema

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

Domain names to profile — one row per domain. URLs and hosts are accepted and normalized ("https://www.Example.com/x" → "example.com"; IDNs → punycode). Sub-domains are walked up to the registered name for RDAP. Invalid entries, IP addresses and internal names (localhost, \*.local, \*.internal…) are skipped with a warning; duplicates collapse. Max 500 per run; how many finish within the run's timeout depends on the modules (README → Limits) — the rest is listed in SUMMARY.notProcessed and not charged.

## `includeRdap` (type: `boolean`):

Registrar, IANA registrar id, abuse contact, registration / expiry / update dates, domain age, EPP status codes, DNSSEC and nameservers. Uses the IANA bootstrap so every gTLD and most ccTLDs work; TLDs without RDAP (e.g. .io, .co, .us, .kz, .ru, .de) return `rdapError` instead of data.

## `includeDns` (type: `boolean`):

Resolve the record types below via Google or Cloudflare DoH. Adds `dns` (per type), `dnsStatus` (NOERROR / NXDOMAIN) and `ipAddresses`.

## `dnsRecordTypes` (type: `array`):

Which record types to query. Valid: A, AAAA, CNAME, MX, NS, TXT, SOA, CAA, SRV, PTR, DS, DNSKEY, TLSA, HTTPS, SVCB, NAPTR.

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

Public resolver used for all DNS queries. Switch if one is rate-limiting you or to compare views.

## `includeEmailSecurity` (type: `boolean`):

Parses SPF (mechanisms, includes, `all` qualifier, DNS-lookup count, issues) and DMARC (policy, pct, rua/ruf, alignment, issues), detects the mail provider from MX, probes DKIM selectors, MTA-STS and BIMI records. Adds `email`, `spfRecord`, `dmarcPolicy`.

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

Selectors checked at `<selector>._domainkey.<domain>` (Google Workspace = google, Microsoft 365 = selector1/selector2, Mailchimp = k1, Postmark = pm…). Max 30. Empty list = skip DKIM.

## `includeCerts` (type: `boolean`):

Certificate history for the domain and all its sub-domains from CT logs: issuer, names, validity, wildcard flag, plus `certificateIssuers` counts and `latestCertificate`. crt.sh is the slowest source (often 30–90 s per domain) and fails under load (then the row has `certsError`): it limits a run with every module on to about 45 domains; turn it off for big lists.

## `certsLimit` (type: `integer`):

Newest certificates kept in `certificates` (deduplicated pre-cert/leaf pairs). `certificatesTotal` always reports the full count. 0 = counts and subdomains only.

## `certsExcludeExpired` (type: `boolean`):

Only currently valid certificates (crt.sh `exclude=expired`). Faster for old domains, but historic sub-domains disappear too.

## `includeSubdomains` (type: `boolean`):

Collects every hostname under the domain that ever appeared in a certificate (wildcards excluded) into `subdomains` / `subdomainsFound`. Needs `includeCerts`: with certificates off it does nothing.

## `subdomainsLimit` (type: `integer`):

Cap on the `subdomains` array (sorted alphabetically). `subdomainsFound` is the uncapped count.

## `includeHttp` (type: `boolean`):

Requests https://domain/ (falls back to http://), follows up to 6 redirects, records status, final URL, redirect chain, page title, server/powered-by, whether plain HTTP upgrades to HTTPS, grades security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy → A–F) and detects the stack (CDN, framework, CMS, tag manager). Adds `http`, `httpStatus`, `websiteTitle`, `securityGrade`, `technologies`, `finalUrl`, `missingSecurityHeaders`, `responseMs`. Never connects to private, loopback or other reserved addresses: every redirect hop is checked.

## `httpTimeoutSecs` (type: `integer`):

Timeout of each website-probe request: the HTTPS homepage with its redirects, the plain-HTTP fallback, the HTTP→HTTPS upgrade check.

## `expiresWithinDays` (type: `integer`):

Domains whose registration expires within this many days get the `expires_soon` flag (renewal / takeover monitoring).

## `newlyRegisteredDays` (type: `integer`):

Young domains are a phishing / fraud signal; adjust the threshold to your policy.

## `fields` (type: `array`):

Keep only these top-level fields, in this order, e.g. \["registrar", "expiresAt", "flags"]. `domain` and `fetchedAt` are always kept (first, unless you list them). Letter case is corrected; unknown names are ignored with a warning in SUMMARY.warnings (nested paths like email.provider → use the top-level `mailProvider`); a list with no known name, or only fields of switched-off modules, fails the run before any domain is looked up (only the run start is charged). Empty = full row.

## `requestDelayMs` (type: `integer`):

Minimum gap between two requests to the same RDAP server or crt.sh. Lower values are raised to each service's published per-IP limit: crt.sh and rdap.org 1 s, registry RDAP servers 0.5 s. DNS-over-HTTPS queries are paced separately (at most 20 at a time, 100 per second). Raise it to be gentler.

## Actor input object example

```json
{
  "domains": [
    "apify.com"
  ],
  "includeRdap": true,
  "includeDns": true,
  "dnsRecordTypes": [
    "A",
    "AAAA",
    "CNAME",
    "MX",
    "NS",
    "TXT",
    "SOA",
    "CAA"
  ],
  "dnsResolver": "google",
  "includeEmailSecurity": true,
  "dkimSelectors": [
    "google",
    "selector1",
    "selector2",
    "default",
    "k1",
    "mail",
    "s1",
    "dkim",
    "mandrill",
    "smtp",
    "zoho",
    "pm",
    "krs",
    "mailgun",
    "amazonses"
  ],
  "includeCerts": true,
  "certsLimit": 50,
  "certsExcludeExpired": false,
  "includeSubdomains": true,
  "subdomainsLimit": 500,
  "includeHttp": true,
  "httpTimeoutSecs": 15,
  "expiresWithinDays": 30,
  "newlyRegisteredDays": 90,
  "requestDelayMs": 200
}
```

# Actor output Schema

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

No description

## `emailView` (type: `string`):

No description

## `websiteView` (type: `string`):

No description

## `summary` (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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/domain-intel").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"] }

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

```

## MCP server setup

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

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/trQc36SF6AncdqkIC/builds/SLqv8FGXA3xWoeLOx/openapi.json
