Bulk WHOIS Lookup (Apify Actor)

Paste domains, get one row each: registrar, created, expires, nameservers, MX and availability. Up to 1,000 per run, $2.50 per 1,000, errors free, no API key. Runs as an Apify Actor, callable from MCP clients via the Apify MCP server (https://mcp.apify.com/?actors=pennydata/bulk-domain-whois-dns-lookup).

Hosted MCP Server

npx add-mcp 'https://mcp.apify.com/?actors=pennydata/bulk-domain-whois-dns-lookup'

Installs into Claude Code, Codex, Cursor and more

Documentation

Bulk WHOIS Lookup: Domain Expiry, Registrar, Nameservers (pennydata/bulk-domain-whois-dns-lookup) Actor

Bulk WHOIS lookup: paste domains, get one row each with availability, registrar, created and expiry dates, nameservers and MX. Up to 1,000 per run, $2.50 per 1,000, errors free, no API key. Click Start: 4 example domains cost about one cent.

Pricing

from $1.75 / 1,000 domain looked ups

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage. 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 section below.

Each client library has reference documentation the quickstart does not restate: JavaScript/TypeScript (npm install apify-client) and Python (pip install apify-client).

README

Bulk WHOIS Lookup: registrar, expiry, nameservers & MX for a domain list

Paste domains, get one row each. $2.50 per 1,000 domains, errors are free, no API key. Click Start: the 4 prefilled domains run as is (availability mode, about one cent). Set mode to full for the registrar, expiry, nameserver and MX columns below.

domainregistrarcreatedexpiresNS (first)MX (first)
apify.comAmazon Registrar, Inc.2009-06-022035-06-02ns-1225.awsdns-25.org1 aspmx.l.google.com
wikipedia.orgMarkMonitor Inc.2001-01-132027-01-13ns0.wikimedia.org10 mx-in2001.wikimedia.org
example.comRESERVED-Internet Assigned Numbers Authority1995-08-142027-08-13elliott.ns.cloudflare.com0 (null MX)

Each row also has available, ageDays, status, A, TXT and (email-dns mode) SPF/DMARC. Click Start: the form is prefilled with 4 domains in availability mode (openai.com, stripe.com, apify.com and one unregistered name) and costs about one cent. Set mode to full for the registrar, nameserver and MX columns shown above.

Details

Look up the registrar, expiry date, domain age, nameservers and availability of up to 1,000 domains per run for $2.50 per 1,000 domains ($0.0025 each; errors and unknown results free). RDAP plus DNS, no API key, no proxy.

Try it in Apify Console: the input form opens already filled with a demo input (below), so you can click Start with no setup. A new Apify account includes free monthly credit.

Demo input (identical to the prefill):

{"mode":"availability","domains":["openai.com","stripe.com","apify.com","thisnamesurelyisfree-48213.com"]}

Real output (local run 2026-10-25, availability mode, 5 domains):

domainstatusregistrarexpiresdaysUntilExpiry
example.comregisteredRESERVED-Internet Assigned Numbers Authority2027-08-13292
apify.comregisteredAmazon Registrar, Inc.2035-06-023142
github.comregisteredMarkMonitor Inc.2028-10-09715
openai.comregisteredMarkMonitor Inc.2029-01-19817
nonexistent-zz-98231.comavailablenullnullnull

Copy-paste input for exactly this run:

{"mode":"availability","domains":["example.com","apify.com","github.com","openai.com","nonexistent-zz-98231.com"]}

Bulk domain availability checker and WHOIS/DNS lookup: set mode to availability for a fast available / registered / unknown check, or leave full for the complete report. check up to 1,000 domains per run and get availability, registrar, creation and expiry dates, age in days, status flags, nameservers and A/MX/TXT records. It uses RDAP, the official successor to WHOIS, plus DNS, so there is no scraping, no proxy and no captcha. Errors and unknown results are free.

Free to try: a new Apify account includes free monthly platform credit, enough to validate the output on a few dozen domains before you pay anything. This Actor has no hidden minimums: failed items are never billed.

How to use Bulk WHOIS Domain Lookup (RDAP)

  1. Paste domains (or URLs) into domains; duplicates are removed.
  2. Pick mode: availability (domain availability checker) or full (WHOIS and DNS report).
  3. Click Start.
  4. Export the dataset as JSON, CSV or Excel.
  5. Re-run any error rows later if a registry rate-limited you.

Email DNS check mode (MX, SPF, DMARC)

Set mode to email-dns to check email deliverability setup for a list of domains. Each row has the MX records, the detected email provider (Google Workspace, Microsoft 365, Zoho Mail, Proofpoint, Mimecast and others), the SPF record and its policy (fail, softfail, neutral, pass-all), the DMARC record and policy (none, quarantine, reject), emailReady and an issues list such as no_dmarc_record or dmarc_policy_none. It uses DNS only (no RDAP, no SMTP probing). Billed as one domain looked up per domain with DNS records; domains with no DNS records are free.

{"mode":"email-dns","domains":["apify.com","github.com"]}

SSL certificate expiry mode

Set mode to ssl to check TLS certificates for a list of hosts, for example the domains of every client site you host. One TLS handshake on port 443 per domain (no scraping, no registry lookup). Each row has issuer, notBefore, notAfter, daysLeft (whole days, negative once expired), sans (subject alternative names) and valid (true when the chain is trusted, the host name matches and the dates are current; validationError says why not). Billed as one domain looked up per domain where a certificate was read; hosts with no HTTPS service are free and carry an error.

{"mode":"ssl","domains":["apify.com","github.com"]}

Pricing

Pay per event, no subscription and no compute fees on top. Price: $0.0025 per domain looked up ($2.50 per 1,000 domains) on the Free plan. Paid Apify plans get tiered discounts down to $0.00175 per domain (Bronze $0.00225, Silver $0.002, Gold and above $0.00175).

EventWhen it is chargedPrice (Free plan)
domain-lookupOne domain successfully looked up via RDAP or DNS evidence.$0.0025

There is no separate price for availability mode; it does not have its own charge event.

Failed, blocked or unknown items are not billed. Set a maximum cost per run in the run options to cap spend.

Example input

{
  "mode": "availability",
  "domains": [
    "example.com",
    "python.org",
    "some-name-nobody-registered-12345.com"
  ]
}

Domains in a Google Sheet? Set sheetUrl to the share link (sheet shared as "Anyone with the link can view") and optionally sheetColumn; the link is converted to CSV export automatically: { "sheetUrl": "https://docs.google.com/spreadsheets/d/<id>/edit#gid=0", "sheetColumn": "Website" }.

Example output

Sample output item (abridged, from the Actor README).

{
  "domain": "python.org",
  "available": false,
  "registrar": "Gandi SAS",
  "created": "1995-03-27T05:00:00.545Z",
  "expires": "2033-03-28T05:00:00Z",
  "expiresAt": "2033-03-28T05:00:00.000Z",
  "daysUntilExpiry": 2349,
  "ageDays": 11525,
  "domainAgeYears": 31.6,
  "alerts": [],
  "nameservers": [
    "ns-1134.awsdns-13.org"
  ],
  "mx": [
    "50 mail.python.org"
  ],
  "source": "rdap"
}

Availability mode output

[
  { "domain": "google.com", "status": "registered", "registrar": "MarkMonitor Inc.", "expires": "2028-09-14T04:00:00Z", "expiresAt": "2028-09-14T04:00:00.000Z", "daysUntilExpiry": 698, "source": "rdap", "checkedAt": "2026-10-19T12:00:00.000Z" },
  { "domain": "qzxjkwvbnmplrtyhgfdc.com", "status": "available", "registrar": null, "expires": null, "expiresAt": null, "daysUntilExpiry": null, "source": "rdap", "checkedAt": "2026-10-19T12:00:00.000Z" }
]

status is available (registry answered "not found"), registered (registry or DNS evidence), or unknown (RDAP failed or the TLD has no RDAP and no DNS records; never charged). Public RDAP is queried first, with DNS NS/A records as fallback.

Details and limits

Use it as a bulk domain expiry checker and expiry monitor: set alertDaysBeforeExpiry (default 30) and filter on a non-empty alerts array. Set includeSsl: true (full mode, no extra charge) to also open a TLS connection on port 443 and get sslIssuer and sslDaysUntilExpiry per registered domain (negative if the certificate has expired; sslError if there is no HTTPS service). Details: daysUntilExpiry and expiresAt come straight from the registry's RDAP expiry date (null where the registry publishes none). Give it a list of up to 1000 domains and get back, for each one: is it available, who is the registrar, when was it created and when does it expire, how old is it (days), registry status flags, nameservers, A / MX / TXT records. It uses RDAP, the official ICANN replacement for WHOIS, plus ordinary DNS lookups. No website is scraped, no proxy is needed, and no captcha is involved.

What it does

  • Input: domains, a list of domain names (the alias urls is also accepted and merged with domains, so swapping in from other lookup actors needs no code change) (maximum 1000 per run, duplicates removed). URLs are accepted and reduced to the registrable domain (https://www.amazon.com/x becomes amazon.com; a small built-in rule handles common co.uk-style suffixes).
  • The IANA RDAP bootstrap file (data.iana.org/rdap/dns.json) is fetched once per run to find the registry RDAP server for each TLD.
  • RDAP requests run with at most 5 in flight per registry server, with exponential backoff and Retry-After handling on HTTP 429 and 5xx.
  • DNS (A, MX, NS, TXT) is resolved with the platform resolver.
  • Output per domain: domain, available, registrar, created, expires, expiresAt (ISO 8601), daysUntilExpiry (whole days from checkedAt, negative if already expired, null when the registry gives no expiry date), ageDays, domainAgeYears (ageDays / 365.25, one decimal), alerts (e.g. ["expires_in_12_days"] or ["expired_3_days_ago"]; [] when the expiry is further away than alertDaysBeforeExpiry), sslIssuer and sslDaysUntilExpiry (only with includeSsl), status, nameservers, a, mx (as "priority host"), txt, source (rdap or dns-only), checkedAt, plus note or error when relevant.

Limits and honesty notes

  • available: true means the registry answered "not found" for that exact name. It is a good signal, not a purchase guarantee: reserved, premium, blocked or restricted names can still be unobtainable.
  • Some country-code TLDs have no RDAP service (for example .de, .io and .ac at the time of writing). For these, source is dns-only: if the domain has NS, A or MX records it is reported available: false with nameservers and DNS records, but registrar, dates and age are null. If it has no DNS records either, available is null (unknown) and it is not charged. A registered domain with no DNS records can therefore show as unknown.
  • Registry servers may rate limit. After retries a persistent failure is returned as an error row and not charged; re-run those domains later.
  • Some registries (notably country-code ones) omit dates or show a generic registrar name. Fields that the registry does not provide are null.
  • Privacy: only registry-level facts are output. RDAP registrant, admin and tech contact entities (names, emails, phones) are never read or stored, and many registries redact them anyway.
  • The registrable-domain reduction is a heuristic, not the full public suffix list; for unusual multi-level suffixes pass the exact registrable domain.

Use as a real-time API

This Actor also runs in Apify Standby mode, so you can call it per request over plain HTTP and get JSON back. Billing is the same as a normal run: one domain-lookup event per domain looked up; errors and unknown results are free. Up to 100 domains per request (use a normal run for up to 1,000).

## one domain
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://pennydata--bulk-domain-whois-dns-lookup.apify.actor/?domain=example.com"

## several domains
curl -X POST -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{"domains":["example.com","apify.com"]}' \
  "https://pennydata--bulk-domain-whois-dns-lookup.apify.actor/"

A single GET returns one JSON object (registrar, created, expires, nameservers, A, MX, TXT, availability). A POST returns {"results":[...]}. Normal batch runs from the Console, API or schedules work exactly as before.

Use from Claude or Cursor (MCP)

Apify hosts an MCP server, so an AI agent can run this Actor from a plain-English prompt. Add it to your MCP client (OAuth is the recommended sign-in, see the Apify MCP docs):

{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=pennydata/bulk-domain-whois-dns-lookup"
    }
  }
}

