Echo Lab Tools › Domain & SSL expiry
Check domain expiry dates and SSL certificates for thousands of domains in one run
The bulk domain expiry checker is an Apify Actor that takes up to 5,000 domains or URLs and returns one JSON row per host with the domain expiration date and days left (from RDAP), the registrar, SSL certificate validity and days left, DNS records, SPF/DMARC status, and short warning codes such as domain_expires_soon.
What you get
- Domain expiration date, days left, registrar, status and nameservers via RDAP (the registry-run successor to WHOIS)
- SSL certificate check on port 443: valid or not, issuer, SANs, expiry date, days left, TLS version
- DNS records (A, AAAA, MX, NS, TXT, CAA) plus SPF and DMARC policy, with alert-ready warning codes
Price: $1.50 per 1,000 domains + $0.00005 per runLogin needed: noRuns on: Apify (API, schedule, MCP)
How it works
Paste bare domains or full URLs. The Actor lowercases them, strips scheme, path and port, converts international names to IDNA and removes duplicates, so a messy spreadsheet column works as input. Invalid lines are skipped and not billed. Then, for each unique host, it runs four checks you can switch on or off:
- RDAP for the registrable domain: registrar, registration, expiry and last-changed dates, status codes, nameservers and DNSSEC. RDAP is the structured, registry-run replacement for WHOIS, so dates come as data rather than scraped text.
- SSL on port 443 of the exact host: a verified TLS connection, then certificate dates, issuer, subject and SANs. If verification fails, it reconnects without verification only to read the dates;
sslValidstays false. - DNS over DNS-over-HTTPS (Cloudflare, with Google as fallback).
- SPF and DMARC on the apex domain.
Each row ends with a status (ok, warning or error) and a warnings list, so a scheduled run can feed Slack, email or a webhook with only the domains that need attention.
Sample output
One row from a real run on the Apify platform (run 3F0eyt2dnY0DSbwrh, build 0.1.6, 2026-11-05) with RDAP and SSL turned on:
{
"registrar": "MarkMonitor Inc.",
"registrationDate": "2007-10-09T18:20:50Z",
"expirationDate": "2028-10-09T18:20:50Z",
"lastChangedDate": "2026-09-07T09:22:52Z",
"daysUntilDomainExpiry": 703,
"domainStatus": [
"client delete prohibited",
"client transfer prohibited",
"client update prohibited"
],
"nameservers": [
"dns1.p08.nsone.net",
"dns2.p08.nsone.net",
"dns3.p08.nsone.net",
"dns4.p08.nsone.net",
"ns-1283.awsdns-32.org",
"ns-1707.awsdns-21.co.uk",
"… 2 more"
],
"dnssecSigned": false,
"rdapError": null,
"sslValid": true,
"sslError": null,
"sslIssuer": "Sectigo Limited, Sectigo Public Server Authentication CA DV E36",
"sslSubject": "github.com",
"sslSans": [
"github.com",
"www.github.com"
],
"sslNotBefore": "2026-10-30T00:00:00Z",
"sslNotAfter": "2027-01-27T23:59:59Z",
"daysUntilSslExpiry": 83,
"sslTlsVersion": "TLSv1.3",
"dns": null,
"hasSpf": null,
"spfRecord": null,
"hasDmarc": null,
"dmarcPolicy": null,
"dmarcRecord": null,
"domain": "github.com",
"inputValue": "github.com",
"checkedAt": "2026-11-05T19:07:12.921734Z",
"warnings": [],
"status": "ok"
}
The same run included expired.badssl.com, a test host with a deliberately expired certificate. It came back with sslValid: false, sslNotAfter: "2015-04-12T23:59:59Z", daysUntilSslExpiry: -4225 and warnings: ["ssl_expired","ssl_invalid"].
Expiry dates from a second real run (128Ws8GLBaRlo3XKb) across gTLDs and ccTLDs, including .io and .so, which are not in the IANA RDAP list and need the registry fallback:
| Domain | Registrar (from RDAP) | Expires | Days left on run day |
|---|---|---|---|
github.com |
MarkMonitor Inc. | 2028-10-09 | 703 |
wikipedia.org |
MarkMonitor Inc. | 2027-01-13 | 67 |
bbc.co.uk |
British Broadcasting Corporation | 2034-12-13 | 2958 |
lemonde.fr |
NAMESHIELD | 2027-06-09 | 215 |
notion.so |
1API | 2027-03-31 | 144 |
web.dev |
MarkMonitor Inc. | 2027-10-29 | 357 |
nic.io |
Registry Operator acts as Registrar (9999) | 2027-05-01 | 175 |
cira.ca |
CIRA Default Registrar | 2050-02-05 | 8492 |
And SPF/DMARC from a third run (ubddcWcK1ELriXsAO):
| Domain | SPF | DMARC | Warnings |
|---|---|---|---|
google.com |
yes | yes, p=reject |
none |
github.com |
yes | yes, p=quarantine |
none |
heise.de |
yes | yes, p=none |
dmarc_policy_none |
python.net |
no | no | no_mx no_spf no_dmarc |
Output fields
| Field | Type | Example | Meaning |
|---|---|---|---|
domain, inputValue |
string | github.com |
Normalized host and the first original input |
registrar |
string / null | MarkMonitor Inc. |
Registrar company only, never contact details |
expirationDate, daysUntilDomainExpiry |
string / integer | 2028-10-09…, 703 |
RDAP expiry and whole days left |
domainStatus, nameservers, dnssecSigned |
array / boolean | client transfer prohibited |
Registry status and delegation |
rdapError |
string / null | no_rdap_server |
Why registration data is missing |
sslValid, sslError |
boolean / string | true |
Chain and hostname verified; null if the connection failed |
sslIssuer, sslSans, sslNotAfter, daysUntilSslExpiry, sslTlsVersion |
mixed | TLSv1.3 |
Certificate details |
dns |
object | {"MX": [...]} |
Record type → answers |
hasSpf, hasDmarc, dmarcPolicy |
boolean / string | reject |
Email authentication records |
warnings, status |
array / string | ["ssl_expired"] |
Alert codes and overall status |
How much does a bulk domain check cost?
Pricing is per result: $1.50 per 1,000 domains ($0.0015 per unique host) plus $0.00005 per run, with Apify platform usage included. Duplicates and invalid lines are not billed.
Worked examples: an agency checking 50 client domains every day pays 50 × $0.0015 ≈ $0.075 per run, about $2.25 per month. A one-off audit of 1,000 domains is $1.50; the maximum batch of 5,000 is $7.50. Apify's free plan includes $5 a month for Store tools (apify.com/pricing, checked 2026-11-06), enough for about 3,300 checks.
Limits you should know about
- Some ccTLDs have no public RDAP. In November 2026 we found none for .de, .jp, .cn, .ru, .eu, .ch, .it, .es, .se, .dk, .at, .be, .us or .edu. Those rows get
rdapError: no_rdap_serverandstatus: error; SSL, DNS and email checks still run. There is no WHOIS fallback. - Some registries answer without an expiry date (for example .nl, .au, .pl). You get the
domain_expiry_unknownwarning instead of a silent "ok". - RDAP expiry is the registry date. It can differ from the renewal date your registrar shows, for example around auto-renewal and grace periods.
- SSL is checked on port 443 of the host you give. Other ports and other hostnames on the same server are not scanned; a network failure leaves
sslValidnull. - SPF/DMARC checks detect records and the DMARC policy. They do not expand SPF includes, count DNS lookups or test real mail delivery.
API: call it from code
curl -X POST "https://api.apify.com/v2/acts/echolab~domain-health-monitor/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domains": ["example.com", "https://www.example.org/page"], "expiryWarningDays": 30}'
Python, keeping only the rows that need attention:
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
domains = [line.strip() for line in open("domains.txt") if line.strip()]
run = client.actor("echolab/domain-health-monitor").call(run_input={
"domains": domains, # up to 5,000
"expiryWarningDays": 30,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["warnings"]:
print(row["domain"], row["daysUntilDomainExpiry"], row["daysUntilSslExpiry"], row["warnings"])
Bulk domain checker vs. alternatives
| Option | Good for | Trade-off |
|---|---|---|
| This Actor | One table for expiry, SSL, DNS and SPF/DMARC across hundreds or thousands of domains; daily schedules | No WHOIS fallback for ccTLDs without RDAP; not an uptime monitor |
Your own whois + openssl s_client script |
Free, a handful of domains | WHOIS text differs per registry and is rate-limited; you parse and maintain it |
| Uptime/SSL monitoring services | Minute-level uptime checks and alert routing | Usually priced per monitor; registration expiry is often missing or a paid extra |
| Commercial WHOIS APIs | Historical WHOIS, ownership research, wider ccTLD coverage | Per-query or subscription pricing; SSL/DNS/email checks are separate products |
When not to use it: if most of your domains are .de or .jp, registration expiry will be missing, so use your registrar's export instead. If you need to know within a minute that a site is down, use an uptime monitor. This tool is for a scheduled renewal-and-certificate audit of many domains in one dataset.
FAQ
Is this a WHOIS lookup? It uses RDAP, the registry-run successor to WHOIS. Registrant contact details are never output, only the registrar company.
Can it alert me before something expires? Schedule it daily and filter rows whose warnings contain domain_expires_soon, ssl_expires_soon, domain_expired or ssl_expired; expiryWarningDays sets the window.
Open the bulk domain checker on Apify for the full input schema.
Changelog
- 2026-11-06 — build 0.1.7: registry RDAP fallback for .io, .co, .me, .so, .sh, .ac, .ag, .bz, .lc, .mn, .vc and .gi (missing from the IANA list); new
domain_expiry_unknownwarning for registries that publish no expiry date; unregistered domains now reportdomain_not_found. - 2026-11-04 — switched to pay-per-result pricing ($1.50 per 1,000 domains).
- 2026-11-04 — first public release on the Apify Store.