# Bulk WHOIS Lookup & DNS Lookup - Domain Age & Expiry (`tidytools/bulk-domain-whois-dns-lookup`) Actor

Look up many domains at once: registrar, creation and expiry dates, status and name servers via RDAP (WHOIS fallback), availability, A/MX/NS/TXT/CAA records, SPF/DMARC grade. $2/1,000.

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

## Pricing

from $1.60 / 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.
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

### What does Bulk WHOIS & DNS Lookup do?

Bulk WHOIS and DNS lookup: give it a list of domains, URLs or e-mail addresses and get the registrar, creation and expiry dates (domain age), availability and DNS records for each domain. In detail, each domain gets:

- 📅 **Registration data (WHOIS)**: registrar and IANA ID, **creation, update and expiry dates**, **days to expiry**, domain age, status codes (e.g. `clientTransferProhibited`), name servers, DNSSEC, and the **registrant organization and country** when the registry or registrar publishes them
- 🌐 **DNS records**: A, AAAA, MX, NS, TXT and CAA, the CNAME of `www`, and the `_dmarc` record
- ✉️ **E-mail security grade (A-F)**: SPF policy (`-all`, `~all`...), DMARC policy (`reject`, `quarantine`, `none`), MX present, plus a list of issues (no SPF, two SPF records, `p=none`, more than 10 SPF lookups...)
- 🔎 **Availability guess** (optional): `registered`, `likely_available` or `unknown`, always worded as a guess

Registration data comes from **RDAP**, the official JSON successor of WHOIS: the Actor finds each registry's RDAP server through the IANA bootstrap file (with https://rdap.org as a fallback). Country-code TLDs without RDAP (.jp, .io, .co, .it, .eu, .ru, .cn...) are asked over **classic WHOIS (port 43)** instead. DNS is read over DNS-over-HTTPS (Cloudflare, Google as a fallback).

Inputs are reduced to the registrable domain (`https://www.bbc.co.uk/news` → `bbc.co.uk`, `jane@shop.example.de` → `example.de`, `_dmarc.github.com` → `github.com`), and the same domain is looked up and charged once.

### Use cases

- **Domain portfolio monitoring**: find domains that expire soon (`daysToExpiry`) or lost their transfer lock
- **Lead research and enrichment**: domain age, registrant company and country, mail provider (from MX)
- **E-mail deliverability and security audits**: SPF / DMARC / MX for hundreds of client domains at once
- **Brand protection**: check look-alike domains and who registered them
- **Domain hunting**: a first pass over name ideas with the availability guess, before checking at a registrar
- **AI agents**: "When does this domain expire and who hosts its mail?"

### How much does it cost?

| Event | Price |
|---|---|
| Domain looked up | **$2.00 / 1,000 domains** |

**Example:** 10,000 domains cost 10,000 × $0.002 = **$20.00** on the Free plan.

**No start fee.** Higher Apify plans get volume discounts (see the *Pricing* tab). A domain is charged when registration data or DNS records were returned. Invalid inputs and lookups where both RDAP/WHOIS and DNS failed, or where registration data could not be read and the name does not resolve in DNS either (e.g. a TLD that does not exist) are not charged; they carry an `errorType`. A domain the registry says does not exist is a valid answer ("not registered") and is charged.

#### Control your cost

- **Before anything is looked up**, the run logs its plan: the number of unique domains × the price = the most the run can cost, compared with your maximum charge per run.
- **Charged**: a domain for which registration data or DNS records were returned ($0.002). Every row has `charged: true` or `false`.
- **Free**: lookups where both RDAP/WHOIS and DNS failed, or where registration data could not be read and the name does not resolve in DNS either (e.g. a TLD that does not exist), invalid lines (one row per line, e.g. `not a domain`, IP addresses), and duplicates (`github.com`, `www.github.com` and `jane@github.com` are one domain).
- **No plan limits**: there is no cap on how many domains a free Apify user can look up; only your maximum charge per run (run options or `maxTotalChargeUsd`) stops a run. When the next domain could exceed it, no new domain is started; the run ends with status `LIMIT_REACHED`, and the `SUMMARY` record lists the domains not processed (`notProcessed`: the count and up to 100 inputs).
- **If Apify restarts the run** (server migration or Resurrect), items already finished are skipped and not charged again (`SUMMARY.resumedSkipped`).

#### Price comparison (checked September 2026)