?tools= limits the server to this Actor. Using a token instead of OAuth? Add "headers": { "Authorization": "Bearer <APIFY_TOKEN>" } next to url. The configurator at https://mcp.apify.com/ writes client-specific snippets; for Cursor you can also run apify mcp install cursor.

Example prompt: "Use bulk-domain-whois-dns-lookup in availability mode on example.com, python.org and my-new-name-12345.com. Show a table of domain and status, and don't guess any domain that comes back unknown."

Runs started by an agent are billed like any other run, at the prices above.

More from PennyData

FAQ

How do I use it as a domain availability checker?
Set mode to availability. Each domain returns available, registered or unknown (plus registrar and expiry when registered); it has no separate price event, and unknown results are never charged.

Why is a ccTLD result dns-only?
Some country-code TLDs (e.g. .de, .io) have no RDAP service. The Actor then reports DNS evidence only and says so; unknown results are not charged.

Is it legal? Does it expose personal data?
Only registry-level facts are output. Registrant, admin and tech contact entities are never read or stored.

Need a custom field, a higher volume or a different source?
Open an issue on the Actor page and describe what you need; custom variants are possible.

Is it maintained?
Yes. Reports of breakage are answered through the Issues tab, and runs that fail are not billed.

Built with AI assistance (Claude); maintained by the publisher.

