# SSL Certificate Checker: Expiry, Chain & CT, No Login (`cliqtomedia/ssl-certificate-inspector`) Actor

Bulk SSL/TLS certificate checker: validity verdict, days to expiry, issuer, SANs, TLS version and cipher grade, chain, optional check of each TLS version (1.0 to 1.3) and Certificate Transparency history for up to 5,000 domains or host:port values. No login, no API key.

- **URL**: https://apify.com/cliqtomedia/ssl-certificate-inspector.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** Developer tools, SEO tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 certificate reads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

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, a cipher grade and the certificate chain. Optional: which TLS versions (1.0 to 1.3) the server accepts, and certificates logged for the host in Certificate Transparency (crt.sh). No login and no API key.

Paste up to 5,000 domains, `host:port` values, URLs or public IPs. The prefilled run with two hosts finishes in a few seconds and costs about $0.0033.

![SSL Certificate Checker: paste hosts, get a verdict, days left, issuer, SANs and TLS version for each certificate](https://api.apify.com/v2/key-value-stores/JMfuUIFynO7ih8IdG/records/readme-hero.png)

### Table of contents

- [What it does](#what-it-does)
- [How it works](#how-it-works)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Output](#output)
- [Pricing](#pricing)
- [Limits](#limits)
- [API, export and integrations](#api-export-and-integrations)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Related Actors](#related-actors)
- [Support](#support)

### What it does

The Actor reads the certificate each server sends during the TLS handshake, the same way a browser does, and tells you whether it is fine. It does not open the web page.

- **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. `daysLeft` is negative for an expired certificate.
- **TLS** shows the version and cipher the server chose, with a grade (`strong`, `medium`, `weak`). With `checkTlsVersions` on, it also lists every TLS version the server accepts and flags old TLS 1.0/1.1.
- **CT history** (optional) comes from [crt.sh](https://crt.sh/), a free public search over [Certificate Transparency](https://certificate.transparency.dev/) logs.

Every row has a clear `checkStatus` (`ok`, `not_found`, `error`). When the certificate was read, `certStatus` gives the verdict.

#### Good fit

- 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`).
- Find servers that still accept TLS 1.0/1.1 or choose a CBC cipher without AEAD (`deprecatedTlsEnabled`, `cipherStrength: medium`).
- Make an inventory of who issued your certificates and which names they cover (`issuer`, `sanList`).
- Check mail and admin ports over TLS, for example `imap.gmail.com:993` or `:8443`.
- See which certificates were issued for a name in the past (CT history).

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### How it works

1. You give a list of hosts. The Actor cleans it: URLs become host and port, duplicates and invalid values are dropped.
2. For each host it looks up the address in DNS, skips private addresses, and opens a TLS connection to read the certificate. If the server only speaks old TLS, it tries once more with old settings.
3. It checks trust, name and dates and gives a verdict. If you asked, it tries each TLS version one by one and asks crt.sh for the CT history.
4. It saves one Dataset row per host, and a `RUN_SUMMARY` with counts and the certificates that need attention.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Quick start

[![Input form: the Domains field (box 1), the options with their defaults (box 2) and the Start button (box 3)](https://api.apify.com/v2/key-value-stores/JMfuUIFynO7ih8IdG/records/quick-start-form-20261028.png)](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)

1. Open the **Input** tab. **Domains** (`domains`) is prefilled with `apify.com` and `expired.badssl.com` (box 1 in the picture). Put your own hosts there, one per line.

2. Keep the defaults: port 443, chain on, TLS version check off, CT history off, warning at 30 days (box 2).

3. Click **Start** (box 3).

4. Open the **Output** tab. The **Certificates** view shows the verdict, days left, expiry date and issuer for each host (box 4).

   [![Output table: Status, Valid, Days left, Expires and Issuer columns for apify.com and expired.badssl.com (box 4)](https://api.apify.com/v2/key-value-stores/JMfuUIFynO7ih8IdG/records/output-table-20261028.png)](https://api.apify.com/v2/key-value-stores/JMfuUIFynO7ih8IdG/records/output-table-20261028.png)

5. Other views: **Certificate details** (SANs, key, fingerprint, cipher), **TLS security** (version, cipher grade, accepted versions), **Chain** and **CT history**.

Expected result: two rows. `apify.com` is `valid`. `expired.badssl.com` is `expired` with a negative `daysLeft`.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Use cases

| Buyer job | Input path | 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` |
| Old TLS and CBC ciphers | `domains`, `checkTlsVersions: true` | `supportedTlsVersions`, `deprecatedTlsEnabled`, `cipherStrength` | Filter rows where `deprecatedTlsEnabled` is `true` |
| 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 |

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Input

#### Input fields

| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect |
| --- | --- | --- | --- | --- | --- | --- |
| `domains` | Domains | array of strings | yes | prefill: `apify.com`, `expired.badssl.com` | 1–5,000 host names, `host:port`, URLs or public IPs | The hosts to check |
| `port` | Default port | integer | no | `443` | 1–65535 | Used for values without their own port |
| `warnDays` | Warn when fewer days left | integer | no | `30` | 0–365 | Below this, a valid certificate is `expiring_soon` |
| `includeChain` | Include chain | boolean | no | `true` | true / false | Adds the `chain` list; `chainLength` is always filled |
| `checkTlsVersions` | Check each TLS version | boolean | no | `false` | true / false | Tries TLS 1.0, 1.1, 1.2 and 1.3 one by one; fills `supportedTlsVersions` and `deprecatedTlsEnabled` |
| `includeCtHistory` | CT history from crt.sh | boolean | no | `false` | true / false | Adds certificates logged for the host name |
| `ctHistoryLimit` | CT entries per host | integer | no | `20` | 1–100 | Newest CT entries kept per host |
| `ctMaxHosts` | CT hosts per run | integer | no | `50` | 1–500 | CT is looked up only for the first N certificates read |
| `connectTimeoutSecs` | Connection timeout | integer | no | `10` | 3–15 | Wait time for a TLS answer from each host |

How `domains` is cleaned:

- A URL becomes its host and port: `https://shop.example.org:8443/page` becomes `shop.example.org` on port 8443.
- Each host name is checked as given. `www.example.com` and `example.com` are two different checks, because they can have different certificates.
- Internationalised names become punycode: `münchen.de` becomes `xn--mnchen-3ya.de`.
- Duplicates (also in different letter case) are checked once.
- Invalid values (for example `localhost`, port `99999`, or text with spaces or HTML) get no row and are not charged. They are listed in `RUN_SUMMARY.invalidInputs` with a reason.
- If no value is valid, the run fails at once with a message that names the bad values.

#### Interacting settings

| When | Setting | Effect |
| --- | --- | --- |
| CT history is on | `ctMaxHosts` | Only the first N certificates read get CT history. The other hosts get the full TLS check, `ctStatus: skipped` and no CT charge. |
| CT history is on | run timeout | Each CT lookup takes about 13 seconds, and the host waits for it. 50 hosts add about 11 minutes. For more than about 200 CT hosts, raise the run timeout above 1 hour. |
| A value has its own port | `port` | The value's own port wins. |
| TLS version check is on | `connectTimeoutSecs` | Up to 3 more handshakes per host, at the same time. A version the server ignores waits up to `connectTimeoutSecs` and is listed in `uncheckedTlsVersions`. |

#### Input example

```json
{
  "domains": ["apify.com", "expired.badssl.com"],
  "port": 443,
  "warnDays": 30,
  "includeChain": true,
  "checkTlsVersions": false,
  "includeCtHistory": false
}
```

Expected output: 2 rows. `apify.com` has `certStatus: valid`. `expired.badssl.com` has `certStatus: expired`, `valid: false` and a negative `daysLeft`.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Output

#### Output fields

| Field | Meaning | Can be empty? |
| --- | --- | --- |
| `input` | The value as you entered it | no |
| `domain`, `port` | Host and port that were checked | no |
| `ipAddress` | IP address the Actor connected to | yes |
| `checkStatus` | `ok` (certificate read), `not_found` (name not in DNS) or `error` | no |
| `certStatus` | `valid`, `expiring_soon`, `expired`, `not_yet_valid`, `hostname_mismatch`, `untrusted` | yes, when no certificate was read |
| `valid` | `true` only when the certificate is trusted, matches the name and is in its dates | yes |
| `trusted` | Chain leads to a trusted root | yes |
| `hostnameMatch` | Host name is on the certificate | yes |
| `expiringSoon` | Fewer than `warnDays` days left | yes |
| `daysLeft` | Whole days until expiry; negative when expired | yes |
| `validFrom`, `validTo` | Certificate dates, ISO 8601 UTC | yes |
| `validationError` | Reason from the trust check, for example `CERT_HAS_EXPIRED` | yes |
| `issuer`, `issuerOrganization` | Who issued the certificate | yes |
| `subject`, `subjectOrganization` | Who the certificate was issued to | yes |
| `sanList`, `sanCount` | All names on the certificate | yes |
| `serialNumber`, `fingerprintSha256` | Certificate identity | yes |
| `keyType`, `keyBits` | Public key, for example `RSA` 2048 or `EC` 256 | yes |
| `selfSigned` | Certificate signed by itself | yes |
| `protocol`, `cipher` | TLS version and cipher the server chose | yes |
| `cipherStrength` | `strong` (TLS 1.3, or AEAD with forward secrecy), `medium` (CBC, or no forward secrecy), `weak` (RC4, 3DES, NULL, export). Servers that offer only RC4/3DES/export ciphers cannot be read at all and come back as `TLS_HANDSHAKE_FAILED`, so `weak` is rare in practice | yes |
| `legacyTlsOnly` | `true` when only an old TLS 1.0/1.1 handshake worked | yes |
| `supportedTlsVersions` | TLS versions the server accepted, for example `["TLSv1.2", "TLSv1.3"]` | `null` when `checkTlsVersions` is off |
| `deprecatedTlsEnabled` | `true` when TLS 1.0 or 1.1 is still accepted | `null` when the check is off or an old version gave no answer |
| `uncheckedTlsVersions` | Versions that gave no answer either way (timeout) | `null` when the check is off |
| `chain`, `chainLength` | Certificates the server sent, with dates and fingerprints | yes |
| `handshakeMs` | Time of the TLS handshake | yes |
| `ctStatus` | `found`, `none`, `unavailable` or `skipped` | no |
| `ctCertificatesFound`, `ctHistory` | Count and newest entries from crt.sh | yes |
| `ctError` | Why CT history is missing | yes |
| `error`, `errorCode` | What failed, for example `CONNECTION_TIMEOUT` | yes |
| `checkedAt`, `source` | Check time and source | no |

When no certificate was read, all certificate fields are `null`. They are never `0` or `false` in that case. An expired or untrusted certificate is a fact about the site, so its row is `ok`, not `error`.

`trusted` can be `null` for an expired certificate: the trust check stops at the date problem, so trust is unknown.

#### Row status and charges

| `checkStatus` | Meaning | Example `errorCode` | Charged |
| --- | --- | --- | --- |
| `ok` | Certificate read | — | host check + certificate |
| `not_found` | Host name does not exist in DNS | `DNS_NOT_FOUND` | host check only |
| `error` | Host could not be reached over TLS | `CONNECTION_TIMEOUT`, `CONNECTION_REFUSED`, `TLS_HANDSHAKE_FAILED`, `DNS_ERROR` | host check only |
| `error` | Private or reserved address | `BLOCKED_ADDRESS` | nothing |

#### Output example

Trimmed row for `apify.com` from a real run (chain shortened to one entry):

```json
{
  "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",
  "cipherStrength": "strong",
  "legacyTlsOnly": false,
  "supportedTlsVersions": null,
  "deprecatedTlsEnabled": null,
  "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"
}
```

Rows appear in the order the checks finish, not in input order. Sort by `input` or `domain` if you need a fixed order.

#### Run summary

Each run also writes `RUN_SUMMARY` to the Key-value store. It has:

- the run status (`COMPLETE`, `PARTIAL` or `ERROR`) and the reason for a partial run;
- counts by `checkStatus` and `certStatus`, hosts with old TLS (`deprecatedTls`) and with a `weak` cipher grade (`weakCipher`, rare: see Limits);
- `attention`: the certificates that are expired, expiring soon, untrusted or for the wrong name;
- invalid inputs, hosts that were not checked, and warnings.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Pricing

#### Pricing unit

You pay per event. Prices are for the Free plan; on Bronze, Silver and Gold plans the certificate and CT prices are lower.

| Event | Free plan price | When |
| --- | ---: | --- |
| Actor start | $0.0015 per run | Once per run |
| Host check | $0.0001 per host | Every host the Actor tried to reach (DNS + TLS), whatever the answer |
| Certificate read | $0.0008 per host (Gold: $0.00056) | Each row with `checkStatus: ok` |
| CT history | $0.002 per host (Gold: $0.0014) | Each row with `ctStatus: found` |

A host with a certificate costs $0.0009 in total on the Free plan. Not charged: invalid values, duplicates, private addresses, CT lookups that failed or found nothing, and hosts over `ctMaxHosts`. Platform usage (compute, storage) is included in these prices. The start fee is per run on the default 256 MB memory; it grows with memory (one start event per GB). The TLS version check has no extra charge.

#### Cost examples (Free plan price)

| Scenario | Hosts | Charge |
| --- | ---: | ---: |
| Prefill | 2 | about $0.0033 |
| Client list | 100 | about $0.09 |
| Portfolio | 1,000 | about $0.90 |
| Maximum input | 5,000 | about $4.50 |
| 50 hosts with CT history | 50 | about $0.15 |

Set **Maximum cost per run** in the run options to cap the spend. When the cap is reached, the Actor stops, keeps the rows it has, and marks the run `PARTIAL` with `CHARGE_LIMIT`.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Limits

#### What the Actor does not do

- **No revocation check.** The Actor does not ask OCSP or CRL servers. A revoked certificate can still show `valid`.
- **No full cipher scan and no security score.** You get the cipher the server chose with a grade, and, if you ask, the TLS versions it accepts. The Actor does not try every cipher and does not test for vulnerabilities.
- **No RC4, 3DES or export ciphers.** The Actor's TLS library does not offer them. A server that accepts only these ciphers returns `TLS_HANDSHAKE_FAILED`, and a TLS version the server accepts only with them shows as not accepted. `deprecatedTlsEnabled: false` means "not accepted with the ciphers the Actor can offer".
- **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.

#### Speed and volume

- 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).
- With `checkTlsVersions` on: 500 hosts in about 1.5 minutes. A server that ignores an old version silently adds up to `connectTimeoutSecs` per host. 5,000 hosts with this option were not measured.
- 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 normal `not_found` result.
- The default run timeout is 1 hour. When the time runs out, the Actor saves what it has and marks the run `PARTIAL` with `DEADLINE`.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### API, export and integrations

- **API:** start the Actor with the [Apify API](https://docs.apify.com/api/v2) and read the Dataset items.
- **Export:** download the Dataset as JSON, CSV, Excel or XML from the Output tab.
- **Scheduling:** create a schedule in Apify Console to check the same list every week and catch certificates before they expire.
- **Webhooks / no-code:** send a webhook when the run finishes, or use the Apify integrations for Make, Zapier or n8n.

#### Use with AI agents (MCP)

- MCP server URL with only this Actor: `https://mcp.apify.com?tools=cliqtomedia/ssl-certificate-inspector`
- Smallest useful input: `{"domains": ["apify.com"]}`. Add `"checkTlsVersions": true` to see the accepted TLS versions.
- Key output fields: `certStatus` (valid / expiring_soon / expired / not_yet_valid / hostname_mismatch / untrusted), `daysLeft`, `validTo`, `issuer`, `cipherStrength`.
- A run with a few hosts takes a few seconds. Cost: $0.0009 per host with a certificate plus $0.0015 per run on the Free plan.
- Limits: up to 5,000 hosts per run; public hosts only; no revocation check; CT history adds about 13 seconds per host.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Troubleshooting

#### A host shows `CONNECTION_TIMEOUT`

Reason: nothing answered with TLS on that port within `connectTimeoutSecs`. Often the port is closed by a firewall, or the service is not TLS (for example SSH on port 22).
Action: check the port. For a slow server, raise `connectTimeoutSecs` up to 15.

#### `TLS_HANDSHAKE_FAILED` on a mail port

Reason: the port starts in plain text and needs STARTTLS (for example 25, 587, 143). The Actor does not support STARTTLS.
Action: use the TLS port (465 for SMTP, 993 for IMAP, 995 for POP3).

#### `uncheckedTlsVersions` is not empty

Reason: for that version the server neither finished the handshake nor refused it within `connectTimeoutSecs`. Some firewalls drop old TLS silently.
Action: treat the version as unknown, not as off. Run the host again with a higher `connectTimeoutSecs`.

#### `deprecatedTlsEnabled` is `false`, but a scanner says TLS 1.0 is on

Reason: the server accepts TLS 1.0/1.1 only with RC4, 3DES or export ciphers, which the Actor cannot offer. A busy server that resets the connection during a version check also shows that version as not accepted.
Action: read `false` as "not accepted with modern and CBC ciphers". Check such a host with a full cipher scanner.

#### `ctStatus: unavailable`

Reason: crt.sh did not answer or returned an error. This happens often, because it is a free and busy service. `ctError` gives the reason.
Action: run the same hosts again later. Failed CT lookups are not charged.

#### A site works in my browser, but the row says `untrusted`

Reason: the server does not send its intermediate certificate. Some browsers download it on their own, but most other software does not. `validationError` shows `UNABLE_TO_VERIFY_LEAF_SIGNATURE` then.
Action: ask the site owner to install the full chain.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### FAQ

#### Do I need an account on another site or an API key?

No. The Actor connects to each host directly and uses the public crt.sh service for CT history.

#### Why is `daysLeft` `null` for some rows?

No certificate was read: the host does not exist (`not_found`) or did not answer (`error`). A `null` value means "unknown". It is never set to `0`.

#### Why does a revoked certificate show `valid`?

The Actor does not check revocation lists (OCSP or CRL). It checks trust, name and dates only.

#### Is a `strong` cipher a full security audit?

No. `cipherStrength` grades only the one cipher the server chose for this connection. A server can still offer weaker ciphers to old clients.

#### 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`); supported protocols → `supportedTlsVersions`; issued by → `issuer`. Failed checks here give `null`, not `0` or empty strings.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Related Actors

- [Domain WHOIS (RDAP) & DNS Lookup, No Login](https://apify.com/cliqtomedia/domain-whois-dns-lookup) — domain expiry, registrar and DNS records for the same list of domains.
- [Bulk URL Status & Redirect Checker](https://apify.com/cliqtomedia/bulk-url-status-redirect-checker) — HTTP status codes and redirect chains, for example the HTTP to HTTPS redirect.
- [Website Tech Stack Detector](https://apify.com/cliqtomedia/tech-stack-detector) — which CMS, server and tools a site uses.
- [Website Metadata & Open Graph Scraper](https://apify.com/cliqtomedia/website-metadata-open-graph-scraper) — titles, descriptions and Open Graph tags of the same sites.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

### Support

For a bug, question or feature request, open an issue on the **Issues** tab of this Actor. Include the run ID, the hosts you checked and the error text. Do not send passwords, tokens or private data.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/OqlJtUKhWjPSikYNO/input)**

# Changelog

This Actor's version history is a separate document: https://apify.com/cliqtomedia/ssl-certificate-inspector/changelog.md

# Actor input Schema

## `domains` (type: `array`):

Up to 5,000 host names, host:port values, URLs or public IP addresses, one per line (e.g. apify.com, mail.example.com:993, https://shop.example.org/page). Each host name is checked as given (www.example.com and example.com are different checks). Duplicates are checked once; invalid values are listed in RUN_SUMMARY and not charged.

## `port` (type: `integer`):

Port used for values without their own port. 443 for HTTPS; 993/995/465 for mail over TLS, 8443 for admin panels.

## `warnDays` (type: `integer`):

A valid certificate with fewer days left than this gets certStatus expiring_soon and expiringSoon true, and is listed in RUN_SUMMARY.attention.

## `includeChain` (type: `boolean`):

Add the chain (intermediates and root) with subject, issuer, dates and fingerprints. Off keeps rows smaller; chainLength is always filled.

## `checkTlsVersions` (type: `boolean`):

Try TLS 1.0, 1.1, 1.2 and 1.3 one by one and list the versions the server accepts (supportedTlsVersions, deprecatedTlsEnabled). Up to 3 extra handshakes per host: slower, no extra charge.

## `includeCtHistory` (type: `boolean`):

Add certificates logged for each host name on crt.sh (issuer, dates, names, serial). crt.sh is a free community service and answers slowly (often 5-20 s per host); the Actor asks it one host at a time, so turn this on for lists of up to a few hundred hosts. Charged only when crt.sh lists certificates.

## `ctHistoryLimit` (type: `integer`):

Newest logged certificates kept per host when CT history is on. ctCertificatesFound always shows the full count.

## `ctMaxHosts` (type: `integer`):

crt.sh answers slowly and allows about 5 requests a minute, so CT history is looked up one host at a time for the first N certificates read. Other hosts get the full TLS check without CT (not charged for CT). 50 hosts need about 11 minutes; raise the run timeout for more.

## `connectTimeoutSecs` (type: `integer`):

How long to wait for a TLS answer from each host before marking it unreachable (3-15 s).

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "expired.badssl.com"
  ],
  "port": 443,
  "warnDays": 30,
  "includeChain": true,
  "checkTlsVersions": false,
  "includeCtHistory": false,
  "ctHistoryLimit": 20,
  "ctMaxHosts": 50,
  "connectTimeoutSecs": 10
}
```

# Actor output Schema

## `overview` (type: `string`):

Verdict, days left, expiry date, issuer and TLS version per host.

## `details` (type: `string`):

Subject, SANs, key, fingerprint and verification code per host.

## `tlsSecurity` (type: `string`):

TLS version, cipher grade and accepted TLS versions per host.

## `chain` (type: `string`):

Certificate chain per host.

## `ctHistory` (type: `string`):

Certificates logged in Certificate Transparency per host (when enabled).

## `runSummary` (type: `string`):

Status, counts per verdict, certificates needing attention, invalid and unprocessed hosts, warnings.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "apify.com",
        "expired.badssl.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/ssl-certificate-inspector").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "domains": [
        "apify.com",
        "expired.badssl.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/ssl-certificate-inspector").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "apify.com",
    "expired.badssl.com"
  ]
}' |
apify call cliqtomedia/ssl-certificate-inspector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cliqtomedia/ssl-certificate-inspector"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/OqlJtUKhWjPSikYNO/builds/A8pKQeebevcyObi49/openapi.json
