# Bulk WHOIS & Domain Lookup: DNS Lookup and Domain Availability (`nebulacolors/domain-lookup`) Actor

Bulk WHOIS and domain lookup: check domain availability, domain age, registrar and expiration dates, status and nameservers from the registries' RDAP (WHOIS where a TLD has none), plus DNS records with MX, SPF and DMARC. A domain checker at $0.002 per domain.

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

## Pricing

from $1.40 / 1,000 domains

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: WHOIS via RDAP, DNS Records and Availability

Give it a list of domains and get back, for each one, who it's registered with, when it was created and when it expires, its status and nameservers, and whether it's available to register. The data comes from each registry's official RDAP server, the modern replacement for WHOIS, and from WHOIS for the TLDs whose registry has no RDAP yet (like .jp, .cn, .it and .se). It also reads the DNS records, with the SPF and DMARC policies. $0.002 per domain, and domains where nothing answered are free. It works for people and for AI agents through Apify's MCP server.

### What it's for

- Check a list of name ideas for which ones are available. Available means the registry has no record of the name. A registrar can still hold it as reserved or sell it at a premium price, so check the price there before you plan on it.
- Some registries give less: .de shows no creation or expiry date, and .es answers only approved addresses, so .es rows have DNS records only, free.
- Research companies and leads: how old a domain is, who hosts its DNS, whether it receives email.
- Watch expiry dates: run it on an Apify schedule and sort by `expiresInDays`.
- Check email setup in bulk: MX, SPF and DMARC.

### Input

| Field | What to put |
|---|---|
| Domains | One per line, like `example.com`. Required. Website and email addresses work too: `https://www.example.co.uk/page` is looked up as `example.co.uk`. Subdomains are looked up as the domain they belong to: blog.example.com as example.com, and user.github.io as github.io. |
| Registration data | On by default: registrar, dates, status, nameservers, and whether the domain is registered. |
| DNS records | On by default: A, AAAA, MX, NS, TXT, CNAME, CAA and SOA, plus SPF and DMARC. |

With Registration data off, `registered` and `available` are null. With DNS records off, the DNS fields are left out. Both off is an error.

Example:

```json
{
  "domains": ["apify.com", "github.io", "bbc.co.uk"]
}
```

### Output

One row per domain. This one came from a test run on October 16, 2026 (DNS records cut short here):

```json
{
  "input": "github.io",
  "domain": "github.io",
  "tld": "io",
  "registered": true,
  "available": false,
  "source": "rdap",
  "rdapServer": "https://rdap.identitydigital.services/rdap/",
  "registrar": "MarkMonitor Inc.",
  "registrarIanaId": "292",
  "registrarUrl": "https://www.markmonitor.com/about-us/",
  "createdAt": "2013-03-08T19:12:48.000Z",
  "updatedAt": "2026-08-12T09:08:04.273Z",
  "expiresAt": "2027-03-08T19:12:48.000Z",
  "status": [
    "client delete prohibited",
    "client transfer prohibited",
    "client update prohibited"
  ],
  "nameservers": [
    "ns-692.awsdns-22.net",
    "ns-1622.awsdns-10.co.uk",
    "dns1.p05.nsone.net",
    "dns2.p05.nsone.net",
    "dns3.p05.nsone.net"
  ],
  "dnssec": false,
  "registrantOrganization": "GitHub, Inc.",
  "registrantCountry": "US",
  "abuseEmail": "abusecomplaints@markmonitor.com",
  "ageDays": 4969,
  "expiresInDays": 143,
  "dns": {
    "a": [
      "185.199.109.153",
      "185.199.110.153"
    ],
    "aaaa": [],
    "mx": [],
    "ns": [
      "dns1.p05.nsone.net",
      "dns2.p05.nsone.net"
    ],
    "txt": [
      "v=spf1 a -all"
    ],
    "cname": [],
    "caa": [
      "0 issue \"sectigo.com\""
    ],
    "soa": {
      "primaryNs": "ns-1622.awsdns-10.co.uk",
      "hostmaster": "awsdns-hostmaster.amazon.com",
      "serial": 1
    }
  },
  "spf": "v=spf1 a -all",
  "dmarc": null,
  "hasEmail": false,
  "resolves": true,
  "nxdomain": false,
  "checkedAt": "2026-10-16T08:34:03.342Z"
}
```

