Domain & SSL Certificate Expiry Monitor avatar

Domain & SSL Certificate Expiry Monitor

Pricing

from $2.00 / 1,000 domain checkeds

Go to Apify Store
Domain & SSL Certificate Expiry Monitor

Domain & SSL Certificate Expiry Monitor

Check domain registration expiry (RDAP) and TLS certificate expiry for a list of domains. Status ok, warning, critical or expired, plus what changed since the last run. Built for daily schedules.

Pricing

from $2.00 / 1,000 domain checkeds

Rating

0.0

(0)

Developer

Matias Isnard

Matias Isnard

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Give it a list of domains. For each one you get the domain registration expiry date (from RDAP, the official successor of WHOIS), the registrar, the registry status codes and nameservers, and the TLS certificate served on port 443: issuer, subject, SANs, expiry date, days left and whether the chain is valid.

Each result gets a status (ok, warning, critical, expired or error) and a list of what changed since the previous run: certificate renewed, registrar changed, expiry date moved, nameservers changed. Run it on a daily schedule and send yourself an email or a Slack message.

Who it is for

  • Agencies, MSPs and freelancers who manage domains and websites for clients.
  • IT and ops teams who do not want a forgotten domain renewal or an expired certificate to take a site down.
  • Anyone with domains spread over several registrars.

What it checks