Excel export

Every run also saves OUTPUT.xlsx (one sheet, same columns as the dataset) to the run's key-value store. The run's OUTPUT record has an xlsxUrl link, and the Output tab shows an "Excel file" link. Open it directly in Excel or Google Sheets.

Switching from another WHOIS actor

Saved tasks and integrations written for these actors run here with only the Actor ID changed:

  • agenscrape/whois-domain-lookup: the single domain field and the domains list both work (domain also takes several domains separated by spaces or commas).
  • santamaria-automations/domain-whois-dns: domains works as is. includeWhois, includeDNS, dnsRecordTypes, useDNSServer and timeoutSeconds are accepted and ignored, because every row here already carries RDAP/WHOIS data and DNS records (A, AAAA, MX, NS, TXT and more) in one result, billed per domain.

Output field names are this Actor's own (see Example output), so check any downstream mapping once.

Migrating from agenscrape/whois-domain-lookup

Saved tasks and API calls for agenscrape/whois-domain-lookup ("Whois Domain Lookup") can keep their input JSON: change only the Actor ID. Checked against that Actor's public input schema on 2026-10-23:

agenscrape fieldHere
domain (single domain)Same name. Also accepts several domains separated by spaces or commas
domains (list)Same name. Up to 1,000 per run, duplicates removed, URLs reduced to the host name

Differences to check before swapping a pipeline:

  • Output field names are this Actor's own (see Example output), so check any downstream mapping once.
  • Registrant, admin and tech contact entities are never read or stored here. If you need those, this Actor is not a replacement.
  • Each row carries RDAP/WHOIS facts plus DNS records (A, AAAA, MX, NS, TXT and more) in one result, and mode: availability gives a fast available / registered / unknown check.
  • Price here is $0.0025 per domain looked up on the Free plan (see Pricing); failed or unknown items are not billed. Check both listings before relying on any figure.

Not affiliated with agenscrape.

Related actors

Use with AI agents (MCP)

Call this actor from an AI agent (Claude, Cursor, or any MCP client) through the Apify MCP server. Add this URL as an MCP server in your client: https://mcp.apify.com?tools=pennydata/bulk-domain-whois-dns-lookup It exposes this actor as a tool the agent can run with the same input as above. An Apify API token is required. This actor was built with AI assistance.

Pairs with: Website Contact Extractor

In full mode, rows for registered domains carry an optional nextStep text field (untick Add next-step hint to drop it). If your domain list is a list of company or lead websites, the WHOIS and DNS fields tell you who owns each domain and how old it is. To reach the business behind a domain, run the domains through Website Contact Extractor: it crawls each site's contact and about pages and returns the public emails, phone numbers and social links it finds, plus contact-form URLs. Pass the same domains as start URLs. It is a separate actor with its own pricing, so nothing runs or is charged unless you start it.

I built this and earn from its runs. Written with AI assistance (Claude).


Other languages

Español: Consulta WHOIS y DNS de dominios en lote

Consulta una lista de dominios y obtén una fila por dominio: registrador, fecha de creación y de vencimiento, servidores de nombres, MX, SPF y DMARC. Sirve para revisar cuándo caducan tus dominios o comprobar si uno está disponible. No necesita clave de API. Precio: 2,50 USD por 1.000 dominios consultados; los errores no se cobran.

Ejemplo de entrada:

{"domains":["apify.com","wikipedia.org","example.com"]}

Português: Consulta WHOIS e DNS de domínios em lote

Consulte uma lista de domínios e receba uma linha por domínio: registrador, datas de criação e de expiração, servidores de nomes, MX, SPF e DMARC. Útil para acompanhar o vencimento dos seus domínios ou verificar se um domínio está disponível. Sem chave de API. Preço: US$ 2,50 por 1.000 domínios consultados; erros não são cobrados.

Exemplo de entrada:

{"domains":["apify.com","wikipedia.org","example.com"]}

Deutsch: WHOIS- und DNS-Abfrage für Domainlisten

Fragt eine Liste von Domains ab und liefert pro Domain eine Zeile: Registrar, Erstellungs- und Ablaufdatum, Nameserver, MX, SPF und DMARC. Praktisch, um Ablaufdaten eigener Domains zu überwachen oder die Verfügbarkeit zu prüfen. Ohne API-Schlüssel. Preis: 2,50 USD pro 1.000 abgefragter Domains; Fehler kosten nichts.

Beispieleingabe:

{"domains":["apify.com","wikipedia.org","example.com"]}

I built this and earn from its runs. Written with AI assistance (Claude).

Actor input Schema

mode (type: string):

availability: fast domain availability checker (available / registered / unknown, plus registrar and expiry when registered). full: complete WHOIS (RDAP) and DNS report. email-dns: MX records, email provider (Google Workspace, Microsoft 365, Zoho...), SPF and DMARC policy and email deliverability issues per domain (DNS only, no RDAP). ssl: SSL certificate expiry check on port 443 (issuer, notBefore, notAfter, daysLeft, SANs, valid), TLS only. Priced per domain looked up, see the Pricing section of the README.

domains (type: array):

Domain names such as example.com, for bulk WHOIS, DNS and domain expiry checks. URLs are accepted and reduced to the host name. Maximum 1000 per run; duplicates are removed.

urls (type: array):

Alias of Domains, for callers that pass URLs. Entries are reduced to the host name and merged with Domains; duplicates are removed.

includeSsl (type: boolean):

Full mode only. For each registered domain, open a TLS connection on port 443 and add sslIssuer and sslDaysUntilExpiry (negative if the certificate has expired). No extra charge.

alertDaysBeforeExpiry (type: integer):

Domains whose registration expires within this many days (or already expired) get an entry in the alerts array, e.g. expires_in_12_days. Use it to monitor a portfolio for expiring domains.

maxConcurrency (type: integer):

Total number of domains looked up in parallel. At most 5 requests are ever in flight to any single RDAP server regardless of this value.

sheetUrl (type: string):

Link to a Google Sheet shared as 'Anyone with the link can view' (docs.google.com/spreadsheets/d//edit#gid=0) or a CSV URL. Domains from the domain column are added to Domains.

sheetColumn (type: string):

Header of the column with domains. Default: first header containing domain, website, url or site, else the first column.

domain (type: string):

One domain, or several separated by spaces or commas. Alias so inputs written for other WHOIS actors work unchanged; same as adding them to Domains.

nextStepHint (type: boolean):

In full mode, adds a nextStep text field on registered-domain rows pointing to a separate actor that finds contact emails and phones. Untick to leave it out.

Actor input object example

{
  "mode": "availability",
  "domains": [
    "openai.com",
    "stripe.com",
    "apify.com",
    "thisnamesurelyisfree-48213.com"
  ],
  "includeSsl": false,
  "alertDaysBeforeExpiry": 30,
  "maxConcurrency": 20,
  "nextStepHint": true
}

Actor output Schema

results (type: string):

No description

excel (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

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 = {
    "mode": "availability",
    "domains": [
        "openai.com",
        "stripe.com",
        "apify.com",
        "thisnamesurelyisfree-48213.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pennydata/bulk-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

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 = {
    "mode": "availability",
    "domains": [
        "openai.com",
        "stripe.com",
        "apify.com",
        "thisnamesurelyisfree-48213.com",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("pennydata/bulk-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

echo '{
  "mode": "availability",
  "domains": [
    "openai.com",
    "stripe.com",
    "apify.com",
    "thisnamesurelyisfree-48213.com"
  ]
}' |
apify call pennydata/bulk-domain-whois-dns-lookup --silent --output-dataset

MCP server setup

{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pennydata/bulk-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/pYjzn2yloiQMdSPAJ/builds/BthY259CGbmKgucDV/openapi.json