# Domain WHOIS and DNS Lookup: Registrar, Expiry, Mail Setup (`deriverge/domain-whois-dns-lookup`) Actor

Look up domains in bulk: registrar, creation and expiry dates, status, nameservers and DNSSEC from the registry (RDAP or WHOIS), plus A, MX, NS, TXT, SPF and DMARC records and the mail provider. Any TLD, changes only, JSON, CSV, API.

- **URL**: https://apify.com/deriverge/domain-whois-dns-lookup.md
- **Developed by:** [deriverge s.r.o.](https://apify.com/deriverge) (community)
- **Categories:** Developer tools, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 domain registry rows

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

## Domain WHOIS and DNS Lookup

### What does Domain WHOIS and DNS Lookup do?

**Domain WHOIS and DNS Lookup** takes a list of domains, in any form, and returns one row per domain with everything a registry and the DNS publicly say about it:

- **Registry data** through RDAP, the machine-readable successor of WHOIS that registries run for free, with WHOIS over port 43 as the fallback for the few registries that have no RDAP yet: registrar and its IANA ID, creation, update, expiry and transfer dates, domain age in days, days until expiry, registry statuses (transfer locks, holds), nameservers and DNSSEC.
- **DNS records** through DNS over HTTPS: A, AAAA, CNAME, MX, NS, TXT, SOA and CAA.
- **Email setup** derived from the records: the SPF record, the DMARC record and its policy, whether the domain has MX at all, and the mail provider recognised from the MX hosts (Google Workspace, Microsoft 365, Zoho, Proton and others).
- **Subdomains** on request, from certificate transparency logs.

No browser, no proxies, no API key, and no registrant contacts: the actor reads what registries publish about the domain, not about the people behind it.

You pay only for domains returned, from $0.50 per 1,000, with no start fee. The $5 of monthly credit in Apify's Free plan covers about 5,000 domains, so you can try it for free.

### Any input, one registrable domain

Paste bare names, URLs, email addresses or hostnames. Each is reduced to its registrable domain with the public suffix list, so `https://blog.example.co.uk/post`, `press@example.co.uk` and `www.example.co.uk` all become `example.co.uk`, checked once. Internationalised names are converted to their ASCII form.

### Fields

| Registry | What you get |
|---|---|
| `registered` | `true`, `false`, or `null` when no registry service answered. |
| `registrar`, `registrarIanaId`, `registrarUrl` | The sponsoring registrar. |
| `createdAt`, `updatedAt`, `expiresAt`, `transferredAt` | Real timestamps. |
| `domainAgeDays`, `daysUntilExpiry` | Computed at run time. |
| `statuses`, `nameservers`, `dnssec` | As the registry publishes them. |
| `lookupSource`, `rdapServer`, `whoisServer` | Where the data came from. |

| DNS | What you get |
|---|---|
| `a`, `aaaa`, `cname`, `ns`, `txt`, `caa` | Arrays of records, `cname` a single value. |
| `mx` | `[{ priority, exchange }]`, sorted by priority. |
| `soa` | Primary nameserver, admin, serial and timers. |
| `websiteResolves`, `hasMx` | Whether the name has an address and a mail exchanger. |
| `spf`, `dmarc`, `dmarcPolicy`, `mailProvider` | The email setup at a glance. |

Nothing is guessed. A field the registry or the DNS did not publish is `null`, and `notes` says why when a lookup could not be made.

### A domain monitor, not an export

Turn on `changesOnly`, give the run a watch name or save it as a task, and schedule it daily. Each run compares every domain with the previous snapshot and returns only the domains whose registrar, expiry date, statuses, nameservers, A, MX, NS, SPF or DMARC records changed, plus new domains. You pay for what moved and nothing else. The `CHANGES` record lists every difference with the old and the new value: a nameserver change, a transfer lock removed, an expiry date pushed out, a DMARC policy relaxed.

`expiringWithinDays` keeps only domains that expire within the number of days you give, which turns a portfolio list into a renewal reminder.

### How much does it cost to look up WHOIS and DNS data?

You pay per result. There is no start fee and no charge for compute time or proxies.

| | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| 1,000 domains, registry data | $1.00 | $0.80 | $0.65 | $0.50 |
| 1,000 domains, DNS records | $0.80 | $0.64 | $0.52 | $0.40 |
| Subdomain lookup, per 1,000 domains (optional) | $5.00 | $4.00 | $3.25 | $2.50 |

For example, registry data for 1,000 domains costs $1.00 on the Free plan and $0.50 on the Business plan. The $5 of monthly credit in Apify's Free plan covers about 5,000 domains.

### How to look up WHOIS and DNS data

1. Click **Try for free** (or **Start** if you are signed in) to open the actor in Apify Console.
2. In **Domains**, add domains, URLs or email addresses; each is reduced to its registrable domain.
3. Click **Start**. Rows appear in the **Output** tab within seconds.
4. Download the results as JSON, CSV, Excel or HTML, or read them through the API.
5. To repeat it, click **Save as a task** and add a schedule. A scheduled task keeps its own snapshot, so change and new-only modes work without any setup.

### Input

```json
{
  "domains": ["apify.com", "https://blog.github.com", "bbc.co.uk", "press@seznam.cz"],
  "includeWhois": true,
  "includeDns": true,
  "includeSubdomains": false,
  "dnsTypes": ["A", "AAAA", "CNAME", "MX", "NS", "TXT", "SOA", "CAA"],
  "emailChecks": true,
  "changesOnly": false
}
```

### Output

```json
{
  "type": "domain",
  "key": "domain:apify.com",
  "input": "apify.com",
  "hostname": "apify.com",
  "domain": "apify.com",
  "tld": "com",
  "publicSuffix": "com",
  "registered": true,
  "lookupSource": "rdap",
  "rdapServer": "https://rdap.verisign.com/com/v1/",
  "whoisServer": null,
  "registrar": "Amazon Registrar, Inc.",
  "registrarIanaId": "468",
  "registrarUrl": null,
  "createdAt": "2009-06-02T17:14:10.000Z",
  "updatedAt": "2026-05-16T16:53:04.000Z",
  "expiresAt": "2035-06-02T17:14:10.000Z",
  "transferredAt": null,
  "domainAgeDays": 6321,
  "daysUntilExpiry": 3174,
  "statuses": ["client transfer prohibited"],
  "nameservers": ["ns-1225.awsdns-25.org", "ns-1928.awsdns-49.co.uk", "ns-449.awsdns-56.com", "ns-839.awsdns-40.net"],
  "dnssec": true,
  "a": ["13.227.192.67", "13.227.192.104"],
  "aaaa": ["2600:9000:223c:4200:9:a03e:6540:93a1"],
  "cname": null,
  "mx": [{ "priority": 1, "exchange": "aspmx.l.google.com" }, { "priority": 5, "exchange": "alt1.aspmx.l.google.com" }],
  "ns": ["ns-449.awsdns-56.com", "ns-839.awsdns-40.net"],
  "txt": ["v=spf1 a mx include:_spf.google.com include:mailgun.org ~all", "google-site-verification=..."],
  "soa": { "primaryNs": "ns-839.awsdns-40.net", "admin": "awsdns-hostmaster.amazon.com", "serial": 1, "refresh": 7200, "retry": 900, "expire": 1209600, "minimum": 86400 },
  "caa": ["0 issue \"amazonaws.com\"", "0 issue \"digicert.com\"", "0 issue \"letsencrypt.org\""],
  "websiteResolves": true,
  "hasMx": true,
  "mailProvider": "Google Workspace",
  "spf": "v=spf1 a mx include:_spf.google.com include:mailgun.org ~all",
  "dmarc": "v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc-reports@apify.com",
  "dmarcPolicy": "reject",
  "subdomains": null,
  "subdomainCount": null,
  "checkedAt": "2026-09-23T11:40:12.000Z",
  "notes": []
}
```

### What it does not collect

No registrant, administrative or technical contacts, even where a registry still publishes them. The row describes the domain: who sponsors it, when it was created and expires, how it is delegated and how its mail is set up.

### Integrations and API

Connect the actor to **Make**, **Zapier**, **n8n**, **Google Sheets**, **Slack** or any webhook, or call it from your own code. With the Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("deriverge/domain-whois-dns-lookup").call(run_input={"domains": ["apify.com", "https://blog.github.com", "bbc.co.uk"]})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["domain"], row.get("registrar"), row.get("expiresAt"))
```

The **API** tab on this page has the same call for Node.js and cURL, and AI agents can run the actor through the Apify MCP server.

### Is it legal to look up WHOIS and DNS data?

Registries publish registration data through RDAP and WHOIS precisely so it can be looked up, and DNS is public by design. The actor does not collect registrant names or contact details.

### FAQ

#### Which TLDs work?

Every generic TLD and most country codes through RDAP, using the IANA bootstrap registry plus a list of registries that run RDAP without being listed there yet (Germany, Switzerland, Norway, Argentina, Slovenia, the United States, .io). Registries without RDAP are read over WHOIS port 43 and return whatever they publish, which for some (for example .de and .jp) is statuses and nameservers without dates. The `lookupSource` field tells you which path answered.

#### Why is `registrar` sometimes a code?

Some registries publish the registrar's handle rather than its name, for example `REG-GANDI` at CZ.NIC. The value is passed through as published.

#### How fast is it?

Six domains at a time, each with one registry request and up to ten DNS queries in parallel. A thousand domains take a few minutes.

#### Is there a rate limit?

Registries serve RDAP for exactly this purpose and public resolvers are built for volume. The actor pauses and retries on any 429.

### Related scrapers

- [Tech Stack Detector](https://apify.com/deriverge/tech-stack-detector): what any website is built with, 7,600 technologies
- [WHOIS Domain Lookup](https://apify.com/deriverge/whois-domain-lookup): registrar, age, expiry, status

### Support and feedback

Missing a field or found something that does not work? Open an issue in the **Issues** tab and it will be answered, usually within a day. If the actor saves you time, a short review helps other people find it.

# Actor input Schema

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

Domain names in any form: bare names, URLs, email addresses, hostnames with a subdomain. Each is reduced to its registrable domain with the public suffix list, so blog.example.co.uk becomes example.co.uk.

## `includeWhois` (type: `boolean`):

Registrar, dates, statuses, nameservers and DNSSEC from the registry. Charged per domain.

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

A, AAAA, CNAME, MX, NS, TXT, SOA and CAA records through DNS over HTTPS. Charged per domain.

## `includeSubdomains` (type: `boolean`):

Every hostname under the domain that received a TLS certificate, from certificate transparency logs, up to 500 per domain. One extra lookup per domain, charged separately.

## `dnsTypes` (type: `array`):

Which record types to read. Leave empty for all eight.

## `emailChecks` (type: `boolean`):

Read the SPF record, the DMARC record and policy, and name the mail provider from the MX hosts. Two extra DNS queries per domain, no extra charge.

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

Keep only domains whose registry expiry date is within this many days. Other domains are dropped and never charged. Leave empty to keep everything.

## `changesOnly` (type: `boolean`):

Keeps a snapshot per watch name (or per saved task) and returns only domains whose registrar, expiry, status, nameservers, A, MX, NS, SPF or DMARC changed, plus new domains. Schedule it daily and you have a domain monitor that bills only for what moved; the CHANGES record lists every difference.

## `watchName` (type: `string`):

Name of the snapshot used by the changes-only mode, for example "our-domains". Runs from a saved task get a snapshot automatically even without a name.

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

Hard cap on returned domains in the run.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "https://blog.github.com",
    "bbc.co.uk"
  ],
  "includeWhois": true,
  "includeDns": true,
  "includeSubdomains": false,
  "dnsTypes": [
    "A",
    "AAAA",
    "CNAME",
    "MX",
    "NS",
    "TXT",
    "SOA",
    "CAA"
  ],
  "emailChecks": true,
  "changesOnly": false,
  "maxItems": 10000
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per domain with registry data, DNS records and email setup signals.

## `changes` (type: `string`):

Domains whose registrar, expiry, statuses, nameservers or key DNS records changed, with the old and new values, compared with the previous snapshot of the same watch name or task.

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

Counts, what was charged, lookups that failed and notes.

# 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",
        "https://blog.github.com",
        "bbc.co.uk"
    ],
    "dnsTypes": [
        "A",
        "AAAA",
        "CNAME",
        "MX",
        "NS",
        "TXT",
        "SOA",
        "CAA"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("deriverge/domain-whois-dns-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",
        "https://blog.github.com",
        "bbc.co.uk",
    ],
    "dnsTypes": [
        "A",
        "AAAA",
        "CNAME",
        "MX",
        "NS",
        "TXT",
        "SOA",
        "CAA",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("deriverge/domain-whois-dns-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",
    "https://blog.github.com",
    "bbc.co.uk"
  ],
  "dnsTypes": [
    "A",
    "AAAA",
    "CNAME",
    "MX",
    "NS",
    "TXT",
    "SOA",
    "CAA"
  ]
}' |
apify call deriverge/domain-whois-dns-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deriverge/domain-whois-dns-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/JmRn7RM9gaQfdNQc5/builds/UntEHxskg6ykcbEBw/openapi.json
