# Domain WHOIS/RDAP Lookup — Registrar, Age & Registrant Data (`foxlabs/domain-rdap-registration-data`) Actor

Look up domain registration data over RDAP, the structured successor to WHOIS. Returns registrar and IANA ID, creation, expiry and last-changed dates, domain age, EPP status codes, nameservers, DNSSEC and any published registrant organization.

- **URL**: https://apify.com/foxlabs/domain-rdap-registration-data.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 company records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Domain WHOIS/RDAP Lookup — Registrar, Age & Registrant Data

Domain age and registrar are two of the cheapest, most reliable signals in B2B data: a counterparty whose domain was registered three weeks ago is a different risk from one registered in 2004. This actor reads RDAP — the structured JSON replacement for WHOIS — so you get real dates and status codes instead of scraped free text, plus the registrant organization and country wherever the registry or registrar still discloses them.

**No API key · Official source · Pay only for delivered rows · Same schema across the series**

### What data do you get?

| Field | Description |
|---|---|
| `companyName` | Registrant organization where one is disclosed, otherwise the domain |
| `registrantOrganization` | Registrant organization where disclosed — never a privacy service or a redaction placeholder |
| `domain` | The registrable domain that was looked up |
| `registrar` | Sponsoring registrar |
| `registrarIanaId` | Registrar's IANA ID |
| `incorporatedOn` | Domain creation date |
| `expiresOn` | Registration expiry date |
| `lastChangedOn` | Last update to the registration |
| `domainAgeDays` | Days since registration — the field most risk rules key on |
| `status` | `active`, or `inactive` when the domain is on hold or pending delete |
| `eppStatusCodes` | Raw EPP status codes (`clientTransferProhibited`, `serverHold` …) |
| `nameservers` | Authoritative nameservers |
| `nameserverProvider` | Second-level domain of the nameservers — usually the DNS or hosting vendor |
| `dnssec` | Whether DNSSEC is signed |
| `registrantCountry`, `countryName` | Registrant country (ISO code and name) — most registrars keep it even when the rest is redacted |
| `address`, `city`, `postalCode` | Registrant address, where disclosed |
| `email`, `phone` | The registrant's contact when its identity is disclosed (and the email is not a privacy relay); otherwise the email and phone the domain's own website publishes |
| `emailSource`, `phoneSource` | *RDAP registrant*, or the website page and how the value was stated there (*mailto link*, *tel link*, *JSON-LD*) |
| `website` | The site the domain actually serves, checked live with redirects followed |
| `privacyService` | The privacy or proxy service registered in the owner's place, when there is one |
| `isRedacted` | True when the registrant's identity is withheld — redacted, hidden behind a privacy service, or not published by the registry |
| `sourceUrl` | The RDAP record that was read |

Every row also carries `query` (what you asked for), `scrapedAt` (ISO timestamp) and, when a
lookup fails, `error` explaining why.

**Not part of RDAP.** `taxNumber`, `legalForm`, `dissolvedOn`, `industry`, `industryCode`,
`employees`, `capital` and `officers` belong to the company schema shared across this series and
stay `null` here.

### Example output

A real row for `shopify.ca`, one of the registries that still publishes the registrant in full:

```json
{
  "country": "GLOBAL",
  "registry": "RDAP",
  "companyName": "Shopify Inc.",
  "registrantOrganization": "Shopify Inc.",
  "domain": "shopify.ca",
  "registrar": "MarkMonitor International Canada Ltd.",
  "incorporatedOn": "2006-02-20",
  "expiresOn": "2028-02-20",
  "lastChangedOn": "2026-01-19",
  "domainAgeDays": 7521,
  "status": "active",
  "eppStatusCodes": ["client delete prohibited", "client transfer prohibited", "client update prohibited"],
  "nameservers": ["blue.foundationdns.com", "blue.foundationdns.net", "blue.foundationdns.org"],
  "nameserverProvider": "foundationdns.com",
  "dnssec": false,
  "address": "151 O'Connor Street, Ground Floor, Ottawa, ON, K1A2L8, Canada",
  "city": "Ottawa",
  "postalCode": "K1A2L8",
  "countryName": "Canada",
  "registrantCountry": "CA",
  "email": "domains@shopify.com",
  "phone": "+1.6132412828",
  "website": "https://www.shopify.com",
  "privacyService": null,
  "isRedacted": false,
  "sourceUrl": "https://rdap.ca.fury.ca/rdap/domain/shopify.ca"
}
```

For a `.com` behind a privacy service the same lookup reads, for example, `"companyName": "notion.com"`, `"privacyService": "c/o whoisproxy.com"`, `"isRedacted": true`.

