# Bulk Domain Checker: WHOIS Lookup, DNS, SSL & HTTP (`aiqlabs/bulk-domain-checker`) Actor

Check a list of domains in one run: DNS records, mail provider from MX, SPF/DMARC, HTTP status and full redirect chain, SSL certificate expiry, and WHOIS/RDAP registration - plus a plain-English issue list per domain. No API keys.

- **URL**: https://apify.com/aiqlabs/bulk-domain-checker.md
- **Developed by:** [Ai-Q Labs](https://apify.com/aiqlabs) (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

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Bulk Domain Checker: DNS, HTTP Status, SSL & WHOIS

Paste a list of domains. Get back one row per domain with everything you would
otherwise collect by hand from five different tools: **DNS records**, the **mail
provider behind the MX**, **SPF and DMARC**, **HTTP status and the full redirect
chain**, **SSL certificate expiry**, and **WHOIS/RDAP registration data** — plus a
short, plain-English list of what is actually wrong with each domain.

No API keys. No account anywhere else. Nothing to configure.

### What you get per domain

| Group | Fields |
|---|---|
| Verdict | `status` (`live` / `parked` / `unreachable` / `client_error` / `server_error` / `does_not_resolve` / `not_registered`), `healthScore` (0–100), `issues[]` |
| Reachability | `httpsStatus`, `httpStatus`, `finalUrl`, `redirectCount`, `redirectChain[]`, `redirectsOffsite`, `httpsUpgrade`, `hsts`, `serverHeader` |
| DNS | `ipv4[]`, `ipv6[]`, `hasIpv6`, `nameservers[]`, `dnsProvider`, `cname` |
| Email | `acceptsEmail`, `mx[]`, `mxProvider`, `hasSpf`, `spfRecord`, `hasDmarc`, `dmarcPolicy`, `emailReady` |
| Certificate | `certIssuer`, `certValidTo`, `certDaysUntilExpiry`, `certExpired`, `certHostnameMatches`, `certTrusted`, `tlsProtocol` |
| Registration | `registered`, `registrar`, `domainCreatedAt`, `domainExpiresAt`, `domainAgeDays`, `domainStatuses[]` |

#### The issue list is the point

Raw DNS dumps are easy to get and hard to act on. Every run also returns a
punch list you can hand to someone:

```json
"issues": [
  { "severity": "high",   "code": "cert_expiring",       "message": "TLS certificate expires in 9 days." },
  { "severity": "medium", "code": "no_https_redirect",   "message": "HTTP does not redirect to HTTPS." },
  { "severity": "medium", "code": "no_dmarc",            "message": "No DMARC record." },
  { "severity": "info",   "code": "redirects_offsite",   "message": "HTTPS ends up on newbrand.com." }
]
```

Sort by `highSeverityCount` and the domains that need attention today are at the top.

### Typical uses

- **Certificate and domain expiry watch** — schedule a daily run over the domains you
  own and alert on `certDaysUntilExpiry` or `domainExpiresAt`.
- **Clean a prospect list before a campaign** — drop anything that is `parked`,
  `does_not_resolve`, or `not_registered`, and segment what is left by `mxProvider`.
- **Post-migration check** — confirm every old domain still redirects where you
  think it does, using `finalUrl` and `redirectChain`.
- **Due diligence on a domain portfolio** — `domainAgeDays`, `registrar` and
  `domainStatuses` in one table.

### Input

```json
{
  "domains": ["apify.com", "https://www.example.com/pricing", "example.org"],
  "checkHttp": true,
  "checkCertificate": true,
  "checkEmail": true,
  "checkRegistration": true,
  "maxConcurrency": 10
}
```

- `domains` accepts bare domains **or** full URLs — URLs are reduced to the domain
  automatically. A single entry may hold many domains separated by spaces, commas
  or newlines, so pasting a column out of a spreadsheet works.
- Turn individual checks off to run faster over a large list.
- `dnsServers` (optional) resolves against specific servers, e.g. `["1.1.1.1"]`.

### Notes on accuracy

Small details that are easy to get wrong, and how this Actor handles them:

- **A null MX (RFC 7505) is not a missing MX.** A domain publishing `MX .` has
  deliberately opted out of email; it is reported as `null_mx` (info), and no
  SPF/DMARC findings are raised against it.
- **Registration is looked up on the registrable domain.** A query for
  `status.example.co.uk` walks up until RDAP has an answer, so subdomains are not
  reported as unregistered.
- **DNS beats RDAP.** Anything that resolves is treated as registered, because RDAP
  legitimately returns nothing for some registries.
- **Wildcard certificates cover exactly one label.** `*.example.com` matches
  `www.example.com` but not `a.b.example.com`.
- **The certificate is read even when it is invalid.** Reporting "expired" or
  "hostname mismatch" requires completing the handshake without verification;
  nothing is ever sent over that socket, and the verification result is reported
  verbatim as `certTrusted`.

### What it does not do

- It does not fetch or parse page content — only status codes and response headers.
- It does not connect to mail servers. Whether an individual mailbox exists cannot
  be answered here; only whether the domain is set up to receive mail at all.
- It reads public DNS, public certificates and public registration data. It does
  not attempt to access anything that is not already served to any client.

### Performance

Checks for one domain run in parallel, and domains run through a fixed-size worker
pool, so memory stays flat regardless of list length. A healthy domain with every
check enabled takes well under a second; unreachable ones are bounded by short
per-check timeouts rather than hanging the run.

***

Built by [Ai-Q Labs](https://apify.com/aiqlabs).

# Actor input Schema

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

Domains to check. Full URLs are accepted and reduced to the domain (https://www.example.com/page -> example.com). One entry may also contain several domains separated by spaces, commas or newlines.

## `checkHttp` (type: `boolean`):

Request the domain over HTTPS and HTTP, record the status code and follow the full redirect chain.

## `checkCertificate` (type: `boolean`):

Read the TLS certificate on port 443: issuer, expiry date, days remaining, hostname coverage and chain trust.

## `checkEmail` (type: `boolean`):

Look up MX, SPF and DMARC records and identify the mail provider.

## `checkRegistration` (type: `boolean`):

Query RDAP - the HTTPS successor to WHOIS - for registrar, creation date, expiry date and domain status.

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

How many domains to check at the same time.

## `dnsServers` (type: `array`):

Optional. Resolve against these servers instead of the container default, for example 1.1.1.1 or 8.8.8.8.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "example.com"
  ],
  "checkHttp": true,
  "checkCertificate": true,
  "checkEmail": true,
  "checkRegistration": true,
  "maxConcurrency": 10,
  "dnsServers": []
}
```

# Actor output Schema

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

No description

## `csv` (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",
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("aiqlabs/bulk-domain-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",
        "example.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("aiqlabs/bulk-domain-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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",
    "example.com"
  ]
}' |
apify call aiqlabs/bulk-domain-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=aiqlabs/bulk-domain-checker",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/yAAL9FLPOSgSOFaCY/builds/sWKWHjbKEQobako5i/openapi.json
