SSL & Domain Expiry Monitor - Certificate + RDAP Checks avatar

SSL & Domain Expiry Monitor - Certificate + RDAP Checks

Pricing

from $18.50 / 1,000 per alert delivereds

Go to Apify Store
SSL & Domain Expiry Monitor - Certificate + RDAP Checks

SSL & Domain Expiry Monitor - Certificate + RDAP Checks

Certificate expiry and domain-registration expiry for a list of hosts, one row each, with a plain list of what is wrong. A real TLS handshake and RDAP - no third-party checking API, no rate limit.

Pricing

from $18.50 / 1,000 per alert delivereds

Rating

0.0

(0)

Developer

Lowland Data

Lowland Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Share

Checks a list of hosts and answers the two questions that cause the same outage: when does the TLS certificate expire, and when does the domain registration lapse? One row per host, with the countdown in days, the issuer, the registrar, and a plain list of what is wrong. Point it at 5 domains or 500.

Live health record: lowlanddata.com/status/ssl-domain-monitor - 30-day success rate and daily canary results, failures included. Full input/output reference: tool page.

The certificate comes from a real TLS handshake with the host - not a third-party checking API with a rate limit, so what you read is what the server actually serves. The registration data comes from RDAP, the structured protocol that replaced WHOIS text. No API key, no login, no scraping.

Quick start (30 seconds, no keys needed)

  1. Hit Start with the prefilled domains. One of them, expired.badssl.com, is expired on purpose - so your first run shows you both a healthy row and a failing one.
  2. Replace them with your own. A hostname, a full URL, or host:8443 all work; paste them one per line.
  3. Add a daily schedule (Actor page -> Schedule) and set alertsOnly: true. From then on the runs are silent until something needs you.

Who uses this

  • Agencies and freelancers holding 40 client domains run one daily audit instead of 40 browser padlock checks.
  • Platform teams watch internal endpoints on non-standard ports (api.internal.example.com:8443) that no public uptime service will look at.
  • Ops on call wire alertsOnly: true to Slack, so the only message that ever arrives is one that needs an action.
  • Domain portfolio owners care less about the certificate than about daysToDomainExpiry - a lapsed registration is unrecoverable in a way an expired certificate is not.
  • Anyone who has had a Sunday ruined by a certificate that quietly ran out.

What you get

One JSON item per host. Three real shapes, because a monitor is judged on the rows that are not healthy.

A healthy host:

{
"host": "lowlanddata.com",
"port": 443,
"domain": "lowlanddata.com",
"reachable": true,
"certificateIssuer": "Let's Encrypt",
"certificateValidTo": "2026-12-04T09:41:00.000Z",
"daysToCertificateExpiry": 87,
"tlsVersion": "TLSv1.3",
"hostnameMatches": true,
"domainExpiresAt": "2027-09-05T12:00:00.000Z",
"daysToDomainExpiry": 362,
"registrar": "OVH sas",
"nameservers": ["dns14.ovh.net", "ns14.ovh.net"],
"problems": [],
"isAlert": false,
"checkedAt": "2026-09-08T20:41:12.000Z"
}

One that has already failed - the certificate expired, and the row says so in a field you can filter on:

{
"host": "expired.badssl.com",
"reachable": true,
"certificateIssuer": "COMODO CA Limited",
"certificateValidTo": "2015-04-12T23:59:59.000Z",
"daysToCertificateExpiry": -4167,
"hostnameMatches": true,
"problems": ["certificate-expired"],
"isAlert": true,
"isNewAlert": true,
"checkedAt": "2026-09-08T20:41:12.000Z"
}

And one that could not be reached at all, which is a finding rather than an error:

{
"host": "does-not-exist-xyz123.com",
"reachable": false,
"error": "getaddrinfo ENOTFOUND does-not-exist-xyz123.com",
"problems": ["tls-handshake-failed"],
"isAlert": true,
"isNewAlert": true
}

What the problems mean

ValueWhat happened
certificate-expiredvalidTo is in the past. Browsers are already refusing the site.
hostname-mismatchThe certificate does not cover the hostname you asked for - a common cause of a scary browser warning on a working server.
self-signedIssuer and subject are the same. Fine inside a lab, not in public.
not-yet-validvalidFrom is in the future, usually a clock or an early deploy.
obsolete-tls-versionThe handshake settled on TLS 1.0/1.1, which modern clients are dropping.
tls-handshake-failedDNS did not resolve, the port refused, or the handshake timed out. See error.

An empty problems array with isAlert: true means nothing is broken yet - a countdown simply crossed one of your thresholds. certificateThresholdDays or domainThresholdDays tells you which one.

How change detection works

Every monitor on this store is vague about this, so here it is in full:

  • Where the memory lives. A named key-value store on your account (stateStoreName). It holds one fingerprint per host: the problems found, plus which expiry threshold has been crossed. There is no server on my side holding your domain list.
  • What an alert is. A host whose fingerprint changed since the last run: a problem appeared, a problem cleared, or a countdown fell past the next threshold (30 -> 14 -> 7 days by default). A certificate that has been expiring for a week does not re-alert every day.
  • alertsOnly: false (the default) returns the full table for every host - what you want for an audit or a dashboard refresh.
  • alertsOnly: true returns only hosts with a new alert - what you want on a schedule. When nothing changed the run finishes empty, and the status line still tells you the truth: 40 hosts checked, 2 needing attention (0 new since the last run).
  • A run cannot cry wolf. If every host in a list of three or more fails its handshake at once, the run fails loudly instead of reporting your whole estate as down - that pattern means the run's own network broke, not your domains.
  • Starting over. resetState: true forgets the fingerprints so everything alerts again.