### Input

```json
{
  "queries": ["stripe.com","shopify.com","https://www.notion.com/pricing"],
  "maxResultsPerQuery": 1,
  "maxConcurrency": 5,
  "includeRaw": false
}
```

| Input | What it does |
|---|---|
| `queries` | Domain names or URLs (`stripe.com`, `https://shopify.com/pricing`). Subdomains are reduced to the registrable domain. |
| `maxResultsPerQuery` | Caps how many rows one query may produce. |
| `maxConcurrency` | How many queries run at once. Lower it if the source starts throttling. |
| `includeRaw` | Attaches the source's untouched record under `raw`, for fields this actor does not map. |
| `requestDelayMs` | Politeness delay between requests. |
| `proxyConfiguration` | Optional. RDAP servers answer datacenter IPs, though some registries rate-limit by IP. |

### What people use it for

- **Fraud and risk screening** — domain age is one of the strongest single signals in vendor and merchant onboarding.
- **Lead qualification** — a registrar like MarkMonitor or CSC means a corporate legal team; a $9 reseller means a small business.
- **Portfolio monitoring** — expiry dates across a domain portfolio, in one table.

### Notes and limits

- **Website contacts are only what the site states explicitly**: `mailto:` and `tel:` links (Cloudflare-protected addresses included) and schema.org JSON-LD, on the homepage or the contact page it links. Nothing is guessed out of running text. An email must be on the domain itself (info@acme.com for acme.com; a web agency's or a provider's address never qualifies), role mailboxes (info@, contact@, sales@…) are preferred over a person's, and template placeholders (example.com, 555-01xx, 123-456-7890) are dropped. On 30 randomly drawn small-company domains, 22 homepages answered; 13 gave an email and 12 a phone, and a manual check found all 25 values to be the company's own published contact.
- Since GDPR, most registries and registrars redact the registrant's name and address. `isRedacted` tells you when that has happened, so a `companyName` that is just the domain is explained rather than mysterious. Only disclosed values are returned: redaction placeholders, privacy-service details and email relays are never passed off as the owner's.
- **When the registry names no registrant — .com and .net never do — the actor follows the registry's link to the registrar's own RDAP server**, where the registrant lives: one more request per domain. A few registrar servers fail on some TLDs (Tucows on `.app`); such rows keep the registry fields and `isRedacted` is `null`.
- **Some registries are missing from IANA's RDAP directory** — the one rdap.org routes by. `.io`, `.me`, `.co`, `.us`, `.de`, `.ch` and `.so` run RDAP anyway and are queried directly (`.de` and `.ch` publish little beyond status and nameservers). Others, among them `.se`, `.jp`, `.es`, `.it`, `.eu` and `.cn`, have no RDAP server this actor can use; such a query returns a free row that says so.

### Where the data comes from

RDAP is the IETF-standard registration data protocol that every gTLD registry is required to serve. The actor finds the authoritative server for each TLD in IANA's RDAP directory (plus the few registries missing from it) and queries it directly, with no key. Sources: [IANA RDAP bootstrap](https://data.iana.org/rdap/dns.json), [About RDAP](https://about.rdap.org/)

### FAQ

#### Is this domain RDAP lookup scraper free?

The data source is free and needs no API key — you pay only for the rows the run delivers ($0.002 each). Failed or empty lookups are never charged.

#### Do I need an API key or a login?

No. RDAP is the IETF-standard registration data protocol that every gTLD registry is required to serve, and registries and registrars answer it without a key.

#### What can I search by?

By domain name or any URL containing one — `stripe.com`, `https://www.stripe.com/pricing` and `WWW.STRIPE.COM` all resolve to the same lookup.

#### How current is the data?

Every run queries the source live, so results are as fresh as the source itself. RDAP answers live from the authoritative registry.

#### How fast is it, and how many queries can I run?

Queries run concurrently (5 at a time by default, tunable in the input). A .com or .net lookup costs one more request, to the registrar, and every domain one request to its website, plus one to its contact page when the homepage lacks an email or phone. A prefilled run finishes in seconds; large lists scale roughly linearly and stay well inside a normal run timeout.

#### Can I export the results to CSV, Excel or JSON?

Yes. Apify datasets export to CSV, Excel, JSON, XML and HTML, and can be pulled through the API or pushed to your own storage.

#### What happens when a query returns nothing?

You still get a row, carrying your original `query` and an `error` field explaining why. Nothing is silently dropped, and you are not charged for it.

#### Is scraping this data legal?

