Domain & SSL Certificate Expiry Monitor
Pricing
from $2.00 / 1,000 domain checkeds
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
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
| Part | What | Notes |
|---|---|---|
| Domain | Registration and expiry dates | From the RDAP server of the TLD, found through the IANA bootstrap file (https://data.iana.org/rdap/dns.json). |
| Domain | Registrar | Name from the registrar vCard, or the registrar handle when the registry gives no name. |
| Domain | Status codes | For example client transfer prohibited, redemption period, pending delete. |
| Domain | Nameservers | As listed in the registry. |
| Certificate | Issuer, subject, SANs, serial, valid from, valid until, days left | Leaf certificate served on port 443 with SNI. |
| Certificate | Chain valid | True 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:
| Status | When |
|---|---|
ok | Nothing expires within warnDays and the chain is valid. |
warning | The domain or the certificate expires within warnDays days (default 30). |
critical | The domain or the certificate expires within criticalDays days (default 7), or the certificate chain is not valid. |
expired | The domain or the certificate is past its expiry date. |
error | Neither 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 code | Meaning |
|---|---|
cert_renewed | New certificate with a later expiry date. |
cert_issuer_changed | Different certificate authority. |
domain_renewed | Registration expiry moved later. |
domain_expiry_moved | Registration expiry moved earlier. |
registrar_changed | The domain moved to another registrar. |
nameservers_changed | Different nameservers. |
domain_status_codes_changed | Registry status codes changed (for example a transfer lock was removed). |
status_changed | The 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 leadingwww.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 aswww.orapi.subdomains. Each one is a separate result.warnDays(default 30) andcriticalDays(default 7): thresholds for the status.criticalDaysmust not be larger thanwarnDays.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:
- Open the Actor, fill in your domains and click Save as a new task.
- 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. - 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 exampleshop.example.com) usually returnsnot_found; put subdomains inhostnamesinstead. - Some registries do not publish an expiry date in RDAP. Then
domain_expires_atis 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
errorsand 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_validuses 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_actorpython -m unittest discover -s tests -t .
Author: Matias Isnard.