# Domain WHOIS, DNS & Email Security Lookup (RDAP, SPF, DMARC) (`optimistical_lectern/domain-whois-dns-lookup`) Actor

Bulk domain lookup: registrar, creation and expiry dates, domain age, availability, nameservers, A/AAAA/MX/NS/TXT records, mail provider, SPF and DMARC policy. Uses the official RDAP registries, so results are structured and current. Accepts domains, URLs or email addresses.

- **URL**: https://apify.com/optimistical_lectern/domain-whois-dns-lookup.md
- **Developed by:** [Ivan Honer](https://apify.com/optimistical_lectern) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 domains

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, DNS & Email Security Lookup

Bulk domain lookup in one run: **who registered a domain and when, when it expires, whether it is available, where it points, who handles its email, and how its SPF and DMARC are set**. One clean, structured row per domain, ready for a spreadsheet, a CRM or an AI agent.

- ✅ **Registration data from the official registries** (RDAP, the modern replacement for WHOIS), so dates and statuses are real fields, not scraped text
- ✅ **DNS and email security in the same row**: A, AAAA, MX, NS, TXT, mail provider, SPF and DMARC
- ✅ **Accepts anything domain-shaped**: `example.com`, `https://www.example.com/page`, `jane@example.com`
- ✅ **Fast and cheap**: no browser, no proxies, thousands of domains in minutes
- ✅ **Invalid inputs are reported and never charged**

### What does Domain WHOIS, DNS & Email Security Lookup do?

You give it a list of domains, URLs or email addresses. For each one it:

1. Normalises the input to the registered domain (`https://www.bbc.co.uk/news` becomes `bbc.co.uk`).
2. Asks the domain's registry for its registration record over RDAP: registrar, creation, update and expiry dates, status codes, nameservers and DNSSEC.
3. Resolves its DNS records through public resolvers: A, AAAA, MX, NS and TXT.
4. Works out the email setup: whether the domain accepts mail, which provider runs it (Google Workspace, Microsoft 365, Proofpoint, Mimecast and others), and its SPF and DMARC policies.
5. Returns everything as one row, with the domain's age and the days left until it expires already calculated.

### Use cases

- 🎯 **Lead scoring and enrichment.** Domain age and mail provider say a lot about a company. A ten-year-old domain on Microsoft 365 is a different prospect from one registered last month.
- 🛡️ **Email security and deliverability audits.** Find every domain in a client or vendor list with no DMARC record, a `p=none` policy or a permissive SPF `+all`.
- ⏰ **Expiry monitoring.** Schedule a daily run over your portfolio and alert on `daysUntilExpiry`.
- 🔎 **Domain research.** Check a list of candidate names for availability in one go.
- 🕵️ **Phishing and fraud review.** A lookalike domain registered last week, with no MX record, is worth a second look.
- 🤖 **AI agents.** A single structured answer to "who owns this domain and is its email set up properly?"

### What data can you get?

| Field | What it is |
|---|---|
| `domain` | The registered domain the input was normalised to |
| `registered` / `available` | Whether the name is taken, from the registry itself |
| `whois.registrar`, `whois.registrarIanaId` | Who the domain is registered through |
| `whois.registrarAbuseEmail` | Where to report abuse |
| `whois.registrantOrganization`, `whois.registrantCountry` | The owner, when the registry publishes it (most redact this) |
| `whois.createdDate`, `whois.updatedDate`, `whois.expiresDate` | Registration dates (ISO 8601) |
| `whois.ageDays`, `whois.daysUntilExpiry` | Calculated for you |
| `whois.status` | Registry status codes such as `client transfer prohibited` |
| `whois.nameservers`, `whois.dnssec` | Delegation and DNSSEC signing |
| `dns.a`, `dns.aaaa`, `dns.mx`, `dns.ns`, `dns.txt` | DNS records |
| `dns.email.acceptsMail`, `dns.email.provider` | Whether mail is accepted, and by whom |
| `dns.email.spf`, `dns.email.spfPolicy` | The SPF record and its `all` qualifier (`-`, `~`, `?`, `+`) |
| `dns.email.dmarc.policy`, `.subdomainPolicy`, `.pct`, `.rua` | The DMARC record, parsed |
| `status`, `whoisNote`, `whoisError` | See [Statuses and errors](#statuses-and-errors) |

### How to use it

1. Click **Try for free** (or **Start**) at the top of this page.
2. Paste your domains into **Domains**, one per line. URLs and email addresses are fine.
3. Leave **Registration data** and **DNS and email security** switched on, or turn one off if you only need half.
4. Press **Start**. Small lists finish in seconds.
5. Open the **Output** tab and download the results as Excel, CSV, JSON or HTML, or send them on with an integration.

### Input examples

**A mixed list.**

```json
{
  "domains": ["anthropic.com", "https://www.bbc.co.uk/news", "jane@stripe.com", "is-this-name-free-12345.com"]
}
```

**Only email security, for a deliverability audit.**

```json
{
  "domains": ["client-one.com", "client-two.com", "client-three.com"],
  "includeWhois": false,
  "includeDns": true
}
```

**Only availability and registration, for domain research.**

```json
{
  "domains": ["getacme.com", "acmehq.com", "tryacme.io"],
  "includeDns": false
}
```

### Output example

```json
{
  "input": "apify.com",
  "domain": "apify.com",
  "status": "ok",
  "registered": true,
  "available": false,
  "whois": {
    "registrar": "Amazon Registrar, Inc.",
    "registrarIanaId": "468",
    "registrarAbuseEmail": "trustandsafety@support.aws.com",
    "createdDate": "2009-06-02T17:14:10Z",
    "updatedDate": "2026-05-16T16:53:04Z",
    "expiresDate": "2035-06-02T17:14:10Z",
    "ageDays": 6343,
    "daysUntilExpiry": 3153,
    "status": ["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
  },
  "dns": {
    "resolves": true,
    "a": ["3.170.42.109", "3.170.42.68"],
    "mx": [{ "priority": 1, "exchange": "aspmx.l.google.com" }, { "priority": 5, "exchange": "alt1.aspmx.l.google.com" }],
    "ns": ["ns-1225.awsdns-25.org", "ns-1928.awsdns-49.co.uk"],
    "txt": ["google-site-verification=...", "..."],
    "email": {
      "acceptsMail": true,
      "provider": "Google Workspace",
      "spf": "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com include:19497222.spf05.hubspotemail.net -all",
      "spfPolicy": "-",
      "dmarc": {
        "record": "v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc-reports@apify.com; ri=604800",
        "policy": "reject",
        "subdomainPolicy": "reject",
        "pct": 100,
        "rua": "mailto:dmarc-reports@apify.com"
      }
    }
  }
}
```

A name nobody has registered comes back as `"registered": false, "available": true`.

### Statuses and errors

Every input gets a row, so your output always lines up with your list.

| `status` | Meaning | Charged? |
|---|---|---|
| `ok` | The lookup ran. Check `whoisNote` and `whoisError` for partial results | Yes |
| `invalid_input` | Not a domain, URL or email address (for example `localhost` or an IP address) | No |

| Extra field | Meaning |
|---|---|
| `whoisNote: "The .de registry does not publish RDAP"` | Some country registries (including .de and .io) have no RDAP service. You still get `registered`, DNS and email security; `whois` is `null` |
| `whoisError: "RDAP timeout"` or `"RDAP returned HTTP 429"` | The registry was slow or rate-limited the lookup after retries. DNS results are still included. Run that domain again later |

### How much does it cost?

You pay per domain checked, and invalid inputs are free. Platform usage is included, so there is nothing else to budget for.

| Domains | Price on the Free and Bronze tiers |
|---|---|
| 100 | $0.20 |
| 1,000 | $2.00 |
| 10,000 | $20.00 |

Larger Apify plans pay less per domain: 15% less on Silver, and 25% less on Gold and above.

### FAQ

#### Can I use it through the API?

Yes. Every Apify Actor has an API. The quickest way for small lists is the synchronous endpoint, which runs the Actor and returns the results in one request:

```bash
curl -X POST "https://api.apify.com/v2/acts/optimistical_lectern~domain-whois-dns-lookup/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["anthropic.com", "stripe.com"]}'
```

With the JavaScript client:

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('optimistical_lectern/domain-whois-dns-lookup').call({ domains: ['anthropic.com', 'stripe.com'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

With the Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("optimistical_lectern/domain-whois-dns-lookup").call(run_input={"domains": ["anthropic.com", "stripe.com"]})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

#### Can AI agents use it?

Yes. It runs with limited permissions, so it needs no approval step and works in autonomous workflows. Add it to Claude, Cursor or any other MCP client through Apify's MCP server at `mcp.apify.com`, using the Actor name `optimistical_lectern/domain-whois-dns-lookup`.

#### Can I connect it to other apps or run it on a schedule?

Yes. Use **Integrations** on the Actor page to send results to Google Sheets, Slack, Zapier, Make, n8n or a webhook, and **Schedules** in Apify Console to run it daily or weekly, for example for expiry monitoring.

#### How many domains can I check?

There is no fixed limit. Registries rate-limit lookups, so very large lists under a single TLD run most reliably with **Domains in parallel** set to 4 or 8. Lists of tens of thousands are fine.

#### Why is the registrant name missing?

Since GDPR, most registries redact personal registrant details. The organisation and country are returned when the registry publishes them.

#### Is this legal?

The tool reads public registration data through the registries' official RDAP service, and DNS records through public resolvers. Both are public by design. How you use the results is up to you; check the rules that apply to you before using contact details from registration records.

#### Something looks wrong?

Open an issue on the **Issues** tab with the domain. Every report is read.

### Related tools

- [📇 Website Contact Extractor](https://apify.com/optimistical_lectern/website-contact-extractor): emails, phones and social profiles from the same websites
- [📄 PDF Text Extractor](https://apify.com/optimistical_lectern/pdf-text-extractor): text from PDF links
- [💼 Google Jobs Scraper](https://apify.com/optimistical_lectern/google-jobs-scraper): who is hiring, with salaries and apply links

# Actor input Schema

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

One per line. Domains, URLs and email addresses are all accepted; the domain is taken from each.

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

Registrar, created / updated / expiry dates, age, status codes, nameservers, DNSSEC and whether the domain is available.

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

A, AAAA, MX, NS and TXT records, the mail provider behind the MX records, and the SPF and DMARC policies.

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

Registries rate-limit lookups. Lower this if you see RDAP errors on very large lists.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "wikipedia.org",
    "this-name-is-probably-free-12345.com"
  ],
  "includeWhois": true,
  "includeDns": true,
  "maxConcurrency": 8
}
```

# Actor output Schema

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

One item per input, as listed in the README.

# 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",
        "wikipedia.org",
        "this-name-is-probably-free-12345.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("optimistical_lectern/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",
        "wikipedia.org",
        "this-name-is-probably-free-12345.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("optimistical_lectern/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",
    "wikipedia.org",
    "this-name-is-probably-free-12345.com"
  ]
}' |
apify call optimistical_lectern/domain-whois-dns-lookup --silent --output-dataset

```

## MCP server setup

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