Yes. RDAP is the registration data registries are required to publish. Since GDPR most registrant contact details are redacted at source — this actor returns what the registry or registrar publishes and never attempts to unmask redacted data.

### Changelog

#### 0.1.10 — 2026-09-25 — contact from the website

- **`email` and `phone` are filled from the domain's own website when the registrant's are withheld** — which, since GDPR, is most domains. Only `mailto:`/`tel:` links and schema.org JSON-LD count, an email must be on the domain itself, and placeholders are dropped. On 30 random small-company domains: 13 emails and 12 phones, every one checked by hand to be the company's own published contact.
- **New `emailSource` and `phoneSource`** say where each came from: the RDAP registrant, or the website page and how it was stated.
- Up to one more request per domain (the contact page, only when the homepage lacks an email or phone). No pricing change.

#### 0.1.9 — 2026-09-25 — live website

- **`website` is filled with the site the domain actually serves**: one request to the domain, redirects followed, so a rebrand or consolidation shows up — angel.co → https://wellfound.com, vercel.app → https://vercel.com, shopify.ca → https://www.shopify.com. A server that refuses bots still counts as a site; a domain where nothing answers gets none. 24 of 25 domains in a test.
- One more request per domain. No pricing change.

#### 0.1.8 — 2026-09-25 — registrant data filled; .io, .co, .de and more now resolve

- **Registrant organization and country are filled** wherever the registry or registrar discloses them. `.com` and `.net` keep no registrant at the registry, so the registrar's own RDAP record is now read as well. On the 20 domains of the pre-release audit: registrant organization 2 → 9 (OpenAI, Canva, GitHub, Booking.com …), registrant country 0 → 10, failed lookups 5 → 2.
- `registrantCountry` was empty in practice (0 of 15 audit rows): it read the country-name slot of the vCard address, which registrars leave blank in favour of the `cc` code.
- **Redaction is no longer passed off as data.** `companyName` could read `REDACTED FOR PRIVACY` (zalando.nl). Redaction placeholders, privacy services (now named in `privacyService`) and email relays are recognised, and `isRedacted` covers all three — it used to say `false` for .com domains whose registrant simply sat at the registrar.
- **`.io`, `.me`, `.co`, `.us`, `.de`, `.ch` and `.so` domains now resolve.** Their registries are missing from IANA's RDAP directory, so every such lookup failed as "No RDAP registration found". TLDs with no usable RDAP server (`.se`, `.jp` …) now say exactly that.
- New fields `registrantOrganization` and `privacyService`; `address`, `city`, `postalCode`, `countryName`, `email` and `phone` are filled where disclosed. `sourceUrl` now points at the RDAP server that answered.
- README example replaced with a real row — the old one showed a registrar and dates stripe.com does not have.
- No pricing change.

***

Built by [Fox Labs](https://apify.com/foxlabs) — B2B company intelligence from public sources, as clean JSON.

# Changelog

This Actor's version history is a separate document: https://apify.com/foxlabs/domain-rdap-registration-data/changelog.md

# Actor input Schema

## `queries` (type: `array`):

Domain names or URLs (`stripe.com`, `https://shopify.com/pricing`). Subdomains are reduced to the registrable domain.

## `maxResultsPerQuery` (type: `integer`):

How many rows a single query may produce.

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

How many queries to run at the same time. Lower it if the source throttles you.

## `includeRaw` (type: `boolean`):

Attach the source's untouched response under `raw`. Useful when you need a field this actor does not map.

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

Politeness delay against a public source. Raise it for large runs.

## `proxyConfiguration` (type: `object`):

Optional. RDAP servers answer datacenter IPs, though some registries rate-limit by IP.

## Actor input object example

```json
{
  "queries": [
    "stripe.com",
    "shopify.com",
    "https://www.notion.com/pricing"
  ],
  "maxResultsPerQuery": 1,
  "maxConcurrency": 5,
  "includeRaw": false,
  "requestDelayMs": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (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 = {
    "queries": [
        "stripe.com",
        "shopify.com",
        "https://www.notion.com/pricing"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/domain-rdap-registration-data").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 = { "queries": [
        "stripe.com",
        "shopify.com",
        "https://www.notion.com/pricing",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/domain-rdap-registration-data").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 '{
  "queries": [
    "stripe.com",
    "shopify.com",
    "https://www.notion.com/pricing"
  ]
}' |
apify call foxlabs/domain-rdap-registration-data --silent --output-dataset

```

## MCP server setup

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

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/NksWV4tadBNak0D9M/builds/o3jZVHn4zAszjbDEu/openapi.json
