# DNS Tools - Domain Age, WHOIS, Expiry & DNS Records Checker (`vero-api/dns-tools`) Actor

Everything about a domain in one run. Paste your domains, tick what you want to know — age, expiration date, last update, availability, owner, registrar, DNSSEC, the full WHOIS record, DNS records — and get one clean row per domain, ready to export as JSON, CSV, or Excel.

- **URL**: https://apify.com/vero-api/dns-tools.md
- **Developed by:** [VeroAPIs](https://apify.com/vero-api) (community)
- **Categories:** Developer tools, SEO tools, Lead generation
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## DNS Tools — WHOIS lookup, domain age, expiry, availability & DNS records in one run

Paste a list of domains, tick what you want to know, and get one clean row per domain. Age, expiration date, last update, availability, owner, registrar, DNSSEC, the full WHOIS record, and live DNS records — for one domain or a thousand. Checking a brand name? Add the name once and check it across every extension you care about.

### What you can do

- See how old a domain is, to the day: `31 years, 1 month, 5 days`.
- Catch expiring domains before they lapse, with days-to-expiry you can sort on.
- List only the domains expiring in the next 30, 60 or 90 days, expired ones included.
- Check whether a name is still available to register.
- Check one name across many extensions at once (`brightpixel` on .com, .io, .ai, .co…) and list only the free ones.
- Find out who owns a domain and which registrar it was bought through.
- Confirm DNSSEC is switched on and see the DS records.
- Pull the complete WHOIS record — status codes, name servers, every contact, raw text.
- Read the live DNS records: A, AAAA, CNAME, MX, NS, TXT, SOA, and CAA.
- Works across .com, .org, .io, .app, .dev, .co.uk, .de, .fr and hundreds more extensions.

### Why use it

- **Domain buying and SEO vetting** — see a domain's true age, registrar and history before you buy it or link to it.
- **Expired-domain hunting** — find names that are about to drop, or already have.
- **Brand naming** — check a shortlist of names across every extension in one run and keep only the free ones.
- **Portfolio renewals** — paste every domain you own and sort by days to expiry.
- **Lead enrichment** — add company age, registrar, mail servers and SPF records to a list of company websites.
- **Security audits** — check DNSSEC, CAA, name servers and transfer locks across many domains at once.

### How to use it

1. Paste your domains — one per line. Full URLs are fine too.
2. Or, to check names across extensions, add the names under **Check names across extensions** (without the extension, like `brightpixel`) and pick the extensions.
3. Tick the checks you want. Everything is on by default.
4. Optional: turn on **Only available domains**, or set **Expiring within** to a number of days.
5. Click **Start**.
6. Download your results as JSON, CSV, or Excel.

### Input example

Check three domains with every tool:

```json
{
  "domains": ["example.com", "wikipedia.org", "bbc.co.uk"]
}
```

Find which extensions are still free for two brand names:

```json
{
  "names": ["brightpixel", "pixelforge"],
  "tlds": ["com", "io", "ai", "co"],
  "tools": ["availability"],
  "onlyAvailable": true
}
```

List the domains in your portfolio that expire within 60 days:

```json
{
  "domains": ["example.com", "wikipedia.org", "bbc.co.uk"],
  "tools": ["age", "expiration", "registrar"],
  "expiringWithinDays": 60
}
```

### What you get

One row per domain, ready to filter in a spreadsheet:

| domain | isAvailable | age | expiresIn | registrar.name | dnssec.enabled |
|---|---|---|---|---|---|
| example.com | false | 31 years, 2 months, 10 days | 9 months, 19 days | RESERVED-Internet Assigned Numbers Authority | true |
| wikipedia.org | false | 25 years, 9 months, 12 days | 2 months, 18 days | MarkMonitor Inc. | false |
| bbc.co.uk | false | 31 years, 10 months, 11 days | 8 years, 1 month, 18 days | British Broadcasting Corporation | false |
| some-name-nobody-took-yet.com | true | | | | |

```json
{
  "domain": "example.com",
  "registrableDomain": "example.com",
  "checkedAt": "2026-10-25T01:17:49.362Z",
  "isAvailable": false,
  "registeredAt": "1995-08-14T04:00:00.000Z",
  "age": "31 years, 2 months, 10 days",
  "ageDays": 11394,
  "expiresAt": "2027-08-13T04:00:00.000Z",
  "expiresIn": "9 months, 19 days",
  "expiresInDays": 292,
  "isExpired": false,
  "updatedAt": "2026-08-14T08:01:43.000Z",
  "updatedAgo": "2 months, 10 days",
  "owner": null,
  "registrar": { "name": "RESERVED-Internet Assigned Numbers Authority", "ianaId": "376", "url": null, "email": null, "phone": null },
  "dnssec": { "enabled": true, "dsRecords": [{ "keyTag": 2371, "algorithm": 13, "digestType": 2, "digest": "C988EC…" }] },
  "whois": { "source": "rdap", "server": "https://rdap.verisign.com/com/v1/", "statuses": ["clientDeleteProhibited", "clientTransferProhibited", "clientUpdateProhibited"], "nameservers": ["elliott.ns.cloudflare.com", "hera.ns.cloudflare.com"], "contacts": [], "raw": "…" },
  "dns": { "a": ["104.20.23.154", "172.66.147.243"], "mx": [{ "priority": 0, "exchange": "." }], "ns": ["elliott.ns.cloudflare.com", "hera.ns.cloudflare.com"], "txt": ["v=spf1 -all"] }
}
```

Only the checks you tick appear in the row. A run that only asks for expiration dates gives you the domain, when it was checked, and the four expiry columns: expiry date, time left in words, days left, and whether it has expired. Once a domain has expired, "Expires in" reads like `expired 3 days ago` and the days count goes negative.

A domain is listed under **Domains we couldn't check** in the Output tab, with the reason, when the line isn't a domain name or when the details you picked didn't come back, for example from a registry that took too long to answer. These are never charged, so you can run them again for free.

Two cases still give you a row, with a **Note** saying what's missing:

- **.es, .ch and .li domains** — these registries never share registration details, so you get the domain's live DNS records (when you picked DNS records and the domain has some).
- **Name servers that don't answer** — you still get the registration details you picked; only the DNS records are missing.

### What each registry shares

Every registry decides what it publishes, so some columns stay empty for some extensions:

- **.com, .net, .org, .io, .app, .dev, .co.uk, .fr and most others** — the full record: dates, registrar, status codes, name servers, DNSSEC.
- **.nl** — everything except the expiry date, which the .nl registry doesn't publish.
- **.de** — availability, status, name servers, last change date and DNSSEC. The .de registry publishes no registration or expiry dates.
- **.eu and .au** — availability, registrar and name servers, without registration or expiry dates.
- **.es, .ch and .li** — these registries don't share registration details publicly. You still get the domain's live DNS records, and the row's **Note** says the registration part isn't available.

### Run it from your code

Start a run and get the rows back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/vero-api~dns-tools/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["example.com", "wikipedia.org"], "tools": ["age", "expiration", "registrar"]}'
```

From JavaScript:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('vero-api/dns-tools').call({
  domains: ['example.com', 'wikipedia.org'],
  tools: ['age', 'expiration', 'registrar'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((row) => `${row.domain}: ${row.age}, expires in ${row.expiresIn}`));
```

From Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("vero-api/dns-tools").call(run_input={
    "domains": ["example.com", "wikipedia.org"],
    "tools": ["age", "expiration", "registrar"],
})
for row in client.dataset(run.default_dataset_id).iterate_items():
    print(row["domain"], row["age"], row["expiresIn"])
```

### Use it from your AI assistant (MCP)

Ask Claude, Cursor, VS Code, or any assistant that supports MCP about domains in plain words. Add this server to its MCP settings:

```json
{
  "mcpServers": {
    "dns-tools": {
      "url": "https://mcp.apify.com?tools=vero-api/dns-tools"
    }
  }
}
```

Then just ask, for example:

- *"How old is stripe.com and when does it expire?"*
- *"Is brightpixel free on .com, .io or .ai?"*
- *"Which of these 20 domains expire in the next 60 days?"*

### Integrations

Send results straight to Google Sheets, Slack, Airtable, or your own webhook with Apify integrations, or connect the actor to Make, Zapier, and n8n. Schedule a weekly run over your portfolio with **Expiring within** set, and you get a renewal list before anything lapses.

### Pricing

You pay only for the domains you get results for — no subscriptions, no surprises. A line that isn't a domain name, a domain we couldn't check, and a domain a filter leaves out are never charged. The run stops right at the spending limit you set.

### FAQ

**Where does the data come from?** Straight from the official registry of each extension, live at the moment you ask. Nothing is cached, so you always see today's record.

**Can I check subdomains?** Yes. Registration details come from the parent domain, and DNS records come from the exact host you entered — so `www.example.com` shows its own CNAME.

**Why is the owner empty for some domains?** Many registries hide personal details for privacy. Hidden fields come back empty and **Details hidden** is true, so you can tell a private owner from a missing one. You still get the registrar, the dates, and everything else.

**Why are the dates empty for my .de domain?** The .de registry doesn't publish registration or expiry dates for anyone. You still get availability, status, name servers, the last change date and DNSSEC.

**What happens with .es or .ch domains?** Those registries don't share registration details publicly, so you get the live DNS records with a note saying so. If you only asked for registration details, the domain is listed under **Domains we couldn't check** and isn't charged.

**How do I check one name on many extensions?** Add it under **Names** without the extension (`brightpixel`, not `brightpixel.com`), pick the extensions, and every pair is checked — 5 names on 6 extensions is 30 domains. Turn on **Only available domains** to keep just the free ones.

**Can I find domains that are about to expire?** Yes. Set **Expiring within** to a number of days and only those domains are listed, expired ones included. Domains whose registry publishes no expiry date are left out.

**How fast is it?** Ten domains are checked at once. A list of 500 finishes in a minute or two, a thousand in under five.

**Do I need an API key?** No. Paste your domains and run it — there is nothing to sign up for.

**Can I call it from my app?** Yes — start a run through the Apify API and read the rows from its dataset. See **Run it from your code** above.

### More tools from us

- [Email Validator](https://apify.com/vero-api/email-validator) — check whether email addresses are real and can receive mail.
- [Website Screenshot Generator](https://apify.com/vero-api/website-screenshot) — full-page screenshots of any list of websites.
- [Google Ads Transparency Scraper](https://apify.com/vero-api/google-ads-transparency-scraper) — every ad a company runs on Google, YouTube and Search.
- [Phone Number Carrier Lookup](https://apify.com/vero-api/phone-carrier-lookup) — carrier, line type, country and time zone for phone numbers.
- [Telegram Username Checker](https://apify.com/vero-api/telegram-username-checker) — see which Telegram usernames are taken by profiles, channels, groups or bots.

# Actor input Schema

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

Domains to check, one per line, like example.com. Full URLs like https://www.example.com/page work too. Up to 1000 per run, counting the name and extension pairs below.

## `tools` (type: `array`):

Pick the information you want for each domain. Everything is selected by default; untick what you don't need.

## `names` (type: `array`):

Names to check across the extensions below, one per line, without the extension, like brightpixel. Each name is paired with every extension, so 5 names and 6 extensions give 30 domains.

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

Extensions to pair with each name above, like com, io or co.uk. Pick from the list or type your own.

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

Turn on to list only the domains you can register right now. Taken domains are left out and never charged.

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

A number of days, like 30: only domains that expire within that many days are listed, including ones already expired. Leave empty to list every domain. Domains left out are never charged.

## Actor input object example

```json
{
  "domains": [
    "example.com",
    "wikipedia.org"
  ],
  "tools": [
    "availability",
    "age",
    "expiration",
    "lastUpdate",
    "owner",
    "registrar",
    "dnssec",
    "whois",
    "dns"
  ],
  "names": [
    "brightpixel",
    "pixelforge"
  ],
  "tlds": [
    "com",
    "net",
    "org",
    "io",
    "ai",
    "co"
  ],
  "onlyAvailable": false
}
```

# Actor output Schema

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

One row per domain: domain, isAvailable, registeredAt, age, expiresAt, expiresInDays, updatedAt, owner, registrar, dnssec, whois and dns, for the checks you selected. A note explains any part a registry didn't share.

## `failed` (type: `string`):

A list of { domain, error } for lines that weren't domain names and for domains whose selected details didn't come back, with the reason for each. Never charged, so you can run them again for free. An empty list when every domain was checked.

# 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": [
        "example.com",
        "wikipedia.org",
        "bbc.co.uk"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("vero-api/dns-tools").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": [
        "example.com",
        "wikipedia.org",
        "bbc.co.uk",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("vero-api/dns-tools").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": [
    "example.com",
    "wikipedia.org",
    "bbc.co.uk"
  ]
}' |
apify call vero-api/dns-tools --silent --output-dataset

```

## MCP server setup

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

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/C9gcvoU8Ym3CQjviX/builds/Pyz0UnETDdWg0b6UM/openapi.json