- `registered` is true or false from the registry, or null if it couldn't say. `available` is its opposite. A name the registry doesn't have can still be reserved or premium-priced at the registrar.
- Dates are ISO 8601 in UTC. `ageDays` and `expiresInDays` count from the time of the lookup.
- `registrantOrganization` and `registrantCountry` are filled only when public. Behind a privacy service, both are null.
- `source` is `rdap` or `whois`. With WHOIS, `whoisServer` names the server asked and `whoisText` has its answer.
- `hasEmail` is true when the domain has MX records, `resolves` when it has an A or AAAA record, and `nxdomain` when the name doesn't exist in DNS at all.
- `warning` says which part didn't answer, like registration data from a registry that refused.
- A domain where nothing answered has an `error` instead, and isn't charged.

The run's `SUMMARY` record says how many domains were looked up, registered, not registered and unknown, how many came from RDAP and from WHOIS, and how many failed.

### Price

- $0.002 per domain looked up, so $2 per 1,000.
- That's the price on Apify's free plan. On a paid Apify plan it's less: $1.80 per 1,000 domains on Starter, $1.60 on Scale and $1.40 on Business, the "from" price at the top of this page.
- Registration data asked for but missing (like most .es): the row keeps its DNS records and isn't charged (`charged` is false). With Registration data off, a DNS result is charged.
- Free: entries that aren't domains, duplicates (the same domain twice, or two addresses on one domain, get one row), and domains where nothing answered.
- You can set a maximum charge for a run. It stops when it's reached and keeps what it already looked up.

### Good to know

- **Official sources.** RDAP servers are the ones IANA lists for each TLD, and WHOIS servers the ones IANA names. Thin registries like .com point to the registrar's own RDAP, which it also reads for the owner's details.
- **Polite.** Requests to the same registry wait their turn, and a rate-limited server is asked again after a pause.
- **Some registries say little.** DENIC (.de) gives only the status, nameservers and last change.
- **Some registries can't be read.** .es answers only approved addresses, and .cn's WHOIS sometimes times out. Those domains still get their DNS records, free, with a `warning`.
- **Speed.** Each registry is asked about twice a second, so a list that mixes TLDs goes faster. In test runs, 1,000 .com domains took 8.5 minutes and a mixed list of 60 took 41 seconds.

### Use it from an AI agent

The Actor works through Apify's MCP server like any Store Actor. An agent can find it, read its input and call it. The MCP call waits 30 seconds by default (45 at most), and a few domains finish in that time. A bigger list takes longer, so the call returns the run's status and dataset ID, and the agent reads the rows with `get-dataset-items`.

### Questions or problems

Open an issue on this Actor's Issues tab. I read them. If it saves you time, a short review helps others find it.

# Actor input Schema

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

One per line, like example.com. Website addresses and email addresses work too: https://www.example.co.uk/page is looked up as example.co.uk.

## `registration` (type: `boolean`):

Registrar, creation, update and expiry dates, status, nameservers, DNSSEC, the registrant's organisation and country when public, and whether the domain is registered. From the registry's RDAP server, or WHOIS where the TLD has no RDAP.

## `dns` (type: `boolean`):

A, AAAA, MX, NS, TXT, CNAME, CAA and SOA records, plus the SPF and DMARC policies.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "github.io",
    "bbc.co.uk"
  ],
  "registration": true,
  "dns": true
}
```

# Actor output Schema

## `domains` (type: `string`):

All domain rows as JSON: registered and available, registrar, createdAt, expiresAt, status, nameservers, registrant organisation and country when public, DNS records, SPF and DMARC. A domain that failed has an error row instead.

## `summary` (type: `string`):

Domains looked up, registered, not registered and unknown, how many came from RDAP and WHOIS, failures, and why the run stopped.

# 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",
        "github.io",
        "bbc.co.uk"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nebulacolors/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": [
        "apify.com",
        "github.io",
        "bbc.co.uk",
    ] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nebulacolors/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/n81FqLLsQgyyDhtRe/builds/5d09VDvgDMTojSQvS/openapi.json
