Monitor SSL Certificate Expiry with a Free API

2026-04-25 | Tags: [ssl, certificates, monitoring, security, devops]

SSL certificates expire. When they do, your site shows a browser warning and users leave. Most teams discover expired certificates from customer complaints. Here's how to discover them before that happens.

Quick Check

curl -s "https://hermesforge.dev/api/ssl?domain=example.com" | python3 -m json.tool

Response:

{
  "domain": "example.com",
  "valid": true,
  "issuer": "DigiCert Inc",
  "subject": "*.example.com",
  "not_before": "2025-01-15T00:00:00Z",
  "not_after": "2026-02-15T23:59:59Z",
  "days_remaining": 335,
  "protocol": "TLSv1.3",
  "cipher": "TLS_AES_256_GCM_SHA384"
}

The days_remaining field tells you exactly how long you have.

Python: Multi-Domain Monitoring

import requests
import json
from datetime import datetime

DOMAINS = [
    "yoursite.com",
    "api.yoursite.com",
    "staging.yoursite.com",
    "admin.yoursite.com",
]

ALERT_THRESHOLD_DAYS = 30

def check_ssl(domain):
    """Check SSL certificate for a single domain."""
    resp = requests.get(
        "https://hermesforge.dev/api/ssl",
        params={"domain": domain},
    )
    return resp.json()

results = []
alerts = []

for domain in DOMAINS:
    data = check_ssl(domain)
    results.append(data)

    days = data.get("days_remaining", 0)
    valid = data.get("valid", False)

    if not valid:
        alerts.append(f"EXPIRED: {domain} — certificate is invalid")
    elif days < ALERT_THRESHOLD_DAYS:
        alerts.append(f"EXPIRING: {domain} — {days} days remaining")

    print(f"{domain}: {'valid' if valid else 'INVALID'}, {days} days remaining")

if alerts:
    print("\n--- ALERTS ---")
    for alert in alerts:
        print(f"  {alert}")

Scheduled Monitoring with Cron

Run checks daily and log results:

#!/usr/bin/env python3
"""ssl_monitor.py — daily SSL certificate monitoring"""
import requests
import json
from datetime import datetime

DOMAINS = ["yoursite.com", "api.yoursite.com"]
LOG_FILE = "/opt/monitoring/ssl.jsonl"
ALERT_DAYS = 14

for domain in DOMAINS:
    data = requests.get(
        "https://hermesforge.dev/api/ssl",
        params={"domain": domain},
    ).json()

    entry = {
        "domain": domain,
        "checked_at": datetime.utcnow().isoformat() + "Z",
        "valid": data.get("valid", False),
        "days_remaining": data.get("days_remaining", 0),
        "issuer": data.get("issuer", "unknown"),
    }

    with open(LOG_FILE, "a") as f:
        f.write(json.dumps(entry) + "\n")

    if not entry["valid"]:
        print(f"CRITICAL: {domain} SSL certificate INVALID")
    elif entry["days_remaining"] < ALERT_DAYS:
        print(f"WARNING: {domain} SSL expires in {entry['days_remaining']} days")

Crontab:

0 8 * * * python3 /opt/monitoring/ssl_monitor.py

Slack Alerts

Send notifications when certificates are close to expiring:

import requests

SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

def send_slack_alert(domain, days_remaining):
    emoji = "🔴" if days_remaining < 7 else "🟡"
    text = f"{emoji} SSL Alert: *{domain}* expires in *{days_remaining} days*"
    requests.post(SLACK_WEBHOOK, json={"text": text})

# After checking each domain:
if days_remaining < 30:
    send_slack_alert(domain, days_remaining)

CI/CD Integration

Block deployments if SSL certificates are about to expire:

#!/bin/bash
DOMAIN="yoursite.com"
MIN_DAYS=7

DAYS=$(curl -s "https://hermesforge.dev/api/ssl?domain=${DOMAIN}" | \
  python3 -c "import sys,json; print(json.load(sys.stdin).get('days_remaining',0))")

echo "SSL certificate for ${DOMAIN}: ${DAYS} days remaining"

if [ "$DAYS" -lt "$MIN_DAYS" ]; then
  echo "FAIL: SSL certificate expires in ${DAYS} days (minimum: ${MIN_DAYS})"
  exit 1
fi

GitHub Actions Workflow

name: SSL Certificate Check
on:
  schedule:
    - cron: '0 8 * * 1'  # Every Monday at 8am
  workflow_dispatch:

jobs:
  check-ssl:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        domain: [yoursite.com, api.yoursite.com]
    steps:
      - name: Check SSL Certificate
        run: |
          RESULT=$(curl -s "https://hermesforge.dev/api/ssl?domain=${{ matrix.domain }}")
          DAYS=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('days_remaining',0))")
          VALID=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('valid',False))")

          echo "Domain: ${{ matrix.domain }}"
          echo "Valid: $VALID"
          echo "Days remaining: $DAYS"

          if [ "$VALID" != "True" ]; then
            echo "::error::SSL certificate for ${{ matrix.domain }} is INVALID"
            exit 1
          fi

          if [ "$DAYS" -lt 14 ]; then
            echo "::warning::SSL certificate for ${{ matrix.domain }} expires in $DAYS days"
          fi

Tracking Expiry Over Time

Parse the JSONL log to spot trends:

import json
from collections import defaultdict

def ssl_report(log_file):
    """Generate SSL expiry report from monitoring log."""
    latest = {}

    with open(log_file) as f:
        for line in f:
            entry = json.loads(line)
            domain = entry["domain"]
            latest[domain] = entry

    print("SSL Certificate Status Report")
    print("=" * 50)

    for domain, data in sorted(latest.items()):
        days = data["days_remaining"]
        status = "VALID" if data["valid"] else "EXPIRED"

        if days < 7:
            indicator = "CRITICAL"
        elif days < 30:
            indicator = "WARNING"
        else:
            indicator = "OK"

        print(f"  {domain}: {status}, {days} days remaining [{indicator}]")

ssl_report("/opt/monitoring/ssl.jsonl")

Rate Limits

Tier Limit Cost
Anonymous 5/min, 20/day Free
API Key 50/day Free
Pro 1000/day $9 / 30 days

Get a free API key:

curl -X POST "https://hermesforge.dev/api/keys" -H "Content-Type: application/json" -d '{}'

The SSL endpoint has a higher anonymous daily limit (20/day) than other APIs because certificate checks are lightweight and commonly needed for monitoring.