# WHOIS Domain Lookup in Bulk: RDAP, DNS, SSL (`maliksaid/domain-intelligence-rdap`) Actor

WHOIS domain lookup in bulk through official RDAP: registered or available, registrar, domain age, created and expiry dates. Adds DNS records, SPF, DMARC and DKIM checks, email provider and SSL certificate expiry. Check one name across many TLDs. One row per domain. Expiry alerts and change monitor.

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

## Pricing

from $1.60 / 1,000 domain checks

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

## Bulk WHOIS Domain Lookup: RDAP, DNS, SSL, DMARC

This bulk WHOIS domain lookup checks a list of domains and returns one flat row per domain. Each row says whether the domain is registered or available, and has WHOIS registration data, domain age and expiry, DNS records, SPF and DMARC policy, and the SSL certificate.

Registration data comes from each registry's official RDAP server. RDAP is the standard that replaces WHOIS. The Actor finds the right server through the IANA RDAP bootstrap file.

Use it for domain portfolio checks, domain expiry alerts, vendor checks, email deliverability audits and lead enrichment. AI agents can call it to check a domain in one step.

### WHOIS lookup, DNS and SSL: what it checks

| Check | Source | Fields |
|---|---|---|
| Registration (WHOIS) | Registry RDAP server listed by IANA | Registered or available, registrar, IANA ID, created, expires, last changed, status, name servers, DNSSEC |
| DNS lookup | Public DNS resolvers (Cloudflare 1.1.1.1 and Google 8.8.8.8) | A, AAAA, MX, NS, TXT |
| Email security | TXT records of the domain and `_dmarc.<domain>` | SPF record, all qualifier, includes, DMARC policy, subdomain policy, pct, report addresses |
| SSL certificate | One TLS handshake on port 443 | Issuer, subject, valid from, valid to, days to expiry, trusted or not, TLS version |

On 21 October 2026 the IANA bootstrap listed RDAP servers for 1,203 top-level domains. That covers .com, .net, .org, .info, .uk, .fr, .nl, .au, .ca, .in, .app, .dev and most new TLDs. Some country domains have no RDAP server yet, for example .io, .de, .eu, .ae, .sa and .us. For those, `rdapAvailable` is false and the registration fields stay empty. DNS, SPF, DMARC and SSL still work.

### Domain lookup data returned

| Field | Meaning |
|---|---|
| `domain` | Domain checked, in lower case. A leading www is removed |
| `input` | The value as you entered it |
| `registrationStatus` | `registered`, `available` or `unknown`. See the FAQ on domain availability |
| `registrationSource` | What decided the status: `rdap` (registry answer) or `dns` (DNS fallback) |
| `rdapAvailable` | True when IANA lists an RDAP server for the TLD |
| `registeredDomain` | Registered domain found in RDAP. For `docs.python.org` this is `python.org` |
| `registrar`, `registrarIanaId`, `registrarAbuseEmail` | Registrar name, IANA ID and abuse contact |
| `registeredAt`, `expiresAt`, `lastChangedAt` | Registration dates |
| `domainAgeDays`, `daysToExpiry` | Days since registration and days until expiry |
| `statuses` | EPP status values |
| `rdapNameservers`, `dnssecSigned` | Name servers at the registry and DNSSEC state |
| `aRecords`, `aaaaRecords`, `mxRecords`, `nsRecords`, `txtRecords` | DNS records |
| `cnameRecords`, `wwwCnameRecords` | CNAME targets of the domain and of its www host |
| `soaRecord` | SOA record: primary name server, hostmaster, serial and timers |
| `spfRecord`, `spfAllQualifier`, `spfIncludes`, `spfLookupMechanisms` | SPF record and its main parts |
| `dmarcRecord`, `dmarcPolicy`, `dmarcSubdomainPolicy`, `dmarcPct`, `dmarcRua` | DMARC record and its tags |
| `hasDkim`, `dkimSelectorsFound` | Whether a DKIM key answers at one of the listed selectors, and which ones |
| `emailProvider` | Mail service behind the MX records, such as Google Workspace or Microsoft 365 |
| `sslHost`, `sslIssuer`, `sslSubject`, `sslValidFrom`, `sslValidTo`, `sslDaysToExpiry` | Certificate facts |
| `sslTrusted`, `sslError`, `tlsProtocol` | Whether the chain is trusted for the host, the reason if it fails, and the TLS version |
| `errors` | Lookup problems other than missing records |

Fields stay empty when the source has no value. The Actor never guesses. A domain with no RDAP record shows `Domain not found in RDAP` in `errors`. That often means the domain is unregistered.

`spfLookupMechanisms` counts only the top-level mechanisms that need a DNS lookup. Nested includes are left out of the count. SPF allows 10 lookups in total.

### How to use the bulk WHOIS lookup