| Actor | Price | Notes |
|---|---|---|
| **Bulk Domain WHOIS (RDAP) & DNS Lookup (this Actor)** | **$0.002 per domain**, no start fee | WHOIS + DNS + SPF/DMARC grade in one row |
| ntriqpro/whois-domain-lookup | $0.02 per domain | |
| santamaria-automations/domain-whois-dns | pay per event; we recorded only its $0.001 start event | |
| agenscrape/whois-domain-lookup | pay per event, price not recorded in our check | |
| onescales/domain-availability-checker | free | availability only |

### How to use it

1. Paste your **domains** (or URLs, or e-mail addresses), one per line, or pick another run's **Dataset ID** and the **Dataset field** with the domain, URL or e-mail (e.g. the output of a contact or lead scraper). Several entries on one line may be separated by commas; a line that is not a domain (e.g. a company name) stays one `invalid_input` row and is not charged.
2. Keep **Registration data** and **DNS records** on, or turn one off if you only need the other.
3. Optional: turn on **Guess availability**.
4. Click **Start** and export the results as JSON, CSV or Excel. The *DNS & e-mail security* view shows the DNS columns.

#### Input example

This is the input the form is prefilled with; a run with it takes about a minute and costs only cents:

```json
{
    "domains": [
        "github.com",
        "https://www.bbc.co.uk/news",
        "spiegel.de",
        "sony.jp"
    ]
}
```

#### Output example (real result, September 2026)

`github.com` and `www.github.com` were both in the input, so they became one row (`inputs`). TXT records are shortened here.

```json
{
    "input": "github.com",
    "inputs": ["github.com", "www.github.com"],
    "domain": "github.com",
    "tld": "com",
    "success": true,
    "registered": true,
    "registrar": "MarkMonitor Inc.",
    "registrarIanaId": "292",
    "createdAt": "2007-10-09T18:20:50Z",
    "updatedAt": "2026-09-07T09:22:52Z",
    "expiresAt": "2028-10-09T18:20:50Z",
    "daysToExpiry": 741,
    "domainAgeDays": 6930,
    "status": ["clientDeleteProhibited", "clientTransferProhibited", "clientUpdateProhibited"],
    "nameservers": ["dns1.p08.nsone.net", "dns2.p08.nsone.net", "dns3.p08.nsone.net", "dns4.p08.nsone.net", "ns-1283.awsdns-32.org", "ns-1707.awsdns-21.co.uk", "ns-421.awsdns-52.com", "ns-520.awsdns-01.net"],
    "dnssec": false,
    "registrantOrg": "GitHub, Inc.",
    "registrantCountry": "US",
    "registrationSource": "rdap",
    "registrationServer": "rdap.verisign.com",
    "availability": "registered",
    "dnsResolves": true,
    "dns": {
        "a": ["140.82.112.4"],
        "aaaa": [],
        "mx": [{ "priority": 0, "host": "github-com.mail.protection.outlook.com" }],
        "ns": ["dns1.p08.nsone.net", "..."],
        "txt": ["MS=ms44452932", "google-site-verification=…", "v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com … ~all"],
        "caa": [{ "flags": 0, "tag": "issue", "value": "digicert.com" }, { "flags": 0, "tag": "issue", "value": "letsencrypt.org" }, "..."],
        "wwwCname": "github.com",
        "dmarc": "v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:dmarc@github.com; ruf=mailto:dmarc@github.com; fo=1"
    },
    "emailSecurity": {
        "grade": "B",
        "hasMx": true,
        "spfPolicy": "softfail",
        "dmarcPolicy": "quarantine",
        "spf": { "policy": "softfail", "includes": ["spf.protection.outlook.com", "_netblocks.google.com", "..."], "dnsLookups": 8, "multipleRecords": false },
        "dmarc": { "policy": "quarantine", "subdomainPolicy": "reject", "pct": 100, "rua": ["mailto:dmarc@github.com"] },
        "issues": [],
        "note": "DKIM is not checked (it needs the sender's selector)."
    },
    "emailGrade": "B",
    "checkedAt": "2026-09-29T14:16:25.456Z"
}
```

A name nobody has registered, with **Guess availability** on:

```json
{ "domain": "this-domain-surely-not-registered-xyz123.com", "success": true, "registered": false, "availability": "likely_available",
  "availabilityNote": "Guess: the registry has no record of this domain. Premium, reserved or blocked names can still be unavailable; confirm with a registrar.",
  "registrationSource": "rdap", "registrationServer": "rdap.verisign.com", "dnsResolves": false }
```