What monitoring 40 domains costs

Pay per outcome, so a quiet day is nearly free.

EventPriceWhen it is charged
Expiry alert$0.0199 (from $0.0185 on paid plans)A host with a problem or a crossed threshold
Domain checked$0.00499A healthy row in a full audit (alertsOnly: false)
Actor start$0.005Per run, whatever it finds

Live prices are always the ones on this page's Pricing tab. A worked example: 40 domains, checked daily with alertsOnly: true. A quiet day costs the actor start alone - about 15 cents a month - and you pay about 2 cents only when a domain genuinely needs attention. A full monthly audit of all 40 costs about 21 cents. New Apify accounts come with free monthly platform credits.

Input reference

FieldTypeDefaultWhat it does
domainsstring[]three samplesHostnames, URLs or host:port, one per line
alertThresholdDaysnumber[][30, 14, 7]Countdown points that raise an alert
alertsOnlybooleanfalseReport only hosts with a new alert - the setting for a schedule
checkDomainExpirybooleantrueAdd the RDAP lookup: registration expiry, registrar, statuses, nameservers
timeoutSecondsinteger15How long to wait for a handshake before recording the host as unreachable
concurrencyinteger8Hosts checked in parallel
resetStatebooleanfalseForget the fingerprints and alert on everything again
stateStoreNamestringssl-monitor-stateWhich named store holds this monitor's memory
maxItemsinteger1000Stop after this many rows

The list is also accepted under urls, hosts, websites or startUrls, and a single comma-separated string works as well as a list - so an AI agent's first guess at the input tends to run.

Scheduling and API use

curl -X POST "https://api.apify.com/v2/acts/lowlanddata~ssl-domain-monitor/runs?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"domains":["example.com","shop.example.com","api.example.com:8443"],"alertsOnly":true}'

Pull just the hosts that need action:

$curl "https://api.apify.com/v2/datasets/DATASET_ID/items?format=json&filter=isAlert"

In n8n or Make: Schedule -> this actor with alertsOnly: true -> Slack. Because a quiet run returns zero items, the message only fires when there is something to say.

Use it with AI agents (MCP)

Every tool here doubles as an MCP endpoint: ssl-domain-monitor/api/mcp. A prompt that works as-is:

Check the certificates and domain registrations for example.com, example.org and
shop.example.com, and list anything expiring within 45 days.

Yes. A TLS handshake is what every browser does before rendering a page, and RDAP is a public protocol run by the registries themselves for exactly this purpose. Nothing is authenticated to, no page is fetched, and one host means one handshake plus one lookup.

RDAP records can contain registrant contact blocks - names, emails, phone numbers - and in the EU the registries already redact most of them. They are not read here. The parser takes registration dates, statuses, nameservers and the registrar's company name; a test in the build feeds it a record stuffed with fake registrant contacts and fails if any of it reaches the output. Certificate fields are the issuing CA's organisation and the hostnames the certificate covers - company and machine facts, not people. So the rows are safe to store, forward and archive.

Honest coverage notes

  • Some registries publish no expiry date over RDAP. Several ccTLDs (.be and .nl among them) omit it by policy. The certificate half is unaffected; domainExpiresAt is simply absent rather than guessed.
  • The registrable domain is derived from a built-in suffix list covering the common multi-label endings (.co.uk, .com.au and friends). An unusual public suffix may resolve to the wrong registry answer - tell me and it gets added.
  • Only the leaf certificate is read, not the full chain. An incomplete-chain misconfiguration that browsers repair silently will not be flagged.
  • The handshake is direct, never proxied, so the certificate you see is the one the host serves you. Only the RDAP lookups go through the proxy.

FAQ

Is this an uptime monitor? No. It answers "will this break on a known date", not "is it up right now". The two belong on different schedules: uptime by the minute, expiry once a day.

What about a certificate that auto-renews? Let's Encrypt renewal at 30 days is normal and quiet. The value is catching the renewal that did not happen - the row keeps counting down and crosses 14, then 7.

Can I check internal hosts? Any host this actor can reach on the public internet, on any port. Hosts behind a VPN are not reachable.

Why did a domain report no expiry date? Its registry does not publish one over RDAP. See the coverage notes.

Does an unchanged problem keep charging me? In alertsOnly: true, no - it is not pushed again until it changes or clears.

How many domains can one run handle? 500 per run, 8 at a time by default; raise concurrency for a long list.

More tools from Lowland Data

Same house rules everywhere: clean schemas, public sources, no personal data in the output.

ToolWhat it does
Dependency Release MonitorNew npm, PyPI and GitHub releases, with breaking-change flags
US Federal Grants MonitorNew federal funding opportunities matching your profile
Company Jobs ScraperEvery open role from any Greenhouse or Lever board

Full catalogue: lowlanddata.com/catalogue.

Support

A domain reported wrong, a registry that needs handling, a port that misbehaves? Open an issue on the Issues tab with the run ID, or write to contact@lowlanddata.com. Reports get read the same day.