# Domain & Website Health Check: Expiry, SSL, DNS, SPF/DMARC (`eddie-power-dev/domain-health-check`) Actor

Bulk-check domains in one run: registration and expiry date (RDAP), registrar, DNSSEC, DNS records, SSL certificate expiry and validity, HTTP to HTTPS redirect, HSTS, SPF and DMARC. A plain-English issue list and score for each domain. Cheap per-domain pricing.

- **URL**: https://apify.com/eddie-power-dev/domain-health-check.md
- **Developed by:** [Eddie Power](https://apify.com/eddie-power-dev) (community)
- **Categories:** SEO tools, Developer tools
- **Security:** Limited permissions
- **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

## Domain & Website Health Check: Expiry, SSL, DNS, SPF/DMARC

**Check hundreds of domains in one run and get a plain-English list of what's wrong with each.** For every domain you get the **registration and expiry date**, registrar and DNSSEC (from the registry's official RDAP service), **SSL certificate expiry and validity**, whether **HTTP redirects to HTTPS**, HSTS, DNS records, and **email security (SPF and DMARC)**, plus a 0–100 health score.

Great for:

- 🛠️ **Web agencies and freelancers:** audit every client site at once; catch expiring domains and certificates before the client does
- 📧 **Email deliverability:** find domains without SPF/DMARC, or with `p=none` / `+all`
- 🔎 **Lead generation and sales:** find businesses with broken HTTPS or expired certificates who need help
- 🗂️ **Domain portfolios:** expiry dates, registrars and statuses in one sheet; spot names that look unregistered
- ⏰ **Monitoring:** schedule weekly and alert when `issueCount` goes up

### Input

| Field | What to put in | Default |
|---|---|---|
| `domains` | Domains, URLs or email addresses, one per line (`example.com.au`, `https://shop.example.com`, `info@example.com`) | – |
| `checkRegistration` | RDAP registration, expiry, registrar, DNSSEC | on |
| `checkDns` | A/AAAA, MX, CAA, SPF, DMARC | on |
| `checkSsl` | Certificate issuer, expiry, trust, host coverage | on |
| `checkWebsite` | `http://` → final URL, redirects, status, HTTPS redirect, HSTS | on |
| `expiryWarningDays` | Flag domains expiring within N days | 30 |

### Output

One row per domain. Highlights: `score`, `issues`, `registered`, `registrar`, `expiresAt`, `daysToExpiry`, `dnssec`, `mxRecords`, `spfRecord`, `spfAll`, `dmarcPolicy`, `sslValid`, `sslIssuer`, `sslDaysLeft`, `sslError`, `httpStatus`, `finalUrl`, `redirectChain`, `httpsRedirect`, `hsts`.

```json
{
  "host": "example.com.au", "score": 76,
  "issues": ["DMARC policy is p=none (monitoring only, no protection)", "No HSTS header"],
  "registered": true, "registrar": "Example Registrar Pty Ltd", "expiresAt": null,
  "spfAll": "~all", "dmarcPolicy": "none",
  "sslValid": true, "sslIssuer": "Let's Encrypt / R11", "sslDaysLeft": 61,
  "httpsRedirect": true, "finalUrl": "https://www.example.com.au/"
}
```

### Pricing

Pay per result: **one charge per domain checked**, whatever checks you turn on. Invalid inputs and duplicates are skipped for free.

### Notes

- **Registration data comes from RDAP**, the public protocol ICANN requires of gTLD registries. Some country registries don't run RDAP (e.g. `.io`, `.de`, `.co`), or don't publish expiry dates (e.g. `.au`). Then `rdapError` explains why, and registration is inferred from DNS.
- **"Not registered" is a hint, not a guarantee** of availability. Premium and reserved names can still be unavailable.
- DNS is queried over HTTPS from Cloudflare (1.1.1.1) with Google Public DNS as a fallback.
- The website check loads only the home page (headers only, body discarded). It's not a crawler and puts no load on the site.
- Only public domain names are accepted. IP addresses and internal names (`localhost`, `.local`, `.internal`) are refused.

Built by [Eddie Power](https://apify.com/eddie-power-dev), a Melbourne web developer. Need help fixing what this finds? Get in touch through the Apify profile.

# Actor input Schema

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

Domains, website URLs or email addresses, one per line (e.g. example.com.au, https://shop.example.com, info@example.com). Up to 10,000.

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

Registrar, created/expiry dates, status, DNSSEC and whether the domain looks unregistered, from the registry's public RDAP service.

## `checkDns` (type: `boolean`):

A/AAAA, MX, CAA, SPF and DMARC records.

## `checkSsl` (type: `boolean`):

Certificate issuer, expiry, trust and whether it covers the host.

## `checkWebsite` (type: `boolean`):

Loads http://<domain>/ and records redirects, final URL, status, HTTPS redirect and HSTS.

## `expiryWarningDays` (type: `integer`):

Flag domains expiring within this many days (SSL warnings use the smaller of this and 14).

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

How many domains are checked at once.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "example.com"
  ],
  "checkRegistration": true,
  "checkDns": true,
  "checkSsl": true,
  "checkWebsite": true,
  "expiryWarningDays": 30,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `report` (type: `string`):

One row per domain with registration/expiry, DNS, SSL, website and email-security results, an issue list and a score.

## `summary` (type: `string`):

Counts and per-input status for the run (JSON record SUMMARY in the default key-value store).

# 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("eddie-power-dev/domain-health-check").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("eddie-power-dev/domain-health-check").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",
    "example.com"
  ]
}' |
apify call eddie-power-dev/domain-health-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,eddie-power-dev/domain-health-check"
        }
    }
}
```

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/HsGd6NiaD4YphANkh/builds/0zRe6WVFsDvAQuEfK/openapi.json
