# Bulk WHOIS Domain Lookup: RDAP, DNS, SSL & Security Headers (`ventura_workalong/domain-lookup-bundle`) Actor

Bulk WHOIS / RDAP domain lookup: registrar, domain age, creation and expiry dates, plus DNS records (MX, SPF, DMARC, email provider), SSL certificate expiry and a security-headers grade, in one call per domain. No personal data. $0.003 per domain; unregistered domains are free.

- **URL**: https://apify.com/ventura_workalong/domain-lookup-bundle.md
- **Developed by:** [Ventura WorkAlong](https://apify.com/ventura_workalong) (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 $3.00 / 1,000 domain analyzeds

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 Domain Lookup: RDAP, DNS, SSL Certificate & Security Headers

**Bulk WHOIS Domain Lookup** runs four domain checks in one call: WHOIS/RDAP registration data (registrar, domain age, expiry), DNS records, the SSL certificate, and HTTP security headers. For each domain you get:

1. **Registration data:** registrar, creation, expiry and update dates, domain age, statuses, nameservers and DNSSEC. It comes from **RDAP**, the official, structured successor to WHOIS. **No personal data**: registrant and other contacts are never returned.
2. **DNS:** A, AAAA, MX, NS, TXT, CAA and SOA records, plus the SPF record, the DMARC policy and the detected **email provider** (Google Workspace, Microsoft 365, Zoho, and others).
3. **SSL certificate:** whether it's valid (trusted chain and matching hostname), issuer, expiry date and days left, SANs, key type and TLS version.
4. **Security headers:** a 0–100 score and A+–F grade for HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy and COOP. It also checks the HTTP→HTTPS redirect and flags server version disclosure.

**$0.003 per domain.** Invalid, unregistered and completely unreachable domains are free.

### How to look up WHOIS data for a list of domains

1. Paste domains, hostnames or URLs into **Domains**, one per line.
2. Optional: switch off sections you don't need (it's faster; the price is the same).
3. Click **Start**. The **Overview** table shows registrar, created and expiry dates, email provider, DMARC, SSL days left and headers grade per domain. Export to CSV/Excel or use the API.

### Use cases

- **AI agents (MCP):** "Is this domain legit? How old is it? Who hosts its email?" One cheap call answers all of it.
- **KYB, fraud and vendor checks:** domain age, registrar, valid SSL, DMARC enforcement.
- **Sales and lead enrichment:** email provider (Google vs Microsoft), DNS host, security posture.
- **Security and IT monitoring:** expiring certificates and domains, missing security headers, weak SPF/DMARC across a portfolio of domains.

### Input

| Field | What it does | Default |
|---|---|---|
| `domains` | Domains, hostnames or URLs. Registration is looked up for the registrable domain (`shop.bbc.co.uk` → `bbc.co.uk`). SSL and headers use the host you give; a bare domain falls back to `www.` if needed. | required |
| `includeRegistration` | RDAP registration data | true |
| `includeDns` | DNS records + SPF/DMARC/email provider | true |
| `includeSsl` | TLS certificate check on port 443 | true |
| `includeSecurityHeaders` | One homepage request; header grade + HTTPS redirect | true |
| `respectRobotsTxt` | Skip the homepage request if robots.txt disallows it | true |
| `maxConcurrency` | Domains in parallel (1–10) | 5 |

Switching sections off doesn't change the price. It only makes runs faster.

### Output (one item per domain)

Real output for `apify.com` (2026-10-03, shortened):

```json
{
  "input": "apify.com", "domain": "apify.com", "host": "apify.com", "status": "ok",
  "summary": {
    "registrar": "Amazon Registrar, Inc.", "createdAt": "2009-06-02T17:14:10Z", "expiresAt": "2035-06-02T17:14:10Z",
    "ageDays": 6331, "emailProvider": "Google Workspace", "hasSpf": true, "dmarcPolicy": "reject",
    "sslValid": true, "sslIssuer": "Amazon", "sslDaysUntilExpiry": 105, "securityGrade": "B"
  },
  "registration": {
    "registered": true, "registrar": "Amazon Registrar, Inc.", "registrarIanaId": "468",
    "registrarAbuseEmail": "trustandsafety@support.aws.com", "statuses": ["client transfer prohibited"],
    "nameservers": ["ns-1225.awsdns-25.org", "..."], "dnssec": true, "rdapServer": "rdap.verisign.com",
    "contactsRedacted": true, "contactRolesOmitted": []
  },
  "dns": { "mx": [{ "priority": 1, "host": "aspmx.l.google.com" }], "spf": { "all": "-all" }, "dmarc": { "policy": "reject" } },
  "ssl": { "valid": true, "issuerOrganization": "Amazon", "notAfter": "2027-01-16T23:59:59+00:00", "tlsVersion": "TLSv1.3" },
  "securityHeaders": { "score": 70, "grade": "B", "missing": ["referrer-policy", "permissions-policy", "cross-origin-opener-policy"], "notes": ["CSP allows 'unsafe-inline' scripts"], "httpsRedirect": true },
  "sectionsOk": ["registration", "dns", "ssl", "securityHeaders"],
  "error": null
}
```

- `status`: `ok` is charged. `not_registered` and `failed` are free.
- If one section fails (e.g. no HTTPS), the others still return, and `error` says which section failed and why.
- The **Overview** dataset view is a flat table with one row per domain.

### Security-header scoring (transparent, our own heuristic)

