# DNS Lookup & SSL Checker: SPF, DMARC, DKIM, Domain Expiry (`gmyri/domain-health-check`) Actor

Bulk DNS lookup and SSL certificate checker: A, MX, TXT, NS, CAA records, SPF/DMARC/DKIM email security, TLS and domain expiry (RDAP) with scored issues. $3 per 1,000 domains.

- **URL**: https://apify.com/gmyri/domain-health-check.md
- **Developed by:** [Giorgos Myrianthous](https://apify.com/gmyri) (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

from $3.00 / 1,000 domain checks

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

## DNS Lookup & SSL Checker: SPF, DMARC, DKIM, Domain Expiry

A **bulk DNS lookup, SSL certificate checker and SPF/DMARC/DKIM validator** in one: every domain gets DNS records, email-security checks, **SSL expiry**, HTTPS/HSTS and **domain expiry** (official RDAP), plus a 0–100 score.

💲 **$3 per 1,000 domains.** Invalid domain names are free. Pay only for results. No subscription, and the Apify free plan's monthly credit covers small jobs.

**Also known as:** DNS lookup, bulk DNS records, MX lookup, SPF checker, DMARC checker, DKIM checker, SSL certificate checker, SSL expiry monitor, domain expiry checker, WHOIS/RDAP lookup, email deliverability check.

### Quick start

1. Click **Start**. The prefilled input works as is.
2. Or paste this input (JSON tab):

```json
{
  "domains": [
    "apify.com",
    "example.com"
  ]
}
```

**Output** (one dataset item):

```json
{
  "domain": "apify.com",
  "score": 100,
  "issues": [],
  "tlsDaysToExpiry": 79,
  "domainDaysToExpiry": 3138,
  "email": {
    "spf": {
      "allQualifier": "-all",
      "dnsLookups": 10
    },
    "dmarc": {
      "policy": "reject"
    },
    "dkimSelectorsFound": [
      "google"
    ]
  }
}
```

### Use it from the API, AI agents (MCP), n8n, Make or Zapier

```bash
curl -X POST "https://api.apify.com/v2/acts/gmyri~domain-health-check/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"domains":["example.com","apify.com"],"expiryWarningDays":30}'
```

- **AI agents / MCP:** connect `https://mcp.apify.com/?actors=gmyri/domain-health-check` to Claude, Cursor, ChatGPT or any MCP client. The input schema is fully described for tool calling.
- **Python:** `ApifyClient(token).actor("gmyri/domain-health-check").call(run_input={...})`
- **n8n / Make / Zapier:** use the Apify "Run Actor" action with `gmyri/domain-health-check`, then read the dataset items.

### Quick FAQ

- **Does it return WHOIS contact details?** No. It uses official RDAP and returns only the registrar, dates, status and DNSSEC. No personal data.
- **What if I set a spending limit?** The Actor stops cleanly at your *Max cost per run* and you're never charged above it.

### Details

Check **any number of domains in one run** and get a scored health report for each:

- **DNS records:** A, AAAA, MX, NS, TXT, CAA (any type you choose)
- **Email security / deliverability:** SPF (syntax, `~all`/`-all`, **10-DNS-lookup limit**), DMARC policy and reporting, DKIM selectors, MTA-STS, BIMI
- **TLS/SSL certificate:** validity, issuer, **days to expiry**, TLS version
- **HTTPS setup:** http → https redirect, HSTS, homepage status
- **Domain registration expiry** from the registry's **official RDAP** service: registrar, created and expiry dates, status, DNSSEC

Each domain gets a **0–100 score** and a list of issues with stable codes (`NO_DMARC`, `SPF_TOO_MANY_LOOKUPS`, `TLS_EXPIRING`, `DOMAIN_EXPIRING`…), so you can alert on them.

**$3 per 1,000 domains.** Invalid domain names are free.

### Why this Actor

- **Monitoring-ready:** schedule it daily or weekly and get warned *before* a certificate or a domain expires (`expiryWarningDays`, default 30).
- **Everything in one call:** today you need a DNS lookup tool, an SPF/DMARC checker, an SSL checker and a WHOIS tool.
- **Reliable in the cloud:** DNS over HTTPS (Cloudflare with a Google fallback) by default, with classic DNS available.
- **Privacy-safe:** registration data comes from official RDAP, and **registrant contact data is never collected**.
- **Agent-friendly:** flat, predictable JSON, a single primary event, and fast results (about 1–2 s per domain).

### Typical uses

- Agencies and MSPs: monitor every client domain for certificate and domain expiry, and for email spoofing protection
- Email deliverability audits (SPF, DMARC, DKIM) before a cold or marketing campaign on **your own** domains
- IT asset inventory: registrar, DNS provider, mail provider and CAA for a portfolio
- Due diligence on supplier and partner domains
- AI agents answering "is this domain set up correctly?"

### Input example

```json
{
  "domains": ["apify.com", "example.com", "https://www.bbc.co.uk/news"],
  "checkEmailSecurity": true,
  "checkWebsite": true,
  "checkRegistration": true,
  "expiryWarningDays": 30
}
```

| Field | Default | What it does |
|---|---|---|
| `domains` | – | Domains or URLs (reduced to the domain, leading `www.` removed) |
| `checkEmailSecurity` | `true` | MX, SPF, DMARC, DKIM, MTA-STS, BIMI |
| `checkWebsite` | `true` | TLS certificate, http→https, HSTS |
| `checkRegistration` | `true` | RDAP registrar and expiry |
| `expiryWarningDays` | 30 | Warn about certificates and domains expiring sooner than this |
| `recordTypes` | A, AAAA, MX, NS, TXT, CAA | DNS records to return |
| `dkimSelectors` | common selectors | Selectors to probe (add your provider's) |
| `dnsTransport` | `doh` | `doh` (DNS over HTTPS) or `udp` |
| `dnsServers` | 1.1.1.1, 8.8.8.8 | Resolvers for `udp` |
| `timeoutSecs` | 8 | Per-check timeout |
| `maxConcurrency` | 5 | Domains in parallel |

### Output (one item per domain)

```json
{
  "domain": "apify.com",
  "score": 100, "errorCount": 0, "issueCount": 0, "issues": [],
  "tlsDaysToExpiry": 79, "domainDaysToExpiry": 3138,
  "email": {
    "mx": ["1 aspmx.l.google.com", "…"],
    "spf": { "record": "v=spf1 … -all", "allQualifier": "-all", "dnsLookups": 10 },
    "dmarc": { "record": "v=DMARC1; p=reject; …", "policy": "reject", "hasRua": true },
    "dkimSelectorsFound": ["google"], "mtaSts": false, "bimi": false
  },
  "tls": { "valid": true, "issuer": "Amazon", "expiresAt": "2027-01-16T23:59:59+00:00", "daysToExpiry": 79 },
  "web": { "httpRedirectsToHttps": true, "httpsStatus": 200, "hsts": "max-age=…" },
  "registration": { "registrar": "Amazon Registrar, Inc.", "createdAt": "…", "expiresAt": "…", "dnssec": false },
  "dns": { "A": ["…"], "NS": ["…"], "CAA": ["0 issue \"letsencrypt.org\""] }
}
```

#### Issue codes

| Severity | Codes |
|---|---|
| error | `NXDOMAIN`, `MULTIPLE_SPF`, `SPF_TOO_MANY_LOOKUPS`, `TLS_INVALID`, `TLS_EXPIRED`, `DOMAIN_EXPIRED` |
| warning | `NO_SPF`, `WEAK_SPF_ALL`, `NO_DMARC`, `TLS_EXPIRING`, `DOMAIN_EXPIRING`, `NO_HTTPS_REDIRECT`, `HTTPS_ERROR_STATUS` |
| notice | `DMARC_P_NONE`, `DKIM_NOT_FOUND`, `NO_MX`, `NO_HSTS`, `NO_CAA`, `NOT_REGISTERED` |

Score = 100 − 25 per error − 10 per warning − 3 per notice (minimum 0).

### Pricing (pay per event)

| Event | Price |
|---|---|
| Domain checked (primary) | **$0.003** ($3 / 1,000) |
| Actor start | $0.00005 per run (Apify default) |

Platform usage is included, and the Actor stops cleanly at your **Max cost per run**.

### Use from AI agents (MCP), n8n, Make, Zapier and the API

- **MCP:** add `gmyri/domain-health-check` to the [Apify MCP server](https://mcp.apify.com).
- **n8n / Make / Zapier:** run the Actor on a schedule and filter dataset items where `errorCount > 0` or `tlsDaysToExpiry < 14`.
- **API:**
  ```bash
  curl -X POST "https://api.apify.com/v2/acts/gmyri~domain-health-check/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
    -H 'Content-Type: application/json' -d '{"domains":["example.com"]}'
  ```

### FAQ

**Is DKIM detection complete?** DKIM keys can only be found if you know the selector. The Actor tries common ones (Google, Microsoft 365 and generic selectors). Add your provider's selector for a definitive answer.

**Why is registration data missing for some TLDs?** A few ccTLDs have no public RDAP service yet. Those domains still get every other check.

**Do you collect WHOIS contact details?** No. Only registrar, dates, status and DNSSEC are returned.

### Related tools

- [Broken Link Checker & SEO Audit: 404s, Redirects, Sitemap](https://apify.com/gmyri/site-health-audit)
- [Website Screenshot API: Full Page, Mobile & URL to PDF](https://apify.com/gmyri/website-screenshot)
- [Merge & Deduplicate Datasets: Remove Duplicates, Only New Items](https://apify.com/gmyri/dataset-merge-dedup)

# Actor input Schema

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

Domains to check, one per line (e.g. 'example.com'). URLs are accepted and reduced to the domain; a leading 'www.' is removed.

## `checkEmailSecurity` (type: `boolean`):

Check mail records and spoofing protection, including the SPF 10-DNS-lookup limit.

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

Connect to the domain on port 443 and 80 to check the TLS certificate and HTTPS setup.

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

Look up registrar, creation and expiry dates and DNSSEC from the registry's official RDAP service. Registrant contact data is never collected.

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

Flag TLS certificates and domain registrations that expire within this many days.

## `recordTypes` (type: `array`):

DNS record types to return for the domain (A, AAAA, MX, NS, TXT, CAA, SOA, CNAME...).

## `dkimSelectors` (type: `array`):

DKIM selectors to probe at <selector>.\_domainkey.<domain>. Add your provider's selector for an exact answer.

## `dnsTransport` (type: `string`):

How DNS lookups are made. DNS over HTTPS is the most reliable in the cloud.

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

Recursive resolvers used only with the classic DNS transport.

## `timeoutSecs` (type: `integer`):

Timeout for each DNS, TLS and HTTP check.

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

Domains checked in parallel.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "example.com"
  ],
  "checkEmailSecurity": true,
  "checkWebsite": true,
  "checkRegistration": true,
  "expiryWarningDays": 30,
  "recordTypes": [
    "A",
    "AAAA",
    "MX",
    "NS",
    "TXT",
    "CAA"
  ],
  "dkimSelectors": [
    "default",
    "google",
    "selector1",
    "selector2",
    "k1",
    "s1",
    "s2",
    "dkim",
    "mail"
  ],
  "dnsTransport": "doh",
  "dnsServers": [
    "1.1.1.1",
    "8.8.8.8"
  ],
  "timeoutSecs": 8,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `domains` (type: `string`):

One item per domain with score, issues, DNS, email security, TLS and registration details.

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

OUTPUT record: checked/invalid/withErrors counts.

# 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("gmyri/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("gmyri/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 gmyri/domain-health-check --silent --output-dataset

```

## MCP server setup

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