# SSL Certificate Checker API: Expiry, Issuer, SAN, DNS (`actose/ssl-certificate-checker`) Actor

Bulk SSL/TLS certificate checker: issuer, expiry date, days left, SANs, full chain, validation errors, plus DNS A, MX, TXT, SPF and DMARC. Expiring-soon and changed-only modes for schedules. No API key, no login. MCP-ready for AI agents. Errors are free. $2 per 1,000 domains.

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

## Pricing

from $1.70 / 1,000 domain checkeds

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

## SSL Certificate Checker API: Expiry, Issuer, SAN, Chain & DNS (bulk)

**$2 per 1,000 domains checked.** No API key, no login, no monthly rental. Unreachable domains, invalid inputs and filtered-out domains are **free**.

Paste a list of domains (hundreds or thousands), get back for each one:

- **SSL/TLS certificate**: issuer, expiry date, **days until expiry**, valid/invalid with the exact validation error (expired, self-signed, hostname mismatch, untrusted root…), subject, **Subject Alternative Names (SAN)**, serial number, SHA-256 fingerprint, key type and size, signature algorithm, TLS version and cipher.
- **Full certificate chain**: leaf, intermediates and root, each with issuer and expiry.
- **DNS records**: A, AAAA, MX, NS, TXT, CAA, plus parsed **SPF** and **DMARC** (record + policy).
- A one-line plain-text **summary** per domain, ready for Slack alerts, emails or LLMs.

Built for agencies, MSPs and IT teams who monitor a portfolio of domains on a schedule: run it every morning with **"Only certificates expiring within N days"** or **"Only new or changed certificates"** and you only get (and only pay for) the domains that need attention.

### How much does it cost?

Pay per event: **$0.002 per domain checked** (`domain-checked` event), charged only when a certificate was actually retrieved and delivered.

| Domains | Price |
|---|---|
| 100 | $0.20 |
| 1,000 | $2.00 |
| 10,000 | $20.00 |

Free (never charged): domains that don't resolve, refuse HTTPS or time out; invalid inputs; duplicates; domains skipped by the expiry filter or by "only changes". The Actor stops cleanly when your spending limit is reached.

### Input

```json
{
  "domains": ["apify.com", "github.com", "https://www.example.org/page", "expired.badssl.com", "mail.example.com:8443"],
  "expiringWithinDays": 30,
  "warningDays": 30,
  "checkDns": true,
  "includeChain": true,
  "onlyChanges": false,
  "includeErrors": true,
  "maxConcurrency": 20,
  "timeoutSecs": 10
}
```

| Field | Description |
|---|---|
| `domains` | Domains, URLs, `host:port`, IPs or internationalized names (`café.fr`). Duplicates removed. |
| `expiringWithinDays` | Alert mode: only output domains expiring within N days (or already expired). Empty = all. |
| `warningDays` | Below this many days, status is `expiring` instead of `ok` (default 30). |
| `checkDns` | A, AAAA, MX, NS, TXT, CAA, SPF, DMARC lookups (default on). |
| `includeChain` | Full certificate chain (default on). |
| `onlyChanges` | Only output certificates that changed since the previous run (renewals, new issuer). |
| `includeErrors` | Also write free `error` rows for unreachable domains (default on). |

### Output

One JSON object per domain in the dataset (export to JSON, CSV, Excel, HTML):

```json
{
  "input": "expired.badssl.com",
  "domain": "expired.badssl.com",
  "port": 443,
  "status": "expired",
  "checkedAt": "2026-10-20T08:00:00Z",
  "ipAddress": "104.154.89.105",
  "certificateValid": false,
  "validationError": "certificate has expired",
  "hostnameMatches": true,
  "issuer": "COMODO CA Limited COMODO RSA Domain Validation Secure Server CA",
  "issuerOrganization": "COMODO CA Limited",
  "issuerCommonName": "COMODO RSA Domain Validation Secure Server CA",
  "subjectCommonName": "*.badssl.com",
  "validFrom": "2015-04-09T00:00:00Z",
  "validTo": "2015-04-12T23:59:59Z",
  "daysUntilExpiry": -4209,
  "isExpired": true,
  "isSelfSigned": false,
  "subjectAltNames": ["*.badssl.com", "badssl.com"],
  "subjectAltNamesCount": 2,
  "serialNumber": "4AE795493A49B0D5B2B2CC3E1F4E5E4B",
  "signatureAlgorithm": "sha256WithRSAEncryption",
  "publicKeyAlgorithm": "RSA",
  "publicKeySize": 2048,
  "sha256Fingerprint": "BA105CE02BAC76888ECEE47CD17D1AE5...",
  "tlsVersion": "TLSv1.2",
  "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
  "chainLength": 3,
  "certificateChain": [
    {"position": 0, "subjectCommonName": "*.badssl.com", "issuerCommonName": "COMODO RSA Domain Validation Secure Server CA", "validTo": "2015-04-12T23:59:59Z", "daysUntilExpiry": -4209, "isSelfSigned": false, "sha256Fingerprint": "BA10..."},
    {"position": 1, "subjectCommonName": "COMODO RSA Domain Validation Secure Server CA", "issuerCommonName": "COMODO RSA Certification Authority", "validTo": "2029-02-11T23:59:59Z", "daysUntilExpiry": 1209, "isSelfSigned": false, "sha256Fingerprint": "02AB..."}
  ],
  "dnsA": ["104.154.89.105"],
  "dnsAAAA": [],
  "dnsMX": [],
  "dnsNS": [],
  "dnsTXT": [],
  "dnsCAA": [],
  "spfRecord": null,
  "hasSpf": false,
  "dmarcRecord": null,
  "dmarcPolicy": null,
  "hasDmarc": false,
  "dnsError": null,
  "summary": "expired.badssl.com: certificate EXPIRED; issued by COMODO CA Limited ...; expires 2015-04-12 (-4209 days left); validation error: certificate has expired; 2 SANs, TLSv1.2; SPF no, DMARC no.",
  "error": null
}
```

