How it will work: 3 steps
- Follow us on ApifyThe tool is built and waits in the queue for the Apify Store. Apify is the cloud platform where our tools run.
- Paste your input and press StartNo code needed: you fill in a form (the input). It already has a working example. For example:
apify.comin Domains. Each start is one run. - Download the resultsWhen the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.
This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.
What it does
Check the SSL/TLS certificate of many websites in one run. For each host you get a clear verdict (valid, expiring soon, expired, wrong name or untrusted), the days left, issuer, all names on the certificate (SANs), TLS version and the certificate chain. Optional: certificates logged for the host in Certificate Transparency (crt.sh). No login and no API key.
- Trust comes from the standard list of trusted root certificates (the same check that makes a browser show a warning).
- Name match checks that the host name is on the certificate.
- Dates are compared with the time of the check.
daysLeftis negative for an expired certificate. - CT history (optional) comes from crt.sh, a free public search over Certificate Transparency logs.
Good for
- Find certificates that expire soon across many client sites (
daysLeft,certStatus: expiring_soon). - Find broken HTTPS: expired, self-signed, wrong host name, missing intermediate (
certStatus,validationError). - Make an inventory of who issued your certificates and which names they cover (
issuer,sanList). - Check mail and admin ports over TLS, for example
mail.example.com:993or:8443. - See which certificates were issued for a name in the past (CT history).
Not a good fit
- No revocation check. The Actor does not ask OCSP or CRL servers. A revoked certificate can still show
valid. - No protocol or cipher scan. You get the TLS version and cipher that the server chose. The Actor does not test every TLS version or cipher, and it does not give a security score.
- No STARTTLS. Ports that start in plain text (SMTP on 25/587, IMAP on 143) are not supported. Use the TLS ports (465, 993, 995).
- Public hosts only. Private, loopback, link-local and reserved addresses (for example
10.0.0.1,127.0.0.1,169.254.169.254) are not checked. - CT history is slow. crt.sh is a free community service. It answers in 5–20 seconds per host and allows about 5 requests a minute, so CT history is meant for short lists.
Who uses it
Website owners
Find certificates that expire soon before a browser warning appears.
IT admins
Make a list of who issued your certificates and which names they cover.
Web agencies
Watch certificates on many brands in one run.
Price
Prices will be shown after release.
Questions
Do I need an account or an API key?
No. It connects to each host directly. CT history uses the public crt.sh service.
Why does a revoked certificate show valid?
It does not check revocation lists. It checks trust, name and dates only.
Why is daysLeft null for some rows?
No certificate was read. The host does not exist or did not answer.
Does it work with SMTP or IMAP on plain ports?
No. STARTTLS is not supported. Use TLS ports like 465, 993 or 995.
Is CT history complete?
It is what crt.sh returns at run time. crt.sh covers the main public CT logs, but it can be slow, can time out, and may not show the newest certificates yet.
I used another SSL checker. Are the field names the same?
Not exactly. The meaning maps like this: expiry days → daysLeft; expired → certStatus: expired; self-signed → selfSigned; chain valid → trusted; host name matches → hostnameMatch; TLS version → protocol (Node naming, for example TLSv1.3); issued by → issuer. Failed checks here give null, not 0 or empty strings.
Details
Step by step on Apify
- Open the Input tab.
- Put your hosts in Domains (
domains), one per line. The field is prefilled withapify.comandexpired.badssl.com. - Keep the defaults: port 443, chain on, CT history off, warning at 30 days.
- Click Start.
- Open the Output tab. The Dataset has four views:
- Certificates — verdict, days left, expiry date, issuer;
- Certificate details — SANs, key, fingerprint, cipher;
- Chain — the intermediate and root certificates;
Use cases
| Task | Input | Key fields | Next step |
|---|---|---|---|
| Expiry watch for client sites | domains, warnDays | certStatus, daysLeft, validTo | Sort by daysLeft; run on a schedule |
| Find broken HTTPS | domains | certStatus, trusted, hostnameMatch, validationError | Filter rows where valid is false |
| Certificate inventory | domains, includeChain | issuer, issuerOrganization, sanList, keyType | Export to CSV or Excel |
| Mail and admin ports | domains with host:port, or port | certStatus, protocol, daysLeft | Same as above |
| Issuance history of a name | includeCtHistory: true | ctCertificatesFound, ctHistory | Look for unknown issuers |
Tips for better results
- Run it every week and look for expiring_soon in certStatus.
- Add host:port values to check mail ports like mail.example.com:993.
- Turn on CT history to see old certificates for a name.
Limits and good to know
- Up to 5,000 values per run (counted before duplicates are removed). For more, split the list
- Measured on 256 MB: 500 hosts in about 1 minute, 5,000 hosts in about 7 minutes
- A host that does not answer takes up to
connectTimeoutSecs(default 10 s) - CT history adds about 13 seconds per host (50 hosts: about 11 minutes)
- If more than 250 hosts cannot be reached and they are more than 10 times the certificates read, the run stops early with
TOO_MANY_FAILURES. This protects you from paying for a wrong list. Names missing from DNS do not count here: they are a normalnot_foundresult - The default run timeout is 1 hour. When the time runs out, the Actor saves what it has and marks the run
PARTIALwithDEADLINE
For developers
Sample result
One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.
{
"input": "apify.com",
"domain": "apify.com",
"port": 443,
"ipAddress": "3.170.42.100",
"checkStatus": "ok",
"certStatus": "valid",
"valid": true,
"trusted": true,
"hostnameMatch": true,
"expiringSoon": false,
"daysLeft": 91,
"validFrom": "2026-07-03T00:00:00.000Z",
"validTo": "2027-01-16T23:59:59.000Z",
"validationError": null,
"issuer": "Amazon RSA 2048 M04",
"issuerOrganization": "Amazon",
"subject": "*.apify.com",
"sanList": ["*.apify.com", "apifier.com", "*.apifier.com", "apify.com"],
"sanCount": 4,
"keyType": "RSA",
"keyBits": 2048,
"selfSigned": false,
"protocol": "TLSv1.3",
"cipher": "TLS_AES_128_GCM_SHA256",
"legacyTlsOnly": false,
"chain": [
{
"subject": "*.apify.com",
"issuer": "Amazon RSA 2048 M04",
"validTo": "2027-01-16T23:59:59.000Z",
"daysLeft": 91,
"isCa": false
}
],
"chainLength": 4,
"ctStatus": "skipped",
"ctHistory": null,
"error": null,
"errorCode": null,
"checkedAt": "2026-10-17T09:19:05.357Z",
"source": "Direct TLS handshake"
}Data fields (39)
| Field | Type | Meaning |
|---|---|---|
input | string | The value as given in the input. |
domain | string | Host that was checked (lowercase ASCII/punycode) or IP address. |
port | integer | TCP port of the TLS connection. |
ipAddress | string | Public IP address the connection was made to (IPv4 preferred). |
checkStatus | string | ok = a certificate was read (host check + certificate charged); not_found = the host name does not resolve in DNS (host check charged); error = no TLS answer: refused, timeout or no TLS on the port (host check charged), or a private address (not charged). |
certStatus | string | Verdict on the certificate the server sent: valid; expiring_soon (valid, fewer than warnDays days left); expired; not_yet_valid; hostname_mismatch (does not cover the host name); untrusted (chain does not verify against the Mozilla root store: self-signed, unknown root, incomplete chain). null when no certificate was read. |
valid | boolean | true when browsers would accept the certificate for this host today (certStatus valid or expiring_soon). null when no certificate was read. Revocation is not checked. |
trusted | boolean | true when the chain verifies against the Mozilla root store; false when it does not; null when it was not determined (the certificate is expired or not yet valid, so verification stopped at the date). |
hostnameMatch | boolean | true when the certificate covers the checked host name (SAN or wildcard). |
expiringSoon | boolean | true when certStatus is expiring_soon: the certificate is accepted today but has fewer than warnDays days left. false for expired, mismatched or untrusted certificates (their certStatus already names the problem). |
daysLeft | integer | Whole days until expiry (rounded down). 0 = expires within 24 hours; negative = already expired by that many days. |
validFrom | string | Start of validity (notBefore), ISO 8601 UTC. |
validTo | string | Expiry (notAfter), ISO 8601 UTC. |
validationError | string | OpenSSL verification code when the certificate is not accepted, e.g. CERT_HAS_EXPIRED, ERR_TLS_CERT_ALTNAME_INVALID, UNABLE_TO_VERIFY_LEAF_SIGNATURE. |
issuer | string | Issuing CA common name (or organization), e.g. R11. |
issuerOrganization | string | Issuing CA organization, e.g. Let's Encrypt. |
subject | string | Certificate subject common name (or organization). |
subjectOrganization | string | Subject organization (OV/EV certificates). |
sanList | array | Subject Alternative Names: host names and IPs the certificate covers (up to 500). |
sanCount | integer | Number of Subject Alternative Names. |
serialNumber | string | Serial number (hex). |
fingerprintSha256 | string | SHA-256 fingerprint of the certificate. |
keyType | string | Public key type, e.g. RSA, EC prime256v1. |
keyBits | integer | Public key size in bits. |
selfSigned | boolean | Subject equals issuer. |
protocol | string | Negotiated TLS version, e.g. TLSv1.3. |
cipher | string | Negotiated cipher suite. |
legacyTlsOnly | boolean | true when the server only completed a handshake with TLS 1.0/1.1 or weak ciphers allowed. |
chain | array | Certificates the server sent, leaf first, followed to the root the verifier found. null when includeChain is off or no certificate was read. |
chainLength | integer | Number of certificates in the chain. |
handshakeMs | integer | Time to complete the TLS handshake, ms. |
ctStatus | string | Certificate Transparency lookup on crt.sh, made for hosts whose certificate was read: found (charged), none (crt.sh lists no certificate for this exact name), unavailable (crt.sh did not answer; not charged), skipped (includeCtHistory off, IP address, no certificate was read, or the ctMaxHosts limit was reached - then ctError says so). |
ctCertificatesFound | integer | Unique certificates crt.sh lists for this exact name (before the ctHistoryLimit cut). null when not looked up or unavailable. |
ctHistory | array | Newest certificates logged for this exact name, up to ctHistoryLimit. Wildcard certificates of the parent domain are not included. |
ctError | string | Why CT history is missing, when ctStatus is unavailable. |
error | string | Why no certificate was read (DNS, connection or TLS failure). Never contains credentials. |
errorCode | string | Machine-readable failure code: DNS_NOT_FOUND (name does not exist), DNS_ERROR (name servers do not answer: SERVFAIL, tried 3 times), BLOCKED_ADDRESS, CONNECTION_REFUSED, CONNECTION_TIMEOUT, CONNECTION_RESET, NETWORK_UNREACHABLE, TLS_HANDSHAKE_FAILED, NO_CERTIFICATE, TECHNICAL_ERROR. |
checkedAt | string | When the check started, ISO 8601 UTC. |
source | string | Where the data came from. |
Input settings (8)
The main fields of the input form. With the API you send them as JSON.
| Setting | Name in the API | Type | Example value |
|---|---|---|---|
| Domains required | domains | array | ["apify.com", "expired.badssl.com"] |
| Default port | port | integer | 443 |
| Expiry warning (days) | warnDays | integer | 30 |
| Certificate chain | includeChain | boolean | true |
| Certificate Transparency history | includeCtHistory | boolean | false |
| CT history: certificates per host | ctHistoryLimit | integer | 20 |
| CT history: max hosts per run | ctMaxHosts | integer | 50 |
| Connection timeout (seconds) | connectTimeoutSecs | integer | 10 |