1. Paste domains or URLs into **Domains**. Duplicates are removed. One run checks at most 500 domains.
2. Turn off any check you do not need. Each check runs in parallel, so turning one off saves little time.
3. To get domain expiry alerts, set **Only domains or certificates expiring within (days)**, for example 30. Schedule the Actor weekly.
4. To track changes, turn on **Monitor mode** and schedule the Actor. After the first run, a domain is returned only when its registrar, expiry date, status, name servers, MX, SPF, DMARC or certificate changed.

| Option | Default | Meaning |
|---|---|---|
| Domains | 5 sample domains | Domains or URLs to check |
| Names to check across TLDs, TLDs for the names | None | Each name with each TLD, for example `acme` with `com`, `io`, `sa` |
| DKIM selectors | 9 common selectors | Selectors to look up for DKIM |
| Registration data (RDAP) | On | Registry data |
| DNS records, SPF and DMARC | On | DNS lookups |
| SSL certificate | On | TLS handshake on port 443 |
| Only domains or certificates expiring within (days) | 0 (off) | Keep only rows that expire soon |
| Monitor mode | Off | Return only changed domains |
| Monitor state key | `default` | Name of the monitor memory |
| Maximum domains | 100 | Stop after this many rows. The maximum is 500. Set 0 to use 500 |
| Domains checked in parallel | 5 | Parallel domains |

A test run checked 73 domains in 33 seconds. RDAP requests to each registry are paced to about 1.5 per second. A full run of 500 .com domains therefore takes about 6 minutes.

### WHOIS lookup pricing

The Actor uses pay-per-event pricing.

| Event | Free plan | GOLD plan and higher |
|---|---|---|
| Run start | $0.002 | $0.002 |
| Domain check | $0.002 ($2.00 per 1,000) | $0.0016 ($1.60 per 1,000) |

| Domains in one run | Free plan | GOLD plan and higher |
|---|---|---|
| 100 | $0.202 | $0.162 |
| 1,000 | $2.002 | $1.602 |

Costs include the run start fee.

The start fee is charged once per GB of run memory, at least once. The default memory is 256 MB, so most runs pay it once. Domains skipped by the expiry filter or by monitor mode cost nothing.

### Input and output example

Input from a real run on 21 October 2026:

```json
{
  "domains": ["nodejs.org", "debian.org"],
  "includeRdap": true,
  "includeDns": true,
  "includeSsl": true
}
```

One output row from that run (MX list shortened, TXT and AAAA records left out):

```json
{
  "domain": "nodejs.org",
  "registrationStatus": "registered",
  "registrationSource": "rdap",
  "rdapAvailable": true,
  "rdapServer": "https://rdap.publicinterestregistry.org/rdap/",
  "registrar": "1API GmbH",
  "registrarIanaId": "1387",
  "registeredAt": "2009-09-29T14:50:55.314Z",
  "expiresAt": "2027-09-29T14:50:55.314Z",
  "domainAgeDays": 6231,
  "daysToExpiry": 343,
  "statuses": ["client transfer prohibited"],
  "rdapNameservers": ["meera.ns.cloudflare.com", "pablo.ns.cloudflare.com"],
  "dnssecSigned": false,
  "aRecords": ["104.16.212.131", "104.16.213.131"],
  "mxRecords": ["10 aspmx.l.google.com", "20 alt1.aspmx.l.google.com"],
  "spfRecord": "v=spf1 include:aspmx.googlemail.com -all",
  "spfAllQualifier": "-all",
  "spfLookupMechanisms": 1,
  "dmarcPolicy": "reject",
  "dmarcSubdomainPolicy": "reject",
  "sslIssuer": "Google Trust Services (WE1)",
  "sslSubject": "nodejs.org",
  "sslValidTo": "2027-01-08T15:28:11.000Z",
  "sslDaysToExpiry": 79,
  "sslTrusted": true,
  "tlsProtocol": "TLSv1.3",
  "errors": []
}
```

### WHOIS and DNS lookup FAQ

#### Can I check one name across many TLDs?

Yes. Put the name in **Names to check across TLDs**, for example `acme`, and the TLDs in **TLDs for the names**, for example `com`, `io`, `sa`. Each pair gets one row with `registrationStatus`.

```json
{ "names": ["acme"], "tlds": ["com", "io", "sa"] }
```

#### How does the DKIM check work?

DKIM keys sit at `<selector>._domainkey.<domain>`, and DNS has no list of selectors. The Actor tries the selectors in **DKIM selectors**. `hasDkim: false` means none of them answered. Add your mail service's selector if you know it.

#### Is RDAP the same as WHOIS?

RDAP is the newer protocol for the same registration data. ICANN made it the standard for generic top-level domains. It returns structured JSON, so the fields are reliable.

#### Does it return the domain owner's name or email?

No. The Actor reads only the registrar entity. Registrant, admin, tech and billing contacts are never read or saved. Most registries redact them anyway.