`status` is one of `ok`, `expiring`, `expired`, `invalid` (untrusted, self-signed, hostname mismatch), `error` (unreachable, free).
An unreachable domain looks like `{"domain": "no-such-domain-xyz.com", "status": "error", "error": "Domain does not resolve (DNS): ..."}` and is not charged.

### Use cases

#### 1. Agency: weekly expiry alert for 300 client websites

A web agency account manager monitors every client site. Scheduled every Monday with the alert filter, the run only returns sites expiring within 21 days, sent to Slack via Make.

Input:

```json
{"domains": ["client-bakery.fr", "shop.client-garage.com", "client-dentist.de", "..."], "expiringWithinDays": 21, "checkDns": false}
```

Output (only the 2 domains needing action, so 300 checked, 2 charged = **$0.004 per week**):

```json
[{"domain": "client-garage.com", "status": "expiring", "daysUntilExpiry": 9, "issuer": "Let's Encrypt R11", "validTo": "2026-10-29T10:12:00Z"},
 {"domain": "client-dentist.de", "status": "expired", "daysUntilExpiry": -3, "validationError": "certificate has expired"}]
```

Next: a Make scenario posts each `summary` to #ops and opens a ticket.

#### 2. MSP / IT team: detect unexpected certificate changes on 1,200 hosts

A security engineer runs the Actor daily with `onlyChanges` on all public hosts (VPN, OWA, APIs on custom ports). Any new fingerprint (renewal by an unknown CA, replaced certificate) shows up with `previousFingerprint`.

Input:

```json
{"domains": ["vpn.corp-example.com:443", "mail.corp-example.com", "api.corp-example.com:8443"], "onlyChanges": true, "includeChain": true}
```

Output:

```json
[{"domain": "api.corp-example.com", "port": 8443, "certificateChanged": true, "previousFingerprint": "3F9A...", "sha256Fingerprint": "C01D...", "issuer": "DigiCert Inc DigiCert Global G2 TLS RSA SHA256 2020 CA1"}]
```

Next: rows go to Google Sheets as an audit trail; first run costs 1,200 × $0.002 = $2.40, then only changes are billed (typically a few cents a day).

#### 3. Email deliverability / lead qualification: SPF & DMARC audit of 5,000 prospect domains

A deliverability consultant (or a sales team selling email security) checks which prospects have no DMARC or a `p=none` policy.

Input:

```json
{"domains": ["prospect-one.com", "prospect-two.io", "..."], "checkDns": true, "includeChain": false}
```

Output:

```json
[{"domain": "prospect-two.io", "hasSpf": true, "spfRecord": "v=spf1 include:_spf.google.com ~all", "hasDmarc": false, "dmarcPolicy": null, "dnsMX": [{"priority": 1, "exchange": "smtp.google.com"}], "daysUntilExpiry": 61}]
```

Next: filter `hasDmarc = false` in the CSV export and use it as an outreach list. 5,000 domains = $10.