PartWhatNotes
DomainRegistration and expiry datesFrom the RDAP server of the TLD, found through the IANA bootstrap file (https://data.iana.org/rdap/dns.json).
DomainRegistrarName from the registrar vCard, or the registrar handle when the registry gives no name.
DomainStatus codesFor example client transfer prohibited, redemption period, pending delete.
DomainNameserversAs listed in the registry.
CertificateIssuer, subject, SANs, serial, valid from, valid until, days leftLeaf certificate served on port 443 with SNI.
CertificateChain validTrue when the chain is trusted by the standard CA bundle and the certificate matches the hostname. When it is not valid, the certificate is still read so you see its dates, and chain_error says why.

Some TLDs have no RDAP server listed with IANA (for example .io and .de at the time of writing). For those the result has rdap_status: "rdap_unavailable" and only the certificate is checked, so the status is based on the certificate alone.

Status

The status is the worst of the domain expiry, the certificate expiry and the chain check:

StatusWhen
okNothing expires within warnDays and the chain is valid.
warningThe domain or the certificate expires within warnDays days (default 30).
criticalThe domain or the certificate expires within criticalDays days (default 7), or the certificate chain is not valid.
expiredThe domain or the certificate is past its expiry date.
errorNeither the RDAP lookup nor the TLS connection returned data.

status_reasons explains the status in plain words, for example Certificate expires in 5 days.

When one part fails and the other works (for example the domain has no website on port 443), the status is based on the part that worked and the problem is listed in errors.

Change detection

After each check the main values are saved in a named key-value store called domain-ssl-expiry-monitor-state in your account. On the next run they are compared:

Change codeMeaning
cert_renewedNew certificate with a later expiry date.
cert_issuer_changedDifferent certificate authority.
domain_renewedRegistration expiry moved later.
domain_expiry_movedRegistration expiry moved earlier.
registrar_changedThe domain moved to another registrar.
nameservers_changedDifferent nameservers.
domain_status_codes_changedRegistry status codes changed (for example a transfer lock was removed).
status_changedThe status itself changed, for example from ok to warning.

changed is true when there is at least one change. change_details has the previous and current value of each one. first_check is true when there was no stored result yet. If a lookup fails in one run, its values are not reported as changed and the old values are kept for the next comparison.

The store is shared by all runs and tasks of this Actor in your account. To start over, delete it in Storage > Key-value stores.

Input

{
"domains": ["google.com", "github.com", "mercadolibre.com.ar"],
"hostnames": ["www.github.com", "api.github.com"],
"warnDays": 30,
"criticalDays": 7,
"timeoutSecs": 20
}
  • domains: registered domains (the name you buy from a registrar). URLs are reduced to the hostname and a leading www. is removed. Each domain gets an RDAP lookup and a certificate check on the domain itself.
  • hostnames (optional): extra hostnames for a certificate check only, such as www. or api. subdomains. Each one is a separate result.
  • warnDays (default 30) and criticalDays (default 7): thresholds for the status. criticalDays must not be larger than warnDays.
  • timeoutSecs (default 20): time limit for each RDAP request and each TLS connection. RDAP requests are retried once after a timeout or a server error.

Invalid lines are skipped with a warning in the log and are not charged. Duplicates are checked once.

Output

One dataset item per domain or hostname. Example (nameservers shortened):

{
"input": "github.com",
"host": "github.com",
"type": "domain",
"checked_at": "2026-10-29T09:26:07Z",
"status": "ok",
"status_reasons": [],
"rdap_status": "ok",
"rdap_server": "https://rdap.verisign.com/com/v1/",
"registrar": "MarkMonitor Inc.",
"domain_created_at": "2007-10-09T18:20:50Z",
"domain_expires_at": "2028-10-09T18:20:50Z",
"domain_days_left": 711,
"domain_status_codes": ["client delete prohibited", "client transfer prohibited", "client update prohibited"],
"nameservers": ["dns1.p08.nsone.net", "ns-1283.awsdns-32.org"],
"tls_status": "ok",
"tls_version": "TLSv1.3",
"chain_valid": true,
"chain_error": null,
"cert_issuer": "Sectigo Limited",
"cert_issuer_dn": "CN=Sectigo Public Server Authentication CA DV E36,O=Sectigo Limited,C=GB",
"cert_subject": "github.com",
"cert_sans": ["github.com", "www.github.com"],
"cert_serial": "a59ebdb596751db7f5c095079613953c",
"cert_not_before": "2026-09-01T00:00:00Z",
"cert_not_after": "2026-11-29T23:59:59Z",
"cert_days_left": 31,
"first_check": false,
"previous_checked_at": "2026-10-28T09:25:47Z",
"changed": false,
"changes": [],
"change_details": [],
"errors": []
}

All dates are UTC in ISO 8601. Days left are whole days rounded down, so a certificate that expired an hour ago shows -1.

Scheduling and alerts

The Actor is meant to run on a schedule. A daily run with a notification:

  1. Open the Actor, fill in your domains and click Save as a new task.
  2. Go to Schedules > Create new, select the task and set a cron expression, for example 0 7 * * * (every day at 07:00). Choose your time zone.
  3. In the task, open the Integrations tab:
    • Email: add Send email on the event Run succeeded (and one on Run failed). The email links to the run and its dataset.
    • Slack: add the Slack integration, connect your workspace and pick a channel.

If you only want a message when something needs attention, add a webhook integration (or connect Make, Zapier or n8n) on Run succeeded. In that flow, read the run's dataset and send a message only for items where status is not ok or changed is true.

Starting a run from your own scheduler through the API:

curl -X POST "https://api.apify.com/v2/acts/<username>~domain-ssl-expiry-monitor/runs?token=<API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"domains": ["example.com", "example.org"], "warnDays": 30, "criticalDays": 7}'

Pricing

Pay per event: you pay once per domain or hostname checked (domain-checked event). You are only charged when at least one of the two lookups (RDAP or TLS) returned data. Names that do not exist, and names where both lookups failed, are not charged. The price is shown on the Actor page. You can cap the cost of a run with the "Maximum cost per run" setting; the Actor stops cleanly when the cap is reached.

Limitations

  • Only registered domains get an RDAP lookup. A subdomain in domains (for example shop.example.com) usually returns not_found; put subdomains in hostnames instead.
  • Some registries do not publish an expiry date in RDAP. Then domain_expires_at is null and the status is based on the certificate.
  • Some RDAP servers are slow or rate-limit requests. A failed RDAP lookup is reported in errors and the certificate is still checked.
  • Only port 443 is checked, and only the dates of the leaf certificate are reported. An expired intermediate certificate shows up as chain_valid: false.
  • chain_valid uses the standard CA bundle. Certificates from private CAs show as not valid.
  • Results are a snapshot at the time of the run. A renewal at the registrar can take some time to appear in the registry's RDAP data.

Run locally

python -m venv .venv
.venv/Scripts/pip install -r requirements.txt # on Linux/macOS: .venv/bin/pip
# put input in storage/key_value_stores/default/INPUT.json
.venv/Scripts/python -m my_actor
python -m unittest discover -s tests -t .

Author: Matias Isnard.