**E-mail grades:** A = SPF (`-all`/`~all`) and DMARC `p=reject`; B = DMARC `p=quarantine` (or reject for part of the mail, or in testing mode `t=y` from DMARCbis, RFC 9989; `testing` and `nonExistentSubdomainPolicy` (`np=`) are in `dmarc`); C = DMARC `p=none`; D = only SPF or only DMARC, or a weak SPF (`?all`, `+all`); F = neither. Domains with no MX that publish `v=spf1 -all` and DMARC reject get A (they are locked against spoofing). SPF `redirect=` and `include:` are followed like a mail server does (RFC 7208, at most 10 DNS lookups): a record such as `v=spf1 redirect=_spf.example.com` takes the policy of the target record (`spfRedirect`, `spfDnsLookups`). Every grade below A comes with at least one entry in `issues`.

A `SUMMARY` record in the key-value store has the run `status` (`SUCCESS`, `PARTIAL_RESULTS`, `FAILED`, `NO_RESULTS`, `LIMIT_REACHED`), failures by `errorType`, where registration data came from (`rdap` / `whois`), the count of each e-mail grade and the number of invalid lines. Each row has `input` (and `inputs` when several lines were the same domain) and `inputIndex`; an input such as `_dmarc.github.com` also gets an `inputNote` saying which domain was looked up (DMARC is in `dns.dmarc`).

### Coverage by TLD (our test of 20 domains, September 2026)

RDAP and WHOIS data differ by registry. What we got:

| TLD | Source | What the registry returned |
|---|---|---|
| .com, .net, .org, .dev, .app, .fr | RDAP | Full: registrar, dates, status, name servers; registrant org/country when public |
| .co.uk (Nominet) | RDAP | Registrar, dates, status, name servers |
| .kr | RDAP | Dates, status, name servers (no registrar name) |
| .com.au | RDAP | Registrar, status, name servers; **no creation or expiry dates** (not published) |
| .de (DENIC) | RDAP | Status and name servers only; DENIC publishes **no dates, registrar or holder** |
| .ch | RDAP | Registrar, status, name servers; no dates |
| .io, .co | WHOIS | Full, including registrant org and country when public |
| .jp | WHOIS | Dates, registrant, name servers; no registrar. For `.co.jp`, `.ac.jp` and other organisation-type names the expiry date comes from `[State]`; JPRS leaves the registration date blank for some names registered before its database (e.g. toyota.co.jp), so `createdAt` is then `null` |
| .it | WHOIS | Registrar, dates, registrant org, name servers |
| .ru | WHOIS | Registrar, creation and paid-till dates, name servers |
| .com.tw | WHOIS | Registrar, dates, registrant, name servers (the TWNIC RDAP server answered HTTP 426 in our test, so WHOIS is used) |
| .eu | WHOIS | Registrar and name servers only; EURid publishes no dates |
| .cn | WHOIS | Worked from our computer, but CNNIC's WHOIS server timed out from Apify's network in our test |
| .es | — | No RDAP, and the .es WHOIS server only answers pre-registered IP addresses: **no registration data** (DNS still works) |

When registration data cannot be read, the row still has the DNS records plus `registrationError`; `registered` is then `null`.

### Use with AI agents (MCP)

Connect Apify's MCP server (https://mcp.apify.com?tools=tidytools/bulk-domain-whois-dns-lookup) to Claude, Cursor or any MCP client, then ask e.g. "Which of these 50 domains expire in the next 90 days, and which have no DMARC record?"

```json
{ "domains": ["github.com", "example.co.uk"] }
```

Failed lookups and invalid lines are not charged and carry an `errorType`.

### Use it from code

```bash
curl -X POST "https://api.apify.com/v2/acts/tidytools~bulk-domain-whois-dns-lookup/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domains":["github.com","bbc.co.uk"],"checkAvailability":true}'
```

**Schedules and integrations:** run it daily or weekly with Apify Schedules, get a webhook when a run finishes, or send the results to Zapier, Make, n8n, Google Sheets, Slack and other apps with Apify integrations. Results can be exported as JSON, CSV, Excel or XML.

### Advanced settings

- **WHOIS fallback for TLDs without RDAP** (on by default): ask the registry's port-43 WHOIS server when a TLD has no RDAP server or its RDAP server fails.
- **Ask the registrar's RDAP server for registrant details** (on by default): .com and .net registries keep only basic data; the registrant organization and country come from the registrar's RDAP server (one extra request).
- **Proxy**: optional Apify Proxy for RDAP and DNS-over-HTTPS requests (not for port-43 WHOIS), billed to your Apify account.
- **Parallel domains**: requests to one registry are limited to 4 at a time to avoid rate limits.