| Header | Points |
|---|---|
| Strict-Transport-Security, max-age ≥ 180 days (shorter: 12) | 25 |
| Content-Security-Policy enforced (report-only: 10) | 25 |
| X-Frame-Options DENY/SAMEORIGIN, or CSP frame-ancestors | 10 |
| X-Content-Type-Options: nosniff | 10 |
| Referrer-Policy (not unsafe-url) | 10 |
| Permissions-Policy | 10 |
| Cross-Origin-Opener-Policy | 10 |

Grades: A+ ≥ 95, A ≥ 80, B ≥ 65, C ≥ 50, D ≥ 30, F < 30. Headers are only one part of security. A good grade doesn't mean a site is secure.

### Limits (please read)

- **Some ccTLDs have no RDAP**, including **.io, .de, .co, .eu, .jp, .us and .me** (per the IANA bootstrap registry, 2026-09-30). For those domains the registration section returns an error. DNS, SSL and headers still work, and the domain is still charged because those sections answered. Filter those TLDs out first if you only need registration data.
- **No personal data, by design.** You won't get registrant names, emails, phones or addresses, even where a registry publishes them. You do get the **registrar's** public abuse contact, which is a business role contact.
- **Registry rate limits.** Each registry server gets at most 1 request per second, and one run makes at most **2,000 registration lookups**. A 2,000-domain list of `.com` names therefore takes about 35 minutes. Lookups past the cap return an error and the other sections still run. Split huge lists into several runs.
- RDAP shows the registry's view. Some registries omit expiry dates or registrar details.
- The SSL check looks at port 443 on the first reachable address only. It doesn't test every server behind a load balancer, and it isn't a full TLS configuration audit (no cipher-suite scan).
- The headers check fetches the homepage once. Bot-protected sites may answer 403. The grade then reflects that error page, and `httpStatus` shows it.
- DNS uses public resolvers at query time. Results can differ by location (GeoDNS).

### Acceptable use

Registry terms (e.g. [Verisign's RDAP terms](https://www.verisign.com/domain-names/registration-data-access-protocol/terms-service/index.xhtml)) allow lawful use only. They **forbid using the data for unsolicited marketing (spam) and high-volume automated querying.** By using this Actor you agree to follow those terms. This Actor is meant for checks, research, security and enrichment, not for building spam lists. The rate limits above are fixed for that reason.

### Responsible operation

- One homepage request per domain (plus robots.txt and an HTTP→HTTPS check), with an identifying user agent (`DomainLookupBot`). robots.txt is honored by default.
- Private and internal addresses are refused.
- Contact data, SOA mailbox (RNAME) and DMARC report addresses are dropped before output.

### Pricing

Pay per event: **$0.003 per domain analyzed** (`domain-analyzed`). Invalid, unregistered and unreachable domains are free. Your run stops cleanly at your maximum total charge.

### FAQ

#### Is RDAP the same as WHOIS?

RDAP is the official successor to WHOIS, run by the same registries. It returns the same registration facts (registrar, creation, expiry, status, nameservers) as structured JSON. Since 2025, gTLD registries are no longer required to run port-43 WHOIS, so RDAP is the reliable source.

#### How do I check domain age in bulk?

Every result has `summary.ageDays` and `summary.createdAt`. Sort the Overview table by the Created column, or the full export by `summary.ageDays`.

#### Why is registration data missing for some domains?

Some ccTLDs (.io, .de, .co, .eu, .jp, .us, .me) publish no RDAP service. See Limits.

#### Does it return the owner's name or email?

No, by design. Registrant contacts are personal data and are never returned.

### Related Actors

- [Tech Stack Detector](https://apify.com/ventura_workalong/tech-stack-detector): CMS, ecommerce platform and analytics for the same domains.
- [Sitemap URL Extractor](https://apify.com/ventura_workalong/sitemap-url-extractor) and [PageSpeed Insights Bulk Checker](https://apify.com/ventura_workalong/pagespeed-insights-bulk).

# Actor input Schema

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

Domains, hostnames or URLs, e.g. `example.com`, `shop.example.co.uk` or `https://www.example.com/page`. Registration data is looked up for the registrable domain; SSL and headers are checked on the host you give (bare domains fall back to www).

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

Registrar, creation/expiry/update dates, statuses, nameservers, DNSSEC. Registrant and other personal contacts are never returned.

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

A, AAAA, MX, NS, TXT, CAA, SOA plus SPF, DMARC policy and detected email provider.

## `includeSsl` (type: `boolean`):

Certificate validity (chain and hostname), issuer, expiry, SANs, key type, TLS version.

## `includeSecurityHeaders` (type: `boolean`):

Fetches the homepage once and grades HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy and COOP; also checks the HTTP to HTTPS redirect.

## `respectRobotsTxt` (type: `boolean`):

Skip the homepage request for the security-headers check if robots.txt disallows it. Other sections are unaffected.

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

Domains processed in parallel. Registry (RDAP) servers are always limited to 1 request per second each.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "python.org",
    "bbc.co.uk"
  ],
  "includeRegistration": true,
  "includeDns": true,
  "includeSsl": true,
  "includeSecurityHeaders": true,
  "respectRobotsTxt": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

All results from this run, one item per input, in the default dataset.

# 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",
        "python.org",
        "bbc.co.uk"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ventura_workalong/domain-lookup-bundle").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",
        "python.org",
        "bbc.co.uk",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ventura_workalong/domain-lookup-bundle").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",
    "python.org",
    "bbc.co.uk"
  ]
}' |
apify call ventura_workalong/domain-lookup-bundle --silent --output-dataset

```

## MCP server setup

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

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/D3UbzNFjc54JbqXgv/builds/wOVdloZEFb5CGgjyI/openapi.json
