One curl command to check any cert and domain — CertWatch's public API
The most common thing engineers want before signing up for any monitoring product is to verify it works on their domain. CertWatch has a public endpoint for exactly this:
curl -s "https://certwatch.app/check/public?domain=example.com" | python3 -m json.tool
Response:
{
"domain": "example.com",
"resolves": true,
"cert_expires_at": "2026-09-14",
"cert_days_left": 96,
"cert_chain_ok": true,
"checked_at": "2026-06-10T06:00:00Z"
}
That's the SSL certificate expiry, the chain validation result (full trust chain to a known root, no self-signed intermediates), and DNS resolution — all in one call. Rate-limited to 3 requests/minute per IP. No API key, no account.
What the paid tier adds
The public endpoint intentionally omits domain expiry (WHOIS registrar-expiration-date). That's behind the API key:
curl -s "https://certwatch.app/check?domain=example.com" \
-H "X-Api-Key: your-api-key"
Response adds:
{
"domain_expires_at": "2027-01-22",
"domain_days_left": 226,
"registrar": "NameCheap, Inc."
}
This is the distinction: if you just need to know whether a cert is about to expire (the common case), the public endpoint is free. If you're also monitoring domain renewals — and the cert expiring because someone forgot to renew the domain is a very real failure mode — that's paid.
Webhooks
The paid tier also fires webhooks when anything crosses a threshold. The payload is the same JSON structure as the API response. You can point it at Slack, PagerDuty, a Lambda function, or a custom endpoint:
# Register a webhook
curl -s -X POST "https://certwatch.app/webhooks" \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.slack.com/services/T/B/xxx",
"domains": ["example.com", "api.example.com"],
"alert_days": [30, 14, 7, 1]
}'
The alert_days array controls which thresholds trigger a webhook. Default is [30, 14, 7, 1] — you get a ping at 30 days, two weeks, one week, and the day before. You can tighten it to [7, 1] if you don't want the early warnings.
Bulk CSV import
If you have a list of domains to monitor, you don't have to add them one at a time:
# domains.csv: one domain per line, `domain` header optional
echo -e "domain\nexample.com\napi.example.com\nblog.example.com" | \
curl -s -X POST "https://certwatch.app/domains/csv" \
-H "X-Api-Key: your-api-key" \
--data-binary @-
Response:
{
"added": 3,
"skipped_existing": 0,
"skipped_invalid": 0,
"skipped_capacity": 0
}
The tier limit is enforced server-side — Starter allows 100 domains, Pro allows 1,000. Import a 50-domain CSV on the Starter tier and all 50 are tracked.
CI pipeline integration
The /check endpoint returns a non-200 when the cert is expired or the chain is broken. That makes it directly usable in CI:
- name: Verify cert before deploy
run: |
result=$(curl -sf "https://certwatch.app/check?domain=$DOMAIN" \
-H "X-Api-Key: $CERTWATCH_API_KEY")
days_left=$(echo "$result" | python3 -c "import sys,json; print(json.load(sys.stdin)['cert_days_left'])")
if [ "$days_left" -lt 7 ]; then
echo "::warning::Cert for $DOMAIN expires in $days_left days"
fi
This pattern works in GitHub Actions, GitLab CI, or any CI system that can run a curl command.
The rate limiting
The public endpoint uses DynamoDB with TTL for rate limiting. Each IP gets 3 requests per 60-second window. The rate limit row expires 90 seconds after creation — a modest over-shoot that prevents edge cases at the window boundary.
The paid endpoint has no rate limit, just the API key auth. Keys are stored as SHA-256 hashes in the database, so a table exfiltration doesn't expose the actual key values.
The public endpoint lives at certwatch.app/check/public. If you're checking certs manually and want automated alerts when they get close to expiry, that's what the product is for.