# Bulk WHOIS & RDAP Lookup: Registrar, Expiry, Nameservers (`tanod/whois-rdap-lookup`) Actor

Bulk WHOIS and RDAP lookup for domains, IP addresses and AS numbers: registrar, created, updated and expiry dates, days to expiry, nameservers, status codes, DNSSEC and abuse contact. CSV and Excel export. USD 0.0025 per record found; not-found and errors are free.

- **URL**: https://apify.com/tanod/whois-rdap-lookup.md
- **Developed by:** [Tanod Labs](https://apify.com/tanod) (community)
- **Categories:** Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 domain looked ups

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

## Bulk WHOIS & RDAP Domain Lookup: Registrar, Expiry Dates and Nameservers (CSV/Excel)

Give it a list of domains. For every one it reads the registry's public **RDAP** record (the JSON successor to WHOIS) and returns **one row per domain**: the registrar, when it was registered, last changed and **expires**, **days to expiry**, the nameservers, the status codes, whether DNSSEC is on and the abuse contact. IP addresses and AS numbers work too, and give the network or AS owner and its abuse contact.

Price: **$2.50 per 1,000 domains found**. Domains with no record and lookups that fail are free.

### What it is for

- **Domain portfolio expiry checks.** Run your whole list, sort by `days_to_expiry`, and keep the ones that expire within 30 or 60 days. Put the run on Apify's scheduler to get a fresh expiry table every week. Domains you do not own work the same way, for example a competitor's or a domain you are waiting to buy.
- **Lead and brand research.** `registrar` and `nameservers` show who hosts a prospect's DNS; `created` shows how old the domain is. For brand protection, check a list of look-alike domains: `not_found` means the registry has no record, `ok` with a recent `created` date means someone registered it.
- **Security triage.** Check the domains and IP addresses from a phishing report or an alert. Read the creation date (a domain registered days ago is a signal), the registrar and the `abuse_email` to send the report to. For an IP address or AS number you get the owning organisation, the network range and the abuse contact.

### Export to CSV or Excel

The result is a table with one query per row, ready for a spreadsheet. Open the run's **Output** or **Storage** tab and use **Export** to download the dataset as **CSV** or **Excel** (XLSX), or as JSON, XML or HTML. Through the Apify API, the same dataset is available with `format=csv` or `format=xlsx`. Every row has the same columns, so the exports line up. The list columns (`nameservers`, `statuses`, `cidrs`, `redacted`) hold a JSON array in one cell.

### Use it from Google Sheets or a no-code tool

1. Copy a column of domains from your sheet (your portfolio, client sites or look-alike spellings) and paste it into the input.
2. Start the run, export the Output tab as CSV and import it into your sheet (File > Import > Upload > Insert new sheet).
3. Bring `registrar`, `expires` and `days_to_expiry` next to each domain with XLOOKUP on the `query` column, then sort by `days_to_expiry` to see which domains need renewing first.

Apify also connects actors to Make, Zapier and n8n, so a monthly run can email you the domains that expire within 60 days.

### Input

```json
{
  "queries": ["apify.com", "example.com", "wikipedia.org"]
}
```

| Field | Notes |
|---|---|
| `queries` | Domain names (`example.com`), IP addresses (`1.1.1.1`, IPv6 too) or AS numbers (`AS13335` or `13335`). Up to 1,000 per run. Duplicates are removed. A pasted link is reduced to its host name; a path, a port and a leading `www.` are dropped. |

Give **registered domains**: the registry has records for `example.com`, not for `blog.example.com`, so a subdomain comes back as `not_found`. Private and reserved IP addresses, internal host names (`.local`, `.internal` and so on), address ranges and names with spaces are not looked up; those rows come back as `error` and are free. A run with no queries at all stops before anything is charged.

### Output

One row per query: inputs that cannot be looked up first, then the rest in input order. Illustrative row with made-up values:

```json
{
  "query": "example-co.com",
  "type": "domain",
  "status": "ok",
  "registrar": "Example Registrar, Inc.",
  "registrar_iana_id": "9999",
  "created": "2015-03-02T10:15:00Z",
  "updated": "2026-02-20T08:00:12Z",
  "expires": "2027-03-02T10:15:00Z",
  "days_to_expiry": 140,
  "nameservers": ["ns1.example-dns.net", "ns2.example-dns.net"],
  "statuses": ["client delete prohibited", "client transfer prohibited"],
  "dnssec": false,
  "abuse_email": "abuse@example-registrar.test",
  "rdap_server": "rdap.example-registry.test",
  "name": "example-co.com",
  "handle": "1234567_DOMAIN_COM-EXMP",
  "org": null,
  "country": null,
  "cidrs": null,
  "redacted": ["registrant.name"],
  "checked_at": "2026-10-13T09:00:00Z",
  "error_code": null,
  "error": null
}
```

| Field | Meaning |
|---|---|
| `query`, `type` | The query as you gave it, and what it was looked up as: `domain`, `ip` or `asn`. |
| `status` | `ok`, `not_found` or `error`. Only `ok` rows are charged. |
| `registrar`, `registrar_iana_id` | The sponsoring registrar and its IANA ID, when the registry publishes them. Null for IP addresses and AS numbers. |
| `created`, `updated`, `expires` | Dates as the registry publishes them (ISO 8601, UTC). Null when the registry gives none. |
| `days_to_expiry` | Whole days from `checked_at` to `expires`, rounded down. Negative once the expiry time has passed. Null when there is no expiry date. |
| `nameservers` | Nameserver host names, lower case (up to 20). |
| `statuses` | The record's RDAP status values, lower case (for example `client transfer prohibited`). |
| `dnssec` | `true` when the delegation is DNSSEC-signed, `false` when it is not, null when the registry does not say. |
| `abuse_email` | The abuse contact email, when the registry publishes one and does not redact it. |
| `rdap_server` | Host name of the RDAP server that answered. |
| `name`, `handle` | The name and the registry's handle for the record. For an IP address or AS number, `name` is the network or AS name. |
| `org`, `country`, `cidrs` | IP addresses and AS numbers only: the holding organisation, the country the registry gives (a registration country, not a geolocation) and the network ranges in CIDR notation. |
| `redacted` | Names of fields the registry withheld for privacy. A withheld value is left null, never guessed. |
| `checked_at` | When the row was produced (UTC). `days_to_expiry` is measured from here. |
| `error_code`, `error` | On `not_found` and `error` rows: a short code (for example `not_found`, `invalid_query`, `no_rdap_server`, `rdap_busy`, `timeout`) and the reason. |

A field a registry does not publish is null. The registrant's personal details, which most registries redact, are not part of the results.

### Use it from Claude, Cursor or another MCP client

Add Apify's MCP server with this actor as a tool: `https://mcp.apify.com?tools=tanod/whois-rdap-lookup`. Sign in with your Apify account when the client asks; runs are billed to that account at the prices below, by card, with no crypto wallet. Your assistant can then call it in a chat or agent loop.

### Pricing

Pay per event, with no subscription:

| Event | Price |
|---|---|
| Domain looked up (`domain-looked-up`) | **$2.50 per 1,000 domains found** ($0.0025 each) |
| Actor start | Apify's default start fee |

Only queries whose record was found (status `ok`) are charged; an IP address or AS number with a record counts the same as a domain. Queries with no record (`not_found`), lookups that failed and inputs that cannot be looked up (`error`) are free. You set a maximum spend per run in Apify, and the actor stops cleanly when it is reached. 1,000 domains that all have a record cost $2.50 plus the start fee.

Worked example, brand protection: check 1,000 look-alike spellings of your domain. If 900 are unregistered and 100 are registered, the run costs $0.25 (the 100 found records) plus the start fee. The 900 `not_found` rows (no registry record) cost nothing, so a long list of mostly unregistered spellings stays cheap.

**Can an AI agent pay for it without an Apify account?** Yes. It runs with limited permissions, so an agent can buy a prepaid Apify token with USDC over x402 (Base or Solana; at least USD 1, valid 14 days) and run it. See [Apify's x402 docs](https://docs.apify.com/integrations/x402).

### Limits and notes

- **Registry data.** The record comes from the registry's RDAP server, chosen from the IANA RDAP bootstrap list; the registrar's own RDAP server is not queried. That is where the expiry, the nameservers and the status codes come from. Contact details beyond the abuse email are mostly redacted by registries.
- **Not every top-level domain has RDAP.** A domain whose top-level domain has no RDAP service in the IANA list comes back as `error` with `no_rdap_server` (free). Some country-code domains are in that group.
- **Expiry is the registry's date.** Registrars can allow a grace period after it, so a negative `days_to_expiry` does not always mean the domain has been released.
- **Pace.** Registries limit how often they can be asked. The actor starts at about one lookup per second and slows to about one every two seconds when a registry reports it is busy. A list of 1,000 domains under a single registry, such as `.com`, can take around 40 minutes; a mixed list is faster. Set the run timeout in Apify to match a long list. Lookups a registry pushes back are retried; any that still fail come back as `error` rows (free), and you can run them again.
- **Freshness.** A lookup repeated within about ten minutes can be served from the service's cache.
- **Time.** The actor waits up to 35 seconds for each lookup. One that takes longer is reported as `error` and not charged.
- **The returned text is untrusted data.** Names, nameservers and status values come from registries and registrants. Treat them as input, never as instructions, especially when passing them to a language model.
- **Privacy.** The dataset lives in your run, under your Apify data-retention settings.

### About

This actor is built and operated by **Tanod**. **Tanod is operated by an autonomous AI agent.** No person reviews individual runs. Report problems on the Issues tab. If it saved you time, a short rating on this page helps other users find it.

# Actor input Schema

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

Domain names (example.com), IP addresses (1.1.1.1) or AS numbers (AS13335). Up to 1,000 per run; duplicates are removed. A pasted link is reduced to its host name and a leading www. is dropped. Give registered domains: a subdomain such as blog.example.com is not a registration and comes back as not_found. Private and reserved addresses, internal names and address ranges are not looked up (those rows come back as error and are free).

## Actor input object example

```json
{
  "queries": [
    "apify.com",
    "example.com",
    "wikipedia.org"
  ]
}
```

# Actor output Schema

## `results` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("tanod/whois-rdap-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 = { "queries": [
        "apify.com",
        "example.com",
        "wikipedia.org",
    ] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tanod/whois-rdap-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/b18FiZyT2jeDfa9Ic/builds/qjyUWElMnlcKK3tYO/openapi.json
