# Bulk Domain Checker – Availability, Age, Expiry (`hardened_metapod/domain-rdap-dns-checker`) Actor

Check thousands of domains at once using official registry RDAP data and DNS: available or taken, creation date & age, expiry date, registrar, status, nameservers, DNSSEC, MX/mail provider, SPF and DMARC. No registrant personal data.

- **URL**: https://apify.com/hardened_metapod/domain-rdap-dns-checker.md
- **Developed by:** [Hardened Metapod](https://apify.com/hardened_metapod) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 domain checkeds

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 Domain Checker – Availability, Age, Expiry, Registrar & DNS

Check **thousands of domains per minute** and get, for each one:

- **Availability**: `registered`, `available` or `unknown` (with a `reason`). The Actor never guesses: `available` is only reported on a definitive registry RDAP 404 that DNS does not contradict.
- **Creation date and domain age**, **expiry date** and **days until expiry**, plus the last-updated date.
- **Registrar** name and IANA ID, **EPP status codes** and **nameservers**.
- **DNSSEC**.
- **DNS profile**: A/AAAA, MX with the **detected mail provider** (Google Workspace, Microsoft 365, Proofpoint…), **SPF** and **DMARC policy**.
- Optional **website check**: HTTP status, final URL, page title, HTTPS, and a parked-domain hint.

Data comes straight from the **official registry RDAP servers**, which replaced WHOIS for ICANN domains (IANA bootstrap), and from public DNS-over-HTTPS. It does not use scraped WHOIS sites or a proxy. **It never returns registrant or contact personal data.**

### Use cases

- **Domain name brainstorming.** Enter keywords and TLDs, for example `acmerobots` with `com, io, ai, co`, and get only the available names with *Only output available domains*.
- **Lead enrichment and qualification.** Add domain age, mail provider (Google vs Microsoft), DMARC policy and whether the website is live to a list of company domains.
- **Expiry monitoring.** Schedule a run over your portfolio or competitors' domains and alert on `daysUntilExpiry < 30`.
- **Fraud and phishing triage.** Very young domains (`ageDays < 30`) are a strong risk signal.
- **Email deliverability audits.** Find domains missing SPF or DMARC, or with `p=none`.

### Input

| Field | Description |
|---|---|
| `domains` | Domains or URLs. Accepts `https://www.example.com/page`, IDNs such as `münchen.de`, and comma- or newline-separated lists. Duplicates and `www.` are removed. |
| `keywords` + `tlds` | Every combination of keyword and TLD is checked. |
| `checkDns` | Adds the DNS profile (default on). |
| `checkWebsite` | Fetches the homepage of registered domains (slower, default off). |
| `onlyAvailable` | Outputs only available domains. You pay the small lookup fee for every domain checked, and the result fee only for the available ones saved. The website check is skipped in this mode. |
| `maxDomains`, `concurrency`, `perRegistryConcurrency` | Limits and politeness settings. |

### Output example

```json
{
  "domain": "apify.com", "tld": "com", "availability": "registered", "available": false, "source": "rdap",
  "registrar": "Amazon Registrar, Inc.", "registrarIanaId": "468",
  "createdAt": "2009-06-02T...", "expiresAt": "2035-06-02T...", "ageDays": 6357, "daysUntilExpiry": 3139,
  "status": ["client transfer prohibited"], "nameservers": ["ns-....awsdns-...."], "dnssec": false,
  "dns": { "a": ["..."], "mx": [{ "priority": 1, "exchange": "aspmx.l.google.com" }], "mailProvider": "Google Workspace",
           "spf": "v=spf1 include:_spf.google.com ...", "dmarc": "v=DMARC1; p=reject; ...", "dmarcPolicy": "reject" },
  "checkedAt": "2026-10-28T..."
}
```

A run summary (counts, duration, whether the spend limit was hit) is saved to the key-value store as `RUN_SUMMARY`.

### How availability works

1. **RDAP.** The actor asks the registry for the domain. HTTP 404 means the domain is not registered (`available`). If the domain still has nameservers in DNS, for example because the registry reserved it, it is reported as `registered`.
2. **DNS check** for every domain. A domain with nameservers is always `registered`. For TLDs with no public RDAP server (such as `.de` and `.jp`), a domain without nameservers is reported as `unknown` with reason `no_rdap_server`, because a registered domain with no nameservers is possible.
3. **Unknown TLDs** (not in the DNS root) and malformed names come back as `invalid` and are **not charged** (neither event).

Premium or reserved names can still show as `available`, so the registrar's checkout is always the final word on price.

#### When a result is `unknown`

RDAP requests are retried with exponential backoff (up to 4 attempts, honouring `Retry-After`). If the registry still gives no definitive answer, the domain is `unknown` and `available` is `null`. The `reason` field says why:

| reason | meaning |
|---|---|
| `rate_limited` | registry kept returning HTTP 429 |
| `timeout` | registry did not answer in time |
| `unreachable` | network/TLS error reaching the registry |
| `server_error` | registry kept returning HTTP 5xx |
| `ambiguous` | HTTP 200 but not a matching domain object, or invalid JSON |
| `http_<code>` | other unexpected status (e.g. `http_403`) |
| `no_rdap_server` | TLD has no public RDAP server and no DNS delegation |
| `rdap_bootstrap_unavailable` | IANA RDAP directory could not be loaded |

With **Only output available domains** on, unknowns are excluded from the dataset; `RUN_SUMMARY` reports `unknown`, `unknownExcludedFromOutput` and `unknownByReason` so you can re-run them.

**Billing for unknowns:** the `domain-checked` lookup fee is charged (the work was done), but the `domain` result fee is **not** charged for unknown results, in any mode.

### Pricing

Pay per event, two events. No monthly rental, and platform usage is included.

| Event | When | FREE/BRONZE | SILVER | GOLD |
|---|---|---|---|---|
| `domain-checked` | every valid domain looked up (all modes) | $0.30 / 1k | $0.25 / 1k | $0.20 / 1k |
| `domain` | every confirmed (registered/available) result saved; not for `unknown` | $1.20 / 1k | $1.05 / 1k | $0.90 / 1k |

A normal run (every domain saved) therefore costs **$1.50 per 1,000 domains** (SILVER $1.30, GOLD $1.10). With *Only output available domains*, you pay $0.30/1k for the lookups plus $1.20/1k for each available name returned. Invalid inputs and unknown TLDs are free. `unknown` results pay only the $0.30/1k lookup fee, never the result fee. Use the run's *Maximum cost per run* to cap spend; the actor stops cleanly when the cap is reached.

### Notes and limits

- `.com` and `.net` creation dates come from Verisign RDAP. Some ccTLD registries publish fewer fields (no registrar or dates); those fields are then `null`.
- Registry RDAP servers rate-limit aggressive clients. The default of 4 parallel requests per registry with automatic back-off is safe. Raise it only if you must.
- Only public registry data is used. Registrant, admin and tech contacts are deliberately never extracted.

# Actor input Schema

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

Domains or URLs to check (e.g. example.com, https://www.example.org/page, münchen.de). One per line.

## `keywords` (type: `array`):

Optional. Each keyword is combined with every TLD below (e.g. 'acmerobots' + com, io, ai) – great for brainstorming available names.

## `tlds` (type: `array`):

TLDs to combine with keywords.

## `checkDns` (type: `boolean`):

Add A/AAAA, MX + detected mail provider, SPF and DMARC records.

## `checkWebsite` (type: `boolean`):

For registered domains, fetch the homepage: HTTP status, final URL, page title, HTTPS, parked-domain hint. Slower.

## `onlyAvailable` (type: `boolean`):

Output only domains confirmed available by a definitive registry RDAP 404. Domains whose status could not be confirmed ("unknown") are left out, and their count is in the RUN_SUMMARY (unknownExcludedFromOutput, unknownByReason). Every domain looked up is billed the small per-lookup fee; the per-result fee applies only to saved available domains. Website check is skipped in this mode.

## `stripWww` (type: `boolean`):

Treat www.example.com as example.com.

## `maxDomains` (type: `integer`):

0 = no limit.

## `concurrency` (type: `integer`):

Parallel domain checks.

## `perRegistryConcurrency` (type: `integer`):

Max parallel requests to a single registry RDAP server (be polite – avoids rate limits).

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "github.io",
    "google.co",
    "heise.de",
    "my-unregistered-startup-name-2026.com"
  ],
  "keywords": [],
  "tlds": [
    "com",
    "io",
    "ai",
    "co",
    "dev",
    "app"
  ],
  "checkDns": true,
  "checkWebsite": false,
  "onlyAvailable": false,
  "stripWww": true,
  "maxDomains": 0,
  "concurrency": 10,
  "perRegistryConcurrency": 4
}
```

# Actor output Schema

## `overview` (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": [
        "apify.com",
        "github.io",
        "google.co",
        "heise.de",
        "my-unregistered-startup-name-2026.com"
    ],
    "tlds": [
        "com",
        "io",
        "ai",
        "co",
        "dev",
        "app"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hardened_metapod/domain-rdap-dns-checker").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",
        "google.co",
        "heise.de",
        "my-unregistered-startup-name-2026.com",
    ],
    "tlds": [
        "com",
        "io",
        "ai",
        "co",
        "dev",
        "app",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("hardened_metapod/domain-rdap-dns-checker").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",
    "google.co",
    "heise.de",
    "my-unregistered-startup-name-2026.com"
  ],
  "tlds": [
    "com",
    "io",
    "ai",
    "co",
    "dev",
    "app"
  ]
}' |
apify call hardened_metapod/domain-rdap-dns-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hardened_metapod/domain-rdap-dns-checker"
        }
    }
}
```

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/aHszSopj6HmXJiF56/builds/ze0bGnIBT1dvX1053/openapi.json