#### Can it check domain availability?

Yes. Each row has `registrationStatus`. The registry's RDAP answer decides it first. `available` means the registry has no record of the name and DNS shows no delegation. A registrar can still refuse a reserved or premium name, so confirm at a registrar before you buy. For a TLD without an RDAP server, a domain with DNS records shows `registered` with `registrationSource` set to `dns`. A domain with no records there shows `unknown`.

#### Why are registration fields empty for my .io or .de domain?

Those registries publish no RDAP server in the IANA bootstrap yet. DNS, SPF, DMARC and SSL results still come back for them.

#### Can I get an alert before a domain or SSL certificate expires?

Yes. Set the expiry filter to a number of days and schedule the Actor. Only domains or certificates that expire within that window are saved.

#### Can AI agents use it?

Yes. The input is a plain list of domains. Each output row is flat JSON with clear field names.

#### How many domains can one run check?

Up to 500. Split larger lists over several runs.

### Data sources and fair use

- Registration data comes from official registry RDAP servers.
- RDAP requests are paced per registry. Registry terms forbid high-volume automated queries and use of the data for spam. Use the Actor for your own checks at a moderate volume.
- DNS answers come from public resolvers. The SSL check opens a TLS connection and sends no HTTP request.

### Related Actors

- [Technical SEO Audit](https://apify.com/maliksaid/technical-seo-audit): crawl a website and find technical SEO problems.
- [Regulator Circulars Monitor](https://apify.com/maliksaid/regulator-circulars-monitor): track new circulars from the Central Bank of Jordan and the Saudi National Cybersecurity Authority.

### Support

Report a problem in the Issues tab. Include the run link.

# Actor input Schema

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

Domains to check, one per line. You can paste URLs. The Actor keeps the host name and drops a leading www. At most 500 domains per run.

## `names` (type: `array`):

Optional. Names without a TLD, for example acme. Each name is checked with every TLD below. The pairs count toward the 500-domain cap.

## `tlds` (type: `array`):

TLDs to pair with each name, without the dot, for example com, net, io, sa.

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

Look up registrar, creation date, expiry date, status and name servers from the registry's official RDAP server. Registrant contact details are never collected.

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

Look up A, AAAA, MX, NS and TXT records, and parse the SPF and DMARC records.

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

DKIM selectors to look up at <selector>.\_domainkey.<domain> when DNS lookups are on. DNS has no list of selectors, so add your own if you know it.

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

Open one TLS connection on port 443 and read the certificate issuer, validity dates and days to expiry.

## `expiringWithinDays` (type: `integer`):

Keep only domains whose registration or SSL certificate expires within this many days. Rows that do not match are not saved and not charged. 0 keeps every domain.

## `monitorMode` (type: `boolean`):

Return a domain only when its registrar, expiry date, status, name servers, MX, SPF, DMARC or certificate changed since the last monitor-mode run with the same state key. The first run returns every domain.

## `stateKey` (type: `string`):

Name of the memory that monitor mode uses. Use a different key for each separate watch list.

## `maxItems` (type: `integer`):

Stop after saving this many domains. The default keeps test runs short and cheap. The maximum is 500. Set 0 to use the maximum of 500.

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

How many domains are checked at the same time. RDAP requests to each registry are paced to about 1.5 per second whatever this value is.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "wikipedia.org",
    "python.org",
    "mozilla.org",
    "bbc.co.uk"
  ],
  "names": [],
  "tlds": [
    "com",
    "net",
    "org"
  ],
  "includeRdap": true,
  "includeDns": true,
  "dkimSelectors": [
    "default",
    "google",
    "selector1",
    "selector2",
    "k1",
    "s1",
    "s2",
    "dkim",
    "mail"
  ],
  "includeSsl": true,
  "expiringWithinDays": 0,
  "monitorMode": false,
  "stateKey": "default",
  "maxItems": 100,
  "maxConcurrency": 5
}
```

# 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",
        "wikipedia.org",
        "python.org",
        "mozilla.org",
        "bbc.co.uk"
    ],
    "tlds": [
        "com",
        "net",
        "org"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("maliksaid/domain-intelligence-rdap").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",
        "wikipedia.org",
        "python.org",
        "mozilla.org",
        "bbc.co.uk",
    ],
    "tlds": [
        "com",
        "net",
        "org",
    ],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("maliksaid/domain-intelligence-rdap").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",
    "wikipedia.org",
    "python.org",
    "mozilla.org",
    "bbc.co.uk"
  ],
  "tlds": [
    "com",
    "net",
    "org"
  ],
  "maxItems": 100
}' |
apify call maliksaid/domain-intelligence-rdap --silent --output-dataset

```

## MCP server setup

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

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/OWJXTBn5IJDYvJs06/builds/STWSLhmLJarfaEIoM/openapi.json