### Use via API

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("<username>/ssl-certificate-checker").call(run_input={
    "domains": ["apify.com", "github.com"], "expiringWithinDays": 30,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["domain"], item["status"], item["daysUntilExpiry"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('<username>/ssl-certificate-checker').call({
    domains: ['apify.com', 'github.com'], expiringWithinDays: 30,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => `${i.domain}: ${i.status} (${i.daysUntilExpiry} d)`));
```

Synchronous call returning the items directly (handy for scripts and webhooks):
`POST https://api.apify.com/v2/acts/<username>~ssl-certificate-checker/run-sync-get-dataset-items?token=<APIFY_TOKEN>`

### Automation (Make, n8n, Zapier, Google Sheets)

Example n8n workflow: **Schedule Trigger** (daily 7:00) → **Apify node** "Run Actor and get dataset items" with input `{"domains": [...], "expiringWithinDays": 14}` → **IF** `status != "ok"` → **Slack / Email** node sending `{{$json.summary}}`.
The same works with the Apify apps for Make and Zapier, or with Apify Schedules + the Google Sheets integration to keep a live certificate inventory sheet.

### Use with AI agents (MCP)

The Actor is available through the Apify MCP server, so Claude, Cursor, ChatGPT and other AI agents can call it as a tool ("check when the certificates of these 20 domains expire and whether they have DMARC"). Each result carries a short plain-text `summary` field the agent can quote directly, and the input is a simple list of domains.

### FAQ

**Does it need an API key or account on the target sites?** No. It performs a standard TLS handshake, like a browser, and public DNS queries (1.1.1.1 / 8.8.8.8). No web page is downloaded.

**Do I pay for domains that are down?** No. Errors, invalid inputs, duplicates and domains skipped by filters are free.

**Can I check custom ports (8443, 993…)?** Yes, write `host:port`. The port must speak TLS directly (STARTTLS is not supported).

**Expired or self-signed certificates?** They are still read and fully reported, with `certificateValid: false` and the exact `validationError`.

**How do scheduled runs work?** Create a Schedule in Apify Console with `expiringWithinDays` or `onlyChanges`; only actionable domains are output and charged.

**Internationalized domains?** Yes, `café.fr` is converted to punycode (`xn--caf-dma.fr`).

### Limits

- DNS records are read on the exact host given: `www.example.com` usually has no MX/DMARC; give the root domain for email checks.
- Domain registration (WHOIS) expiry is not included, only certificate expiry.
- STARTTLS (SMTP 25/587, IMAP 143) is not supported; implicit TLS ports (443, 465, 993, 8443…) are.
- Certificate validity is judged against the Mozilla/OpenSSL trust store of the Actor's container; OCSP/CRL revocation is not checked.
- Hosts behind firewalls allowing only specific countries may time out (reported as free `error`).
- Up to 10,000 domains per run; split larger lists into several runs.

# Actor input Schema

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

Domains or URLs to check, one per line. Accepted formats: example.com, https://www.example.com/page, example.com:8443 (custom port), IP addresses, internationalized domains (café.fr). Duplicates are removed.

## `expiringWithinDays` (type: `integer`):

Alert mode: only output (and only charge) domains whose certificate expires within this many days or is already expired. Leave empty to output every domain.

## `warningDays` (type: `integer`):

Certificates expiring within this many days get status "expiring" instead of "ok".

## `checkDns` (type: `boolean`):

Look up A, AAAA, MX, NS, TXT, CAA records plus SPF and DMARC (via public resolvers 1.1.1.1 / 8.8.8.8). Records are read on the exact host you give (e.g. www.example.com).

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

Add the full certificate chain (leaf, intermediates, root) with issuer and expiry of each certificate.

## `onlyChanges` (type: `boolean`):

For scheduled monitoring: only output domains whose certificate fingerprint changed since the previous run (renewal, new issuer, replaced certificate). The first run outputs everything. State is kept in the named key-value store "ssl-certificate-checker-state".

## `includeErrors` (type: `boolean`):

Also write a row with status "error" for domains that do not resolve, refuse HTTPS or time out. These rows are never charged.

## `maxConcurrency` (type: `integer`):

How many domains are checked at the same time.

## `timeoutSecs` (type: `integer`):

Connection and handshake timeout for each attempt.

## `maxRetries` (type: `integer`):

Extra attempts when a connection times out or is reset.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "github.com",
    "example.org",
    "expired.badssl.com"
  ],
  "expiringWithinDays": 30,
  "warningDays": 30,
  "checkDns": true,
  "includeChain": true,
  "onlyChanges": false,
  "includeErrors": true,
  "maxConcurrency": 20,
  "timeoutSecs": 10,
  "maxRetries": 1
}
```

# Actor output Schema

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

One row per domain: status, expiry, issuer, SANs, chain and DNS records.

## `results` (type: `string`):

Full results including certificate chain and every DNS record.

# 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",
        "github.com",
        "example.org",
        "expired.badssl.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("actose/ssl-certificate-checker").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",
        "github.com",
        "example.org",
        "expired.badssl.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("actose/ssl-certificate-checker").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",
    "github.com",
    "example.org",
    "expired.badssl.com"
  ]
}' |
apify call actose/ssl-certificate-checker --silent --output-dataset

```

## MCP server setup

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

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/oOQYdnIPcVmaXydAw/builds/Q1uvco0JUCYnJawrg/openapi.json
