# RDAP Lookup: Domain, IP & ASN Registration Data (Modern WHOIS) (`winterlock/rdap-domain-lookup`) Actor

Look up domain registration, expiry, registrar, nameservers and DNSSEC straight from the registries over RDAP, the official structured replacement for WHOIS. Also covers IP networks and AS numbers. No API key, no rate-limited WHOIS proxy.

- **URL**: https://apify.com/winterlock/rdap-domain-lookup.md
- **Developed by:** [Alex Burton](https://apify.com/winterlock) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$8.00 / 1,000 lookup (domain, ip or asn)s

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

## RDAP Lookup: Domain, IP & ASN Registration Data

Registration data for domains, IP networks and AS numbers, read straight from the registries over **RDAP** — the IETF protocol that officially replaced WHOIS. No API key, no scraping, no third-party WHOIS proxy sitting between you and the answer.

| Mode | What you get |
|---|---|
| **Domains** | Registrar and IANA ID, registration / expiry / last-changed dates, nameservers, DNSSEC, EPP status codes, transfer lock, registrant and abuse contacts |
| **IP addresses** | Network name and range, CIDR blocks, allocating organization, allocation type, country, abuse contact |
| **AS numbers** | AS name, number range, holding organization, country, abuse contact, registration dates |

### Why use this one

- **It queries the real registry, not a middleman.** The Actor loads IANA's official bootstrap file once per run and maps each top-level domain to its own registry's RDAP server. Most tools funnel every lookup through one redirector, which adds a hop and a single point of failure.
- **It tells you when there is no answer, and why.** If a country-code TLD publishes no RDAP service at all, you get a row saying exactly that. If a domain is unregistered, you get a row saying that. You never get a half-empty record you have to interpret.
- **It does the flattening that makes RDAP usable.** Raw RDAP hides the expiry date inside an `events` array keyed by `eventAction`, the registrar inside an `entities` array keyed by `roles`, and every contact inside a jCard `vcardArray`. This Actor unpacks all three into plain spreadsheet columns.
- **It gets addresses right.** jCard's `adr` property normally carries seven empty strings as its value and the readable address in a `label` parameter. Parsers that read only the value array lose the address. This one prefers the label.
- **Bulk by design.** Paste a thousand domains and get a thousand rows, with the failures labelled rather than dropped.

### How to use it

1. Choose **What to look up**.
2. Paste your **Domains**, **IP addresses** or **AS numbers**, one per line.
3. Click **Start**, then download as JSON, CSV, Excel or HTML, or fetch through the Apify API.

Domains are cleaned up for you, so all of these work and mean the same thing:

```
https://www.example.com/pricing?ref=x
www.example.com
example.com.
```

#### Example input: check a portfolio for expiry

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

#### Example input: who owns this IP range

```json
{
  "mode": "ip",
  "ips": ["8.8.8.8", "1.1.1.1"],
  "includeRawEntities": true
}
```

### Output

**Domain**

```json
{
  "type": "domain",
  "query": "example.com",
  "found": true,
  "domain": "example.com",
  "tld": "com",
  "handle": "2336799_DOMAIN_COM-VRSN",
  "registrar": "RESERVED-Internet Assigned Numbers Authority",
  "registrarIanaId": 376,
  "registeredOn": "1995-08-14T04:00:00Z",
  "expiresOn": "2027-08-13T04:00:00Z",
  "lastChangedOn": "2026-08-14T08:01:43Z",
  "statuses": ["client delete prohibited", "client transfer prohibited", "client update prohibited"],
  "locked": true,
  "nameservers": ["elliott.ns.cloudflare.com", "hera.ns.cloudflare.com"],
  "dnssec": true,
  "registrantName": null,
  "registrantRedacted": false,
  "registrarAbuseEmail": null,
  "error": null
}
```

**IP network**

```json
{
  "type": "ipNetwork",
  "query": "8.8.8.8",
  "found": true,
  "rir": "ARIN",
  "handle": "NET-8-8-8-0-2",
  "name": "GOGL",
  "organization": "Google LLC",
  "startAddress": "8.8.8.0",
  "endAddress": "8.8.8.255",
  "cidrs": ["8.8.8.0/24"],
  "ipVersion": "v4",
  "allocationType": "DIRECT ALLOCATION",
  "abuseEmail": "network-abuse@google.com",
  "registeredOn": "2023-12-28T22:24:33Z"
}
```

A lookup that cannot be answered still produces a row:

```json
{ "type": "domain", "query": "example.xyzzy", "found": false, "error": "the .xyzzy registry does not publish an RDAP service" }
```

### Pricing

| Result | Price |
|---|---|
| Lookup (domain, IP or ASN) | $8.00 per 1,000 |

Rows that report a problem are charged the same as rows that return data **when a registry was actually asked** — a name that does not exist still costs a lookup. Rows rejected before any request, such as an unreadable domain or a TLD that publishes no RDAP service at all, are saved to the dataset but not charged. Set a maximum spend per run in the run options and the Actor stops cleanly when it is reached.

### Limits to know about

These come from the registries, not from this Actor:

- **Contact details are usually redacted.** Since GDPR, most registries return the registrar and the dates but blank out registrant name, email and address. Placeholders such as "REDACTED FOR PRIVACY" or `redacted@registry` are turned into null rather than passed off as a real name or address, and `registrantRedacted` is `true` when the registry says it withheld them. Privacy-service relays (a proxy company's name and address) are real registrations of record and are kept. Abuse contacts are generally still published.
- **Not every TLD has RDAP.** Generic TLDs are required to run it. Many country-code registries (and `.uk` among them, at the time of writing) do not, and a few run it only for authenticated registrars. The Actor reports those by name rather than guessing.
- **Registries rate-limit.** A long batch against a single registry can start returning 429s; the Actor backs off and retries, and you can lower *Max parallel requests* or turn on a proxy.
- **Date precision varies.** Some registries publish only a date, others a full timestamp with a timezone. Everything is normalised to UTC.
- **IP and ASN lookups** go to ARIN by default, which redirects to RIPE, APNIC, LACNIC or AFRINIC when the range belongs to them; `rir` says which registry answered. You can point the Actor at a different registry directly. AFRINIC records often carry no name or abuse contact entity, so the Actor also reads the record's free-text remarks into `description` and takes an abuse address from them when one is given.

### FAQ

**How is this different from a WHOIS lookup?** WHOIS returns free text that every registry formats differently, so parsing it is guesswork. RDAP returns JSON with a defined schema, which is why dates and statuses here are reliable rather than best-effort.

**Can I monitor my domains for expiry?** Yes. Schedule the Actor and connect the dataset to Slack, Google Sheets, Zapier or Make, then alert on `expiresOn`.

**Why is a domain I know exists reported as not found?** Check the spelling first, then the TLD. If the registry publishes no RDAP service the `error` field will say so.

**Is this legal?** RDAP is a public, standardised service that registries are required to operate, and this Actor uses it as intended. Records may contain personal data, so handle any contact details you receive in line with the laws that apply to you.

### Feedback

Found a bug or need a field that's missing? Open an issue on the Actor's **Issues** tab.

# Actor input Schema

## `mode` (type: `string`):

Domain names, IP networks, or autonomous system numbers.

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

One per line. Full URLs, a leading www. and a trailing dot are all cleaned up automatically, so https://www.example.com/pricing becomes example.com. International names are converted to punycode.

## `ips` (type: `array`):

IPv4 or IPv6, with or without a prefix length, such as 8.8.8.8 or 2001:4860:4860::8888.

## `asns` (type: `array`):

Autonomous system numbers, with or without the AS prefix: 15169 or AS15169.

## `useIanaBootstrap` (type: `boolean`):

Loads IANA's official bootstrap file once per run to find each top-level domain's own RDAP server. This is faster, avoids a third-party redirector, and lets the Actor tell you when a TLD publishes no RDAP service at all. Turn it off to route everything through rdap.org instead.

## `ipRdapBase` (type: `string`):

ARIN answers for any address and redirects to the correct regional registry when the range is not its own. Change this only if you want to query RIPE, APNIC, LACNIC or AFRINIC directly.

## `includeRawEntities` (type: `boolean`):

Adds an entities array holding every contact in the record with its role, handle, name, email, phone and address. The headline registrar and registrant columns are always present either way.

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

Stop the run after saving this many items. Leave empty for no limit.

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

Registries rate-limit by IP and some are strict. Raise this only for large batches across many different top-level domains.

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

Not needed for ordinary runs. Turn on Apify Proxy if a registry starts rate-limiting a long batch.

## Actor input object example

```json
{
  "mode": "domains",
  "domains": [
    "example.com",
    "apify.com"
  ],
  "useIanaBootstrap": true,
  "ipRdapBase": "https://rdap.arin.net/registry",
  "includeRawEntities": false,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

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

// Run the Actor and wait for it to finish
const run = await client.actor("winterlock/rdap-domain-lookup").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",
        "apify.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("winterlock/rdap-domain-lookup").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": [
    "example.com",
    "apify.com"
  ]
}' |
apify call winterlock/rdap-domain-lookup --silent --output-dataset

```

## MCP server setup

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

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/EfuEH5DOUqxJ9hJPo/builds/oZxYszvjc0fH4Okgc/openapi.json
