# Bulk Domain Intelligence: WHOIS/RDAP, DNS, SSL, Availability (`cottony_brittlegill/domain-intelligence-bulk`) Actor

Bulk domain lookup: RDAP/WHOIS registration data, DNS records, SSL certificate and registration availability for up to 100k domains in one normalized JSON per domain. Failed lookups are never charged.

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

## Pricing

Pay per event

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/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 Bulk — WHOIS/RDAP + DNS + SSL + Availability in one JSON

Check thousands of domains in one run: registration data (registrar, creation,
expiry), DNS records, an SSL certificate check and domain availability — one
normalized JSON per domain. A bulk WHOIS API and RDAP client, a DNS records
lookup and a domain expiry checker, combined, so you don't have to stitch
three tools together. Up to 100,000 domains per run — see "Honest limits" for
the throughput and default timeout you should actually expect on a run that large.

### What you get per domain

Every dataset item is a single `DomainReport`:

| Field | Type | Meaning |
|---|---|---|
| `domain` | string | Normalized hostname you queried (punycode if IDN) |
| `registrableDomain` | string | eTLD+1 actually queried against the registry |
| `tld` | string | Public suffix (e.g. `com`, `co.uk`) |
| `status` | `registered` | `likely_available` | `unknown` | See "Honest limits" below |
| `registration.source` | `rdap` | `whois` | `null` | Which protocol answered |
| `registration.registrar` | string | null | Registrar name, when the source exposes it |
| `registration.createdAt` / `updatedAt` / `expiresAt` | ISO date | null | Registration dates |
| `registration.daysToExpiry` | number | null | Days until `expiresAt`, computed at check time — the domain-expiry-tracking signal |
| `registration.domainStatus` | string\[] | EPP status codes (RDAP only) |
| `registration.nameserversFromRegistry` | string\[] | Nameservers as declared at the registry. Always populated on RDAP. On WHOIS fallback, only when the reply uses a per-line label our parser recognizes (e.g. DENIC's `Nserver:`) — dialects that list nameservers under a section header instead (e.g. `.it`'s `Nameservers` block) still come back `[]`, same parser-gap situation as the `.it` registrar field below |
| `dns` | object | null | NS, A, AAAA, MX, TXT, CNAME records (`null` if you disable the signal) |
| `ssl` | object | null | SSL/TLS certificate check: issuer, subject CN, SAN, validity window, days to expiry |
| `errors` | string\[] | Non-fatal per-signal errors (a DNS timeout doesn't fail the whole report) |
| `checkedAt` | ISO date | When the check ran |
| `chargeable` | boolean | `true` unless `status` is `unknown` — whether this report qualifies for billing |
| `charged` | boolean | Whether the Apify platform actually billed this report (see Pricing) |

Real output for `example.com` (this run's `.env`/network conditions — dates and
counts will differ on your own run):

```json
{
  "domain": "example.com",
  "registrableDomain": "example.com",
  "tld": "com",
  "status": "registered",
  "registration": {
    "source": "rdap",
    "registrar": "RESERVED-Internet Assigned Numbers Authority",
    "createdAt": "1995-08-14T04:00:00Z",
    "updatedAt": "2026-01-16T18:26:50Z",
    "expiresAt": "2026-08-13T04:00:00Z",
    "daysToExpiry": 15,
    "domainStatus": ["client delete prohibited", "client transfer prohibited", "client update prohibited"],
    "nameserversFromRegistry": ["elliott.ns.cloudflare.com", "hera.ns.cloudflare.com"]
  },
  "dns": {
    "ns": ["hera.ns.cloudflare.com", "elliott.ns.cloudflare.com"],
    "a": ["104.20.23.154", "172.66.147.243"],
    "aaaa": ["2606:4700:10::ac42:93f3", "2606:4700:10::6814:179a"],
    "mx": [{ "priority": 0, "exchange": "" }],
    "txt": ["_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9", "v=spf1 -all"],
    "cname": [],
    "error": null
  },
  "ssl": {
    "issuer": "SSL Corporation",
    "subjectCN": "example.com",
    "validFrom": "2026-05-31T21:39:12.000Z",
    "validTo": "2026-08-29T21:41:26.000Z",
    "daysToExpiry": 32,
    "san": ["example.com", "*.example.com"],
    "error": null
  },
  "errors": [],
  "checkedAt": "2026-07-28T11:51:38.401Z",
  "chargeable": true,
  "charged": false
}
```

(`charged: false` here because this example was run locally, outside Apify's
Pay-Per-Event ledger — see Pricing.)

### Why this actor

- 4 signals in 1 run (others give you only WHOIS, only DNS, or only availability)
- RDAP-first (the modern registry protocol), WHOIS only as fallback — real
  bulk WHOIS API + RDAP coverage across gTLDs and ccTLDs, not just `.com`
- Registry rate limits respected (per-registry throttling, RFC 7480 backoff)
- SSRF/DNS-rebinding hardened SSL check — the TLS lookup resolves the host, blocks private,
  loopback, link-local and cloud-metadata addresses (RFC1918, `127.0.0.0/8`, `169.254.0.0/16`,
  etc.), and connects to the validated IP rather than re-resolving at connect time, so pointing
  this actor at attacker-controlled or untrusted domain input can't be used to reach your
  internal network
- **Failed lookups are never charged** — every report is pushed with an
  honest `chargeable` flag (`false` whenever `status` is `unknown`), and
  `charged` reflects what the Apify platform's own billing ledger actually
  billed, not merely what we attempted — you pay only for delivered reports
- Pay-per-event pricing, agent-friendly (x402-eligible): no subscription, no
  per-run minimum, callable straight from an API key or an agent's wallet

### Honest limits (read before buying)

- **No registrant email/name.** GDPR-redacted WHOIS/RDAP since 2018.
  Registrar, dates and nameservers: yes. Personal contact data: no — from
  anyone, on any TLD. If a tool promises registrant emails from public
  WHOIS in 2026, be skeptical of it, not of us.
- **Some ccTLDs have no RDAP at all** (confirmed directly against IANA's
  live bootstrap registry: neither `.de` nor `.it` are listed) and fall back
  to WHOIS:43. WHOIS formats vary a lot registry to registry: `.de`
  (DENIC) actually replies with six lines — `Domain:`, `Domain-Ace:`, up to
  three `Nserver:` lines, `Status:` and `Changed:` (a last-modification
  timestamp) — confirmed with a live query. We parse `Changed:` into
  `updatedAt` and the `Nserver:` lines into `nameserversFromRegistry`, but
  DENIC's reply never includes a registrar name or creation/expiry dates,
  so those three stay `null` — that part genuinely is the registry's own
  minimal record, not a parser gap. IDN (umlaut) `.de` domains are queried
  with DENIC's documented `-T dn,ace` flag so they resolve correctly to the
  real registry answer (`Status: connect`/`Status: free`), not a
  malformed-query artifact. `.it` (Registro.it) replies with a fuller
  record — creation, last-update and expiry dates. It **does** include a
  registrar section (`Registrar` / `Organization: ...`) and a `Nameservers`
  section, but our parser doesn't yet handle that section-header layout
  (no per-line `Registrar:`/`Nserver:` label to match), so `registrar` and
  `nameserversFromRegistry` currently come back `null`/`[]` for `.it` — a
  parser gap, not a registry limitation; it's on the list to fix, not being
  passed off as "the registry doesn't say". In all cases you get
  `status: "registered"` with whatever fields we could actually extract —
  unparsed/unsupported fields are reported as `null`/`[]`, never guessed or
  backfilled.
- **When a registry's reply can't be interpreted at all** (neither a known
  "found" nor a known "not found" shape), `status` is `"unknown"` with an
  explicit message in `errors` (e.g. `"unparseable whois response for <tld>"`) — never silently reported as available or registered.
- **`"likely_available"` is a strong signal** (registry 404/"available"
  answer + zero nameservers), not a guarantee — always confirm at your
  registrar before buying a name.
- **Large single-TLD lists are throttled per registry, on purpose** — but
  raising `perTldConcurrency` is *not* the way to speed that up.
  Concurrency to any one registry defaults to 3 lookups at a time (per the
  input's `perTldConcurrency`), regardless of how many domains in your
  batch share that registry — multi-label ccTLDs like `.co.uk` count
  against the same budget as `.uk`, since they're the same registry under
  the hood. This respects RFC 7480 and keeps registries from rate-limiting
  or blocking us (and you). What it does *not* do is throttle the DNS and
  SSL/TLS checks that run alongside it — those are governed only by
  `globalConcurrency`. Measured live, on 51 fast, healthy `.com` domains
  with default settings (`perTldConcurrency=3`, `globalConcurrency=20`,
  DNS+SSL on): **115 ms/domain**. Raising `globalConcurrency` to its
  maximum (50) gave **24 ms/domain** — a 5x speedup — while
  `perTldConcurrency` barely moves the number once DNS/SSL are enabled,
  because the registry call is rarely the bottleneck. **If you need a large
  run to finish faster, raise `globalConcurrency`, not `perTldConcurrency`.**
  Expect throughput in the 100–150 ms/domain range at the defaults (real
  prospect/portfolio lists — slower DNS, expired certs, dead hosts eating
  the full `lookupTimeoutMs`, default 10s — will run slower than this
  best-case healthy-`.com` measurement, not faster). At that rate a
  100,000-domain run is realistically **2.5–4 hours** at default settings,
  or well under an hour with `globalConcurrency` raised toward its maximum.
  The actor's own platform run timeout defaults to 6 hours
  (`timeoutSecs: 21600`) specifically to give a default-settings 100k run
  room to finish rather than being killed mid-flight with charges already
  incurred standing — but a very slow, mostly-unresponsive list can still
  exceed it. For predictable large runs: split single-TLD batches across
  runs, or raise `globalConcurrency`.
- **The domain cap is enforced, not just documented.** `domains` +
  `domainsText` are capped at 100,000 combined; anything past that is
  dropped before processing (never charged, never returned) and a warning
  naming the submitted/kept/dropped counts is written to the run log — so a
  run over the cap is loud about it instead of silently returning a
  shorter dataset than you submitted.
- **A leading `www.` is stripped and deduplicated with the apex domain by
  default.** `www.example.com` and `example.com` in the same batch are
  treated as one domain (one `domain-report` charge, one dataset row) —
  set `keepWww: true` if you specifically need the `www` host's own DNS/TLS
  data checked separately from the apex (it can genuinely differ, e.g. a
  different SSL certificate or CNAME target).
- No claim of "cheapest domain intelligence API" and no domain-monitoring
  subscription pitch here: this is a bulk lookup actor, run on demand, for
  what it actually measures — registration, DNS, SSL, availability — priced
  per result, not per month.

### Pricing

Pay-Per-Event (PPE) — no subscription:

| Event | Price | When it fires |
|---|---|---|
| `actor-start` | $0.005 | Once per run |
| `domain-report` | $0.002 | Once per domain report with `chargeable: true` (`status` other than `unknown`) |

**$2.00 per 1,000 domains** (`domain-report` events) plus a flat $0.005 per
run. Unparsed/unresolvable domains (`status: "unknown"`) are never billed —
`chargeable` and `charged` are pushed on every dataset item so you can
reconcile your own invoice against the data.

### Use cases

**1. Domain portfolio expiry audit.** Feed your whole portfolio (hundreds or
thousands of domains across TLDs) in one run and get `daysToExpiry` for
every one, in a single normalized JSON — instead of clicking through each
registrar's dashboard or juggling five WHOIS CLIs for five different TLD
formats.

**2. Lead-gen / sales tech enrichment.** Given a prospect list of company
domains, enrich each with registrar, DNS provider (nameservers reveal
Cloudflare/AWS/etc.), MX provider (Google Workspace vs. Microsoft 365 vs.
self-hosted) and SSL issuer — firmographic and tech-stack signal for
outbound targeting, at $2 per 1,000 domains.

**3. Agent pipelines via API / x402.** An AI agent doing domain
due-diligence before recommending or registering a name calls this actor
directly — pay-per-event pricing means no dashboard, no subscription, just
an API call (or an x402 payment) per batch.

Standard Apify API call (synchronous, returns the dataset items directly):

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/YOUR_USERNAME~domain-intelligence-bulk/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "domains": ["example.com", "nic.it", "münchen.de"],
    "includeDns": true,
    "includeSsl": true,
    "whoisFallback": true
  }'
```

### FAQ

**What's the difference between WHOIS and RDAP?**
RDAP is the modern, structured (JSON) successor to the old plain-text WHOIS
protocol — it returns more consistent field names across registries instead
of a different free-text layout per registrar. This actor queries RDAP
first and only falls back to WHOIS:43 for TLDs that don't offer RDAP yet
(confirmed directly against IANA's live RDAP bootstrap registry — see
"Honest limits" for exactly which ones).

**Can I get a domain's owner name or email from this?**
No — and no tool legitimately can, from public WHOIS/RDAP, since the 2018
GDPR redactions. Registrar, dates, nameservers and status codes: yes.
Registrant contact data: no, from anyone, on any TLD.

**How do I check if a domain is available before buying it?**
`status: "likely_available"` is a strong signal (a registry not-found/
available answer plus zero nameservers), not a guarantee — always confirm
availability at a registrar before purchasing.

**Does `www.example.com` and `example.com` count as two domains?**
No, by default. A leading `www.` is stripped and deduplicated with the apex
domain — one `domain-report` charge, one dataset row — unless you set
`keepWww: true` because you specifically need the `www` host's own DNS/TLS
data checked separately.

### Input / Output

#### Input

```json
{
  "domains": ["example.com", "google.com"],
  "domainsText": "",
  "keepWww": false,
  "includeDns": true,
  "includeSsl": true,
  "whoisFallback": true,
  "perTldConcurrency": 3,
  "globalConcurrency": 20,
  "lookupTimeoutMs": 10000
}
```

| Field | Type | Default | Description |
|---|---|---|---|
| `domains` | string\[] | `["example.com", "google.com"]` | Domains to analyze. Bare domains or full URLs; scheme/`www`/path stripped automatically, so `www.example.com` and `example.com` count and dedupe as one domain. Combined with `domainsText`, capped at 100,000 — anything past the cap is dropped before processing, with a warning logged naming the submitted/kept/dropped counts. |
| `domainsText` | string | — | Alternative/additional domains pasted as newline- or comma-separated text; merged with `domains` (same 100,000 combined cap). |
| `keepWww` | boolean | `false` | Keep the `www` host as its own domain instead of stripping/deduplicating it with the apex — use when the `www` host's own DNS/TLS data genuinely needs to be checked separately. |
| `includeDns` | boolean | `true` | Include NS, A, AAAA, MX, TXT, CNAME records in the output. The DNS lookup itself always runs regardless of this flag (it's also used internally to detect `likely_available` domains) — this only controls whether the `dns` object appears in the output, not whether it's fetched, so turning it off does not reduce runtime. |
| `includeSsl` | boolean | `true` | Include the SSL/TLS certificate check (issuer, SAN, validity, days to expiry). |
| `whoisFallback` | boolean | `true` | Fall back to WHOIS:43 for TLDs without RDAP. |
| `perTldConcurrency` | integer | `3` | Concurrent lookups per registry — kept low on purpose to respect registry rate limits (RFC 7480). Does not throttle DNS/SSL, so raising it does little for overall speed once those are enabled. |
| `globalConcurrency` | integer | `20` | Overall concurrent domains in flight — the effective throughput knob for full-signal runs. Measured ~5x faster (115 ms/domain → 24 ms/domain) going from the default to the maximum (see "Honest limits"). |
| `lookupTimeoutMs` | integer | `10000` | Per-lookup timeout in milliseconds. |

#### Output (dataset items)

One `DomainReport` per domain (see field table above), in the order
resolved (not necessarily input order, since domains run concurrently).
Three real examples from a live run — a clean `.com`, an available `.com`,
and a `.de` domain resolved through the WHOIS fallback with sparse fields
(the honest ccTLD case described above):

```json
{
  "domain": "this-domain-should-not-exist-x9z7q.com",
  "registrableDomain": "this-domain-should-not-exist-x9z7q.com",
  "tld": "com",
  "status": "likely_available",
  "registration": {
    "source": null, "registrar": null, "createdAt": null, "updatedAt": null,
    "expiresAt": null, "daysToExpiry": null, "domainStatus": [], "nameserversFromRegistry": []
  },
  "dns": { "ns": [], "a": [], "aaaa": [], "mx": [], "txt": [], "cname": [], "error": null },
  "ssl": {
    "issuer": null, "subjectCN": null, "validFrom": null, "validTo": null,
    "daysToExpiry": null, "san": [], "error": "tls getaddrinfo ENOTFOUND this-domain-should-not-exist-x9z7q.com"
  },
  "errors": ["tls getaddrinfo ENOTFOUND this-domain-should-not-exist-x9z7q.com"],
  "checkedAt": "2026-07-28T11:51:38.422Z",
  "chargeable": false,
  "charged": false
}
```

```json
{
  "domain": "xn--mnchen-3ya.de",
  "registrableDomain": "xn--mnchen-3ya.de",
  "tld": "de",
  "status": "registered",
  "registration": {
    "source": "whois", "registrar": null, "createdAt": null, "updatedAt": "2025-05-15T13:42:32.000Z",
    "expiresAt": null, "daysToExpiry": null, "domainStatus": [],
    "nameserversFromRegistry": ["ns01e.muenchen.de", "ns02e.muenchen.de", "ns04e.muenchen.de"]
  },
  "dns": {
    "ns": ["ns04e.muenchen.de", "ns02e.muenchen.de", "ns01e.muenchen.de"],
    "a": ["194.246.166.100"], "aaaa": ["2a00:1830:a001:f00a::100"],
    "mx": [], "txt": [], "cname": [], "error": null
  },
  "ssl": {
    "issuer": "Let's Encrypt", "subjectCN": "www.xn--mnchen-3ya.de",
    "validFrom": "2026-06-26T22:49:29.000Z", "validTo": "2026-09-24T22:49:28.000Z",
    "daysToExpiry": 58, "san": ["www.xn--mnchen-3ya.de", "xn--mnchen-3ya.de"], "error": null
  },
  "errors": [],
  "checkedAt": "2026-07-28T11:51:38.614Z",
  "chargeable": true,
  "charged": false
}
```

Note `registrar`/`createdAt`/`expiresAt` are still `null` for the `.de` domain
above even though `status` is `"registered"` — DENIC's WHOIS reply genuinely
never includes a registrar name or creation/expiry dates, reported honestly
rather than papered over. `updatedAt` and `nameserversFromRegistry`, by
contrast, *are* populated — DENIC's `Changed:`/`Nserver:` lines are parsed
(see "Honest limits").

```json
{
  "domain": "nic.it",
  "registrableDomain": "nic.it",
  "tld": "it",
  "status": "registered",
  "registration": {
    "source": "whois", "registrar": null,
    "createdAt": "1997-08-26T22:00:00.000Z",
    "updatedAt": "2026-01-15T23:46:30.000Z",
    "expiresAt": "2026-12-31T00:00:00.000Z",
    "daysToExpiry": 155, "domainStatus": [], "nameserversFromRegistry": []
  },
  "dns": {
    "ns": ["dns.nic.it", "r.dns.it", "m.dns.it", "a.dns.it", "v.dns.it", "nameserver.cnr.it"],
    "a": ["192.12.192.40"], "aaaa": ["2a00:d40:1:1:192:12:192:40"],
    "mx": [{ "priority": 10, "exchange": "mail.nic.it" }],
    "txt": ["HARICA-H1dSHZOI5GbN6mRuTtw", "facebook-domain-verification=p6h10ymlbgvexgwmhi9ufw2bc2d7k8", "v=spf1 ip4:192.12.192.0/23 ip6:2a00:d40::/32 ip4:192.12.194.0/24 a:*.nic.it ~all"],
    "cname": [], "error": null
  },
  "ssl": {
    "issuer": "Let's Encrypt", "subjectCN": "www.nic.it",
    "validFrom": "2026-06-16T10:15:34.000Z", "validTo": "2026-09-14T10:15:33.000Z",
    "daysToExpiry": 47,
    "san": ["dotitalianregistry.it", "dotitregistry.it", "nic.it", "registro.it", "registrodominitalia.it", "registroitaliadomini.it", "web-r4.nic.it", "www.dotitalianregistry.it", "www.dotitregistry.it", "www.nic.it", "www.registro.it", "www.registrodominitalia.it", "www.registroitaliadomini.it"],
    "error": null
  },
  "errors": [],
  "checkedAt": "2026-07-28T11:51:38.620Z",
  "chargeable": true,
  "charged": false
}
```

### Related tools

Part of the same domain-tooling family, same pay-per-event pricing, same
"never charge a failed lookup" policy:

- **[Domain Mail-Health Bulk](https://apify.com/cottony_brittlegill/domain-mailhealth-bulk)**
  — SPF, DMARC, DKIM and MX audit with an A-F grade. Use it when the
  question is "can mail sent from this domain be trusted," not "who owns it
  or is it up."
- **[Typosquat & Lookalike Domain Detector](https://apify.com/cottony_brittlegill/domain-typosquat-detector)**
  — finds lookalike/typosquatted domains registered against a brand you own
  and classifies which ones look like active threats versus parked
  inventory. Use it when you're monitoring for impersonation of *your*
  brand, not checking domains you already have in hand.

# Actor input Schema

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

List of domains to analyze (max 100,000 combined with domainsText; the rest are dropped, with a warning logged). Accepts bare domains or URLs; scheme/www/path are stripped automatically, so www.example.com and example.com count and dedupe as one domain (see keepWww to disable).

## `domainsText` (type: `string`):

Alternative to the list above: paste domains separated by newlines or commas. Merged with the list (max 100,000 domains combined).

## `keepWww` (type: `boolean`):

By default a leading 'www.' is stripped so www.example.com and example.com are treated as one domain (deduplicated, billed once). Turn this on to keep the www host separate when its own DNS/TLS data genuinely needs to be checked independently from the apex domain.

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

NS, A, AAAA, MX, TXT, CNAME. The DNS lookup itself always runs (it's also used internally to detect likely\_available domains) — this only controls whether the dns object is included in the output; turning it off does not reduce runtime.

## `includeSsl` (type: `boolean`):

Issuer, subject CN, SAN, validity window, days to expiry.

## `whoisFallback` (type: `boolean`):

When a TLD has no RDAP service, fall back to a WHOIS (port 43) lookup for registration data. Turn off to skip WHOIS and mark such domains as unknown.

## `perTldConcurrency` (type: `integer`):

Kept low to respect registry rate limits (RFC 7480). Only throttles the RDAP/WHOIS registry call — raising it barely changes overall run time once DNS/SSL are enabled, since those aren't limited by this setting. For faster runs, raise globalConcurrency instead.

## `globalConcurrency` (type: `integer`):

How many domains to process in parallel across all registries. Higher is faster but uses more memory. This is the effective throughput knob for full-signal runs (DNS+SSL enabled): measured ~5x faster (115ms/domain to 24ms/domain) going from the default to the maximum.

## `lookupTimeoutMs` (type: `integer`):

How long to wait for each individual RDAP/WHOIS/DNS/TLS lookup before giving up on it (milliseconds).

## Actor input object example

```json
{
  "domains": [
    "example.com",
    "google.com"
  ],
  "keepWww": false,
  "includeDns": true,
  "includeSsl": true,
  "whoisFallback": true,
  "perTldConcurrency": 3,
  "globalConcurrency": 20,
  "lookupTimeoutMs": 10000
}
```

# 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": [
        "example.com",
        "google.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cottony_brittlegill/domain-intelligence-bulk").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": [
        "example.com",
        "google.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("cottony_brittlegill/domain-intelligence-bulk").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "example.com",
    "google.com"
  ]
}' |
apify call cottony_brittlegill/domain-intelligence-bulk --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=cottony_brittlegill/domain-intelligence-bulk",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/ewGJl5fLQmAhcpCLj/builds/2J0FVWOWrliecrzme/openapi.json
