Bulk Domain Intelligence: WHOIS/RDAP, DNS, SSL, Availability
Pricing
Pay per event
Bulk Domain Intelligence: WHOIS/RDAP, DNS, SSL, Availability
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.
Pricing
Pay per event
Rating
0.0
(0)
Developer
cottony_brittlegill
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 hours ago
Last modified
Categories
Share
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):
{"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
chargeableflag (falsewheneverstatusisunknown), andchargedreflects 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
.denor.itare 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 threeNserver:lines,Status:andChanged:(a last-modification timestamp) — confirmed with a live query. We parseChanged:intoupdatedAtand theNserver:lines intonameserversFromRegistry, but DENIC's reply never includes a registrar name or creation/expiry dates, so those three staynull— that part genuinely is the registry's own minimal record, not a parser gap. IDN (umlaut).dedomains are queried with DENIC's documented-T dn,aceflag 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 aNameserverssection, but our parser doesn't yet handle that section-header layout (no per-lineRegistrar:/Nserver:label to match), soregistrarandnameserversFromRegistrycurrently come backnull/[]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 getstatus: "registered"with whatever fields we could actually extract — unparsed/unsupported fields are reported asnull/[], 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),
statusis"unknown"with an explicit message inerrors(e.g.) — never silently reported as available or registered."unparseable whois response for <tld>" "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
perTldConcurrencyis not the way to speed that up. Concurrency to any one registry defaults to 3 lookups at a time (per the input'sperTldConcurrency), regardless of how many domains in your batch share that registry — multi-label ccTLDs like.co.ukcount 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 byglobalConcurrency. Measured live, on 51 fast, healthy.comdomains with default settings (perTldConcurrency=3,globalConcurrency=20, DNS+SSL on): 115 ms/domain. RaisingglobalConcurrencyto its maximum (50) gave 24 ms/domain — a 5x speedup — whileperTldConcurrencybarely 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, raiseglobalConcurrency, notperTldConcurrency. Expect throughput in the 100–150 ms/domain range at the defaults (real prospect/portfolio lists — slower DNS, expired certs, dead hosts eating the fulllookupTimeoutMs, default 10s — will run slower than this best-case healthy-.commeasurement, not faster). At that rate a 100,000-domain run is realistically 2.5–4 hours at default settings, or well under an hour withglobalConcurrencyraised 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 raiseglobalConcurrency. - The domain cap is enforced, not just documented.
domains+domainsTextare 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.comandexample.comin the same batch are treated as one domain (onedomain-reportcharge, one dataset row) — setkeepWww: trueif you specifically need thewwwhost'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):
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
{"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):
{"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}
{"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").
{"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 — 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 — 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.