### Limitations

- **Registrable domain rule**: the last two labels, or the last three when the last two are a known second-level suffix (`co.uk`, `com.au`, `co.jp`, `com.tw`...) or a two-letter country TLD with a generic second level (`co`, `com`, `net`, `org`, `gov`, `edu`, `ac`, `or`, `ne`, `go`). This is a documented simple rule, not the full Public Suffix List; a two-label name such as `go.to` or `co.cc` is looked up as it is, only the listed suffixes (`co.uk`, `com.au`...) alone are rejected. Hosting suffixes such as `github.io` are not treated as suffixes: `user.github.io` → `github.io`, the name that is registered.
- **Privacy**: most registrant names and e-mails are redacted under GDPR. Values that say "REDACTED", "privacy", "on behalf of" and similar are returned as `null`, but some privacy services use an ordinary-looking company name.
- **Availability is a guess.** `likely_available` means the registry has no record. Premium, reserved and blocked names can still be unavailable.
- WHOIS text formats differ per registry; the parser reads the common field names. Dates are returned in UTC; registries that give a local time without a zone are read as UTC.
- DKIM is not checked (it needs the sender's selector). DNS answers come from public resolvers, so very recent changes may not be visible yet.

### Responsible use

- Only the registrant **organisation** and country are returned, and only when the registry publishes them. The name, email and phone of an individual registrant, and admin/tech/billing contacts, are never returned: a bare "Registrant:" line in a WHOIS answer (e.g. .cn, .tw) is kept only when it reads like an organisation (Inc, Ltd, GmbH, 公司 …), because it could be a private person.
- Registration data is public registry data (WHOIS/RDAP); follow the registries' terms and the data-protection laws that apply to you.

### FAQ

**Does the Actor collect usage data?** The Actor sends one anonymous usage counter per run (input type, item count range, outcome) to improve the tool; no URLs, content or account data.

**Is this a WHOIS lookup or an RDAP lookup?** Both. Registration data comes from RDAP, the official JSON successor of WHOIS, found through the IANA bootstrap file (with rdap.org as a fallback). Country-code TLDs without RDAP (.jp, .io, .co, .it, .eu, .ru, .cn...) are asked over classic WHOIS (port 43) when **WHOIS fallback for TLDs without RDAP** is on, which it is by default.

**Does it show domain age, expiry dates and DNS records?** Yes. Each domain gets creation, update and expiry dates, days to expiry and domain age, plus DNS records (A, AAAA, MX, NS, TXT, CAA, the CNAME of `www` and the `_dmarc` record) read over DNS-over-HTTPS. Turn off **Registration data (WHOIS / RDAP)** or **DNS records** if you only need the other; some registries (e.g. .de, .eu, .ch, .com.au) publish no dates.

**Can it check domain availability?** Turn on **Guess availability** to get `registered`, `likely_available` or `unknown` for each domain. It is always a guess: `likely_available` means the registry itself answered that it has no record, but premium, reserved or blocked names can still be unavailable, so confirm with a registrar. When the registry could not be reached and only rdap.org answered "not found", or a registry's WHOIS server timed out, the domain is `unknown`, never `likely_available`.

**What is charged and what is free?** You pay $2.00 per 1,000 domains with no start fee, charged when registration data or DNS records were returned (a registry answer that the domain does not exist also counts). Lookups where both RDAP/WHOIS and DNS failed, or where registration data could not be read and the name does not resolve in DNS either (e.g. a TLD that does not exist), invalid lines and duplicates (`github.com`, `www.github.com` and `jane@github.com` are one domain) are free.

**What if my input is empty, or has only invalid lines?** The run ends normally (it does not fail): each invalid line gets a free `invalid_input` row, an empty input or both **Registration data** and **DNS records** switched off gets one such row, `SUMMARY.status` is `NO_RESULTS` and nothing is charged. A dataset ID that cannot be read gets the same kind of free row, and the other domains of the run are still looked up.

**Why is a domain I gave marked `not_found` and free?** Registration data was asked for and could not be read (e.g. the TLD does not exist or has no WHOIS), and the name does not resolve in DNS either, so there was nothing to return.

**Can I give non-Latin (IDN) domains, quoted or pasted text?** Yes. `bücher.de` or `台灣.tw` are converted to punycode (`xn--bcher-kva.de`) and the row's `domain` holds that ASCII form; `input` keeps what you typed. Quotes, angle brackets and a trailing `;` or `)` around a name are ignored.

**What do the `errorType` values mean?** `invalid_input` (not a domain, IP address, a registry suffix such as `co.uk`, empty input), `not_found` (no registration data and the name does not resolve), `no_registration_data` (the TLD has no RDAP or WHOIS service and DNS was off), `network` (registry unreachable), `blocked` (the registry's WHOIS server refuses automated queries), `timeout`, `save_failed`. All of them are free.

### Related Actors

More tools by TidyTools that work well with this one:

- [Website Tech Stack Detector](https://apify.com/tidytools/website-tech-stack-detector): find the CMS, shop platform and tools a site uses
- [Company Website Enrichment](https://apify.com/tidytools/company-website-enrichment): add company details to a list of domains
- [Email List Cleaner](https://apify.com/tidytools/bulk-email-validator): clean an email list with MX, disposable and typo checks
- [Bulk URL Status Checker](https://apify.com/tidytools/bulk-url-status-checker): check HTTP status and redirects for a list of URLs

### Support

Open an issue in the **Issues** tab with the domain. Issues are checked regularly.

Found this useful? A short review on the Store helps other users find it.

# Actor input Schema

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

Main input (fill this, or `datasetId`). Domains, URLs or e-mail addresses, one per line (several per line separated by commas also work). Each is reduced to its registrable domain: https://www.bbc.co.uk/news → bbc.co.uk, \_dmarc.github.com → github.com. The same domain is looked up and charged once. A line that is not a domain (e.g. a company name) is reported as one invalid row and not charged.

## `datasetId` (type: `string`):

Look up the domains in another Actor run's dataset, e.g. the website or email field of a lead list or contact scraper.

## `datasetField` (type: `string`):

Field with the domain, URL or e-mail address, e.g. "website", "domain" or "email" (dot paths work). Empty = the first URL field found (url, website, link, domain...).

## `includeRegistration` (type: `boolean`):

Registrar, creation, update and expiry dates, days to expiry, status codes, name servers, DNSSEC and the registrant organization and country when the registry publishes them.

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

A, AAAA, MX, NS, TXT and CAA records, the CNAME of www and the \_dmarc record, read over DNS-over-HTTPS.

## `emailSecurity` (type: `boolean`):

Parse SPF and DMARC and grade the domain A-F, with a list of issues. Needs DNS records. DKIM is not checked (it needs the sender's selector).

## `checkAvailability` (type: `boolean`):

Adds "availability": registered, likely_available (the registry has no record) or unknown, with a note. This is a guess: premium, reserved or blocked names can still be unavailable, so confirm with a registrar before you rely on it.

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

How many domains are looked up at the same time. Requests to one registry are limited to 4 at a time whatever this is set to, to avoid rate limits.

## `whoisFallback` (type: `boolean`):

Some country-code TLDs (.jp, .io, .co, .it, .eu, .cn, .ru, .kr, ...) have no RDAP server. With this on, their registry's classic WHOIS server (port 43) is asked instead and its text answer is parsed.

## `followRegistrarRdap` (type: `boolean`):

For .com, .net and other registries that keep only basic data, ask the registrar's RDAP server for the registrant organization and country (one extra request per domain).

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

Only used for RDAP and DNS-over-HTTPS requests sent from Apify's network (not for port-43 WHOIS). Useful for very large lists if a registry rate-limits. Apify proxy usage is billed to your Apify account.

## Actor input object example

```json
{
  "domains": [
    "github.com",
    "https://www.bbc.co.uk/news",
    "spiegel.de",
    "sony.jp"
  ],
  "includeRegistration": true,
  "includeDns": true,
  "emailSecurity": true,
  "checkAvailability": false,
  "maxConcurrency": 10,
  "whoisFallback": true,
  "followRegistrarRdap": true
}
```

# Actor output Schema

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

No description

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

No description

## `full` (type: `string`):

No description

## `details` (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": [
        "github.com",
        "https://www.bbc.co.uk/news",
        "spiegel.de",
        "sony.jp"
    ]
};

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

```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": [
        "github.com",
        "https://www.bbc.co.uk/news",
        "spiegel.de",
        "sony.jp",
    ] }

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

```bash
echo '{
  "domains": [
    "github.com",
    "https://www.bbc.co.uk/news",
    "spiegel.de",
    "sony.jp"
  ]
}' |
apify call tidytools/bulk-domain-whois-dns-lookup --silent --output-dataset

```

## MCP server setup

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