# Bulk WHOIS Lookup – RDAP, DNS Records, SSL & Redirects (`openharvest/domain-intel`) Actor

Bulk WHOIS lookup via RDAP (registrar, creation and expiry dates, status, name servers), DNS records (A, AAAA, MX, NS, TXT, SPF, DMARC, CAA, SOA), SSL certificate (issuer, SANs, days to expiry) and HTTP redirect chain with HSTS - one flat row per domain. Pay per domain.

- **URL**: https://apify.com/openharvest/domain-intel.md
- **Developed by:** [Enes Bulanık](https://apify.com/openharvest) (community)
- **Categories:** Developer tools, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 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 WHOIS Lookup – RDAP, DNS Records, SSL & Redirects

Bulk WHOIS lookup via RDAP (registrar, creation and expiry dates, status, name servers), DNS records (A, AAAA, MX, NS, TXT, SPF, DMARC, CAA, SOA), SSL certificate (issuer, SANs, days to expiry) and HTTP redirect chain with HSTS - one flat row per domain. Pay per domain.

**Price: $2 per 1,000 domains.** Invalid domains are free.

### What does this bulk WHOIS lookup do?

For every domain or URL you enter, it runs up to four checks in parallel:

- **Registration (RDAP + WHOIS fallback)**: registered or not, registrar and IANA ID, creation / update / expiry dates, domain age, days to expiry, EPP status codes, transfer lock, name servers, DNSSEC, registrant **organisation and country** (only if public, never personal data). RDAP follows the official IANA bootstrap. For ccTLDs without RDAP (`.de`, `.io`, `.tr`, `.co`, `.ru`, `.jp`, …) it falls back to port-43 WHOIS automatically.
- **DNS**: A, AAAA, CNAME, MX (sorted by priority), NS, TXT, **SPF** (record, `all` qualifier, lookup count, validity), **DMARC** (`_dmarc` record and policy), CAA and SOA.
- **SSL/TLS**: valid or not and why (`CERT_HAS_EXPIRED`, `ERR_TLS_CERT_ALTNAME_INVALID`, `DEPTH_ZERO_SELF_SIGNED_CERT`…), issuer, subject, SANs, valid from/to, **days to expiry**, TLS version, cipher, ALPN (h2), key type/size, serial, SHA-256 fingerprint, chain.
- **HTTP**: full **redirect chain** from `http://`, final URL and status, HTTP→HTTPS redirect, **HSTS** (max-age, includeSubDomains, preload), `Server` / `X-Powered-By`, security headers (CSP, X-Frame-Options…), response time, TTFB and page title.
- **Issues**: a list of detected problems such as `SSL_CERT_HAS_EXPIRED`, `DOMAIN_EXPIRES_SOON`, `NO_DMARC`, `NO_SPF`, `NXDOMAIN`, `NO_HTTPS`, `HTTP_404`.

Input can be bare domains, subdomains or full URLs (`https://shop.example.de/path`). Internationalized names (`münchen.de`) are converted to punycode.

### Why use it?

- **One row per domain.** Registration, DNS, SSL and HTTP results sit in the same flat row, so you can load them straight into a sheet, CRM or monitoring dashboard.
- **ccTLD coverage.** RDAP where it exists, WHOIS where it doesn't. Tested with .com, .org, .io, .ai, .app, .co, .co.uk, .de, .fr, .nl, .ru, .jp, .com.tr, .edu.tr.
- **No personal data.** Registrant names, emails, phone numbers and street addresses are never output.
- **No browser.** 35 domains with all four checks finish in about 35–55 s.

### Use cases

- **Domain portfolio and expiry monitoring**: schedule daily runs and alert on `DOMAIN_EXPIRES_SOON` or `SSL_EXPIRES_SOON`.
- **Security and compliance audits**: find expired or mismatched certificates, missing HSTS, missing SPF/DMARC.
- **Email deliverability**: check SPF, DMARC policy and MX for prospects' or customers' domains.
- **Lead enrichment and sales research**: domain age, registrar, hosting server, email provider (MX), final website URL.
- **M\&A, brand protection and OSINT**: registration dates, registrars and name servers of look-alike domains. Also checks whether a domain is free (`registered: false`).
- **SEO / migrations**: verify redirect chains (www/non-www, http→https) after a site move.

### How to use it

1. Enter domains or URLs in **Domains or URLs** (one per line, or comma-separated).
2. Pick the **Checks to run** (all four by default; the price is the same).
3. Click **Start** and export to JSON, CSV or Excel, or connect via API, Make, Zapier or webhooks.

### Input example

```json
{
    "domains": ["apify.com", "google.com.tr", "nic.io", "https://www.bbc.co.uk/news", "expired.badssl.com"],
    "checks": ["rdap", "dns", "ssl", "http"]
}
```

| Field | Description | Default |
|---|---|---|
| `domains` | Domains, subdomains or URLs | demo: apify.com, wikipedia.org, expired.badssl.com |
| `checks` | Any of `rdap`, `dns`, `ssl`, `http` | all |
| `maxConcurrency` | Domains checked in parallel (1–25) | 10 |

### Output

Real output for `apify.com` (shortened):

```json
{
    "type": "domain",
    "domain": "apify.com",
    "registrableDomain": "apify.com",
    "registered": true,
    "registrar": "Amazon Registrar, Inc.",
    "registrarIanaId": "468",
    "createdAt": "2009-06-02T17:14:10.000Z",
    "updatedAt": "2026-05-16T16:53:04.000Z",
    "expiresAt": "2035-06-02T17:14:10.000Z",
    "domainAgeDays": 6337,
    "daysToDomainExpiry": 3159,
    "domainStatus": ["client transfer prohibited"],
    "transferLocked": true,
    "nameservers": ["ns-1225.awsdns-25.org", "ns-1928.awsdns-49.co.uk", "ns-449.awsdns-56.com", "ns-839.awsdns-40.net"],
    "dnssec": true,
    "registrationSource": "rdap",
    "registrationServer": "rdap.verisign.com",
    "dnsResolves": true,
    "aRecords": ["18.239.255.67", "18.239.255.31", "18.239.255.92", "18.239.255.10"],
    "mxRecords": [{ "priority": 1, "exchange": "aspmx.l.google.com" }, { "priority": 5, "exchange": "alt1.aspmx.l.google.com" }],
    "spf": "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com include:19497222.spf05.hubspotemail.net -all",
    "spfAll": "fail",
    "spfLookups": 6,
    "spfValid": true,
    "dmarc": "v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc-reports@apify.com; ri=604800",
    "dmarcPolicy": "reject",
    "sslValid": true,
    "sslIssuer": "Amazon",
    "sslSubject": "*.apify.com",
    "sslSans": ["*.apify.com", "apifier.com", "*.apifier.com", "apify.com"],
    "sslValidTo": "2027-01-16T23:59:59.000Z",
    "sslDaysToExpiry": 101,
    "sslProtocol": "TLSv1.3",
    "httpStatus": 200,
    "finalUrl": "https://apify.com/",
    "redirectCount": 1,
    "redirectChain": [
        { "url": "http://apify.com/", "status": 301, "location": "https://apify.com/" },
        { "url": "https://apify.com/", "status": 200, "location": null }
    ],
    "httpsRedirect": true,
    "hsts": true,
    "hstsMaxAge": 15768000,
    "pageTitle": "Apify: Marketplace of ready-to-run tools for AI",
    "responseTimeMs": 1724,
    "issues": [],
    "errors": {},
    "checkedAt": "2026-10-07T19:23:06.391Z"
}
```

`expired.badssl.com` returns `"sslValid": false`, `"sslError": "CERT_HAS_EXPIRED"`, `"sslDaysToExpiry": -4196` and `"issues": ["SSL_CERT_HAS_EXPIRED"]`.
`google.com.tr` (no RDAP for .tr) returns `"registrationSource": "whois"`, `"registrationServer": "whois.trabis.gov.tr"`, `"createdAt": "2001-08-23T00:00:00.000Z"` and `"registrantOrg": "Google LLC"`.

**Error (free)**:

```json
{ "type": "error", "input": "192.168.1.1", "errorCode": "INVALID_DOMAIN", "error": "\"192.168.1.1\" is an IP address - enter a domain name instead." }
```

Error codes: `INVALID_DOMAIN` (bad syntax, IP, unknown TLD), `NO_DATA` (every check failed), `CHECK_FAILED`.

The dataset has four views: **Overview**, **WHOIS / RDAP**, **DNS & email security** and **SSL & HTTP**.

### Pricing

Pay per event: you pay per checked domain, and platform usage is included.

| Event | Price | When |
|---|---|---|
| `domain` | **$0.002** ($2 per 1,000) | Each domain row saved (any number of checks) |
| Errors | **Free** | Invalid input, or no check returned any data |

| Job | Cost |
|---|---|
| Default input (3 domains) | $0.006 |
| 1,000 domains, all checks | $2.00 |
| Daily expiry monitoring of 200 domains (30 days) | $12.00 |

A domain that is **not registered** is still a useful answer (`registered: false`), so it is charged. Set Apify's **Max cost per run** to cap spending. The Actor stops exactly at your budget.

### Use with AI agents (MCP)

This Actor works as a tool for AI agents through the [Apify MCP server](https://mcp.apify.com) (Claude, Cursor, VS Code, OpenAI Agents and others). Example tool input:

```json
{ "domains": ["example.com", "example.org"], "checks": ["rdap", "ssl"] }
```

The agent gets flat fields (`registered`, `expiresAt`, `sslValid`, `sslDaysToExpiry`, `issues`) that answer questions like "who is the registrar and when does it expire?" or "is the certificate valid?" without parsing raw WHOIS text.

### FAQ

**Why is the registrant name missing?**
It is left out on purpose. Since GDPR, most registries redact registrant contacts anyway. This Actor never outputs personal names, emails, phone numbers or addresses. It returns the registrant **organisation** and **country** only when they are public and clearly belong to a company.

**Why are some registration fields empty for .de / .jp domains?**
Some registries publish very little. DENIC (.de) only shows status, name servers and the last change date. JPRS (.jp) shows no registrar. The Actor returns what the registry publishes.

**RDAP or WHOIS?**
RDAP (the modern JSON successor of WHOIS) is used whenever the TLD has an RDAP server in the IANA bootstrap. Otherwise the Actor asks `whois.iana.org` for the TLD's WHOIS server and parses its answer. `registrationSource` tells you which one was used.

**Does it check certificate revocation?**
No. OCSP/CRL revocation is not checked, so a revoked but unexpired certificate shows as valid.

**Which ports are checked?**
SSL is checked on port 443 and HTTP starts at `http://domain/` (port 80). If port 80 is closed, it falls back to HTTPS. Ports in the input (`host:8443`) are ignored.

**Will WHOIS servers block me?**
Requests are rate-limited per registry server (2–3 in parallel) and retried with backoff. Very large runs on a single ccTLD can still hit registry limits, and those rows show the error in `errors.rdap`.

### Is it legal?

The Actor only queries **public** sources: RDAP/WHOIS registries, public DNS, and each website's public TLS handshake and homepage response (one request per redirect hop). It does not collect personal data. Use the results in line with registry terms of use (for example, no bulk WHOIS for spam) and applicable law.

### Related actors

- *Coming soon:* Website Tech Stack Detector
- *Coming soon:* Sitemap URL Extractor
- *Coming soon:* Keyword Suggestions Scraper

### Support & feedback

Missing a field or a ccTLD that parses badly? Open an issue in the **Issues** tab with the domain.

# Actor input Schema

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

Domains, subdomains or full URLs, one per line. Accepted: <code>apify.com</code>, <code>www.example.co.uk</code>, <code>https://shop.example.de/path</code>, internationalized names (<code>münchen.de</code>). Duplicates are removed.

## `checks` (type: `array`):

<b>rdap</b>: registration data (registrar, created/expiry dates, status, name servers, DNSSEC) via RDAP, with WHOIS fallback for TLDs without RDAP. <b>dns</b>: A, AAAA, CNAME, MX, NS, TXT, SPF, DMARC, CAA, SOA. <b>ssl</b>: certificate issuer, SANs, validity, days to expiry, TLS version. <b>http</b>: redirect chain, final URL, status, server, HSTS, security headers, response time, page title. The price is the same however many checks you pick.

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

How many domains are checked in parallel. Registry servers are always queried politely (max 2-3 parallel requests per server).

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "google.com.tr",
    "nic.io",
    "https://www.bbc.co.uk/news"
  ],
  "checks": [
    "rdap",
    "dns",
    "ssl",
    "http"
  ],
  "maxConcurrency": 10
}
```

# Actor output Schema

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

All scraped items in the default dataset (JSON, CSV, Excel and more via the dataset export).

## `files` (type: `string`):

Records saved in the default key-value store, e.g. the run summary.

# 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",
        "expired.badssl.com"
    ],
    "checks": [
        "rdap",
        "dns",
        "ssl",
        "http"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("openharvest/domain-intel").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",
        "expired.badssl.com",
    ],
    "checks": [
        "rdap",
        "dns",
        "ssl",
        "http",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("openharvest/domain-intel").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",
    "expired.badssl.com"
  ],
  "checks": [
    "rdap",
    "dns",
    "ssl",
    "http"
  ]
}' |
apify call openharvest/domain-intel --silent --output-dataset

```

## MCP server setup

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

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/zLneRqBgbWORsq9rb/builds/fqaeqCeJhVsTaelIH/openapi.json
