Programmatic access to the ctlogs.dev certificate index. JSON over HTTPS.
Base URL is https://api.ctlogs.dev. All endpoints are GET, all responses are JSON (UTF-8). Search results are sorted newest-first by not_before and paginated at up to 100 rows per page — see pagination. Path parameters must be URL-encoded. Cross-origin requests are allowed (Access-Control-Allow-Origin: *).
Anonymous requests are limited to one concurrent request per client and are queued in a shared pool with all other keyless traffic — under load they are served as capacity allows. An API key lifts the concurrency cap — pass it as an Authorization: Bearer <key> header or a ?key=<key> query parameter. Keys come from the account cabinet (sign in with GitHub or Google): a free key with 10 000 API credits per month for non-commercial use — testing, research, personal and open-source projects — and paid plans for commercial use or more quota, from $50 per month; custom arrangements at [email protected]. Requests presenting an unknown or revoked key are rejected with 401 {"error": "invalid api key"}.
A key may carry a monthly quota of API credits (calendar month, UTC). Organization search (/v1/org) and public-key search (/v1/spki) cost 10 credits per request; every other endpoint costs 1 credit — org search scans the full organization dictionary and SPKI search probes every storage part, each an order of magnitude more expensive to serve than the indexed lookups. Responses to quota-limited keys include X-RateLimit-Limit and X-RateLimit-Remaining headers (in credits); once the quota is spent, requests return 429 until the first of the next month.
curl -H 'Authorization: Bearer YOUR_KEY' 'https://api.ctlogs.dev/v1/domain/example.com'
Server-side latency measured on 2026-09-03 over 1 212 real queries from the access log (cold result cache, one request in flight, production load): domain 26 / 31 ms, subdomains 46 / 54 ms, hosts 41 / 49 ms, serial 43 / 51 ms, cert 109 / 182 ms, org 352 / 516 ms (p50 / p90). End-to-end from Europe through Cloudflare adds roughly 40–60 ms. Method and raw numbers: the measurement is repeated after major changes.
GET/v1/domain/{hostname}
Certificates whose subject CN or SAN dNSName equals {hostname} exactly, as logged. A wildcard certificate for *.example.com is a distinct name and is not returned for sub.example.com — use the subdomains search to see both.
curl 'https://api.ctlogs.dev/v1/domain/example.com'
GET/v1/subdomains/{domain}
Certificates for {domain} itself and every name under it — www.example.com, *.example.com, a.b.example.com, … Equivalent to the *.example.com query on the website.
curl 'https://api.ctlogs.dev/v1/subdomains/example.com'
GET/v1/hosts/{domain}
Every distinct hostname that ever appeared as a certificate name under {domain} (the apex itself included, wildcards such as *.example.com inline), one row per hostname instead of one per certificate: certs (how many certificates named it), first_seen / last_seen (first and latest certificate), last_not_after (expiry of the latest one), and the live answer of ctlogs.dev’s own resolver — dns (ok, nxdomain, nodata, servfail, parked, …) with a records when resolved. Rows come in reversed-name order (example.com, *.example.com, a.example.com, www.example.com, …), 100 per page with the usual has_next / next_cursor; the website shows the first page only, paging needs an API key.
curl 'https://api.ctlogs.dev/v1/hosts/example.com'
{
"hosts": [
{"host": "example.com", "certs": 41, "first_seen": "2015-03-12T09:14:02Z",
"last_seen": "2026-08-30T11:02:57Z", "last_not_after": "2026-11-28T23:59:59Z",
"dns": "ok", "a": ["93.184.216.34"]},
{"host": "*.example.com", "certs": 6, "first_seen": "2019-01-08T17:40:11Z",
"last_seen": "2025-12-01T08:30:15Z", "last_not_after": "2026-03-01T08:30:14Z"}
],
"has_next": false,
"next_cursor": "",
"duration_ms": 47
}
GET/v1/org?q={name}
Certificates by subject organization (O=). Queries of 4+ characters match case-insensitively anywhere in the name (paypal finds “PayPal, Inc.”); shorter queries must match the full name exactly (so EY still resolves). Rows from this method carry subject_cn and a domains sample instead of issuer/key columns. Costs 10 API credits per request (see limits).
curl 'https://api.ctlogs.dev/v1/org?q=paypal'
GET/v1/serial/{serial}
Finds certificates by serial number: 4–42 hex characters, separators (:, -, spaces) and leading zero bytes tolerated. Serials are not globally unique, so one serial can return certificates from several CAs.
curl 'https://api.ctlogs.dev/v1/serial/0624d0ab311558780b7d5213b9631831'
GET/v1/spki/{hash}
Requires an API key — this search is disk-heavy, so it is not served anonymously; request a key at [email protected]. Finds every certificate sharing one public key: {hash} is the SHA-256 of the SubjectPublicKeyInfo (64 hex characters, separators tolerated) — the value OpenSSL prints as openssl x509 -pubkey | openssl pkey -pubin -outform DER | sha256sum. Useful for tracking key reuse across certificates and CAs.
curl -H 'Authorization: Bearer YOUR_KEY' \ 'https://api.ctlogs.dev/v1/spki/0856752f53199a673dcc955c137fe1f5b105a180528acb320bb3eddf15103a9b'
GET/v1/cert/{id}
The full record for one certificate: subject and issuer DNs, complete SAN list, validity, key and signature info, policy OIDs, precert/final fingerprints, and every CT log sighting with entry index and SCT timestamp. {id} is the id value returned by the search methods above, or a 64-hex SHA-256 fingerprint (of the precertificate or the final certificate, separators tolerated).
precert_sha256 is the SHA-256 of the precertificate DER exactly as logged (the value crt.sh shows for the precertificate), final_sha256 that of the final certificate; either can be empty when that leaf form was not logged or its hash was not retained.
curl 'https://api.ctlogs.dev/v1/cert/000000b50624d0ab311558780b7d5213b9631831'
While the raw body is archived, it can be downloaded by appending /final.pem, /final.der, /precert.pem or /precert.der.
Search responses return up to 100 rows plus has_next and next_cursor. To fetch the next page, repeat the same request with ?after=<next_cursor> (cursor= is accepted as an alias). The cursor is an opaque token — do not parse it.
curl 'https://api.ctlogs.dev/v1/subdomains/example.com?after=NEXT_CURSOR'
{
"rows": [
{
"id": "000000b50624d0ab311558780b7d5213b9631831",
"match": "example.com",
"not_before": "2026-07-29T22:10:08Z",
"not_after": "2026-10-27T22:17:21Z",
"serial_hex": "0624d0ab311558780b7d5213b9631831",
"issuer": "DigiCert Global G3 TLS ECC SHA384 2020 CA1",
"key_algo": "ECDSA P-256",
"san_count": 9
}
],
"has_next": true,
"next_cursor": "MTc1MzgyMDIwOTAwMDAwMDAwMHwxODV8…",
"duration_ms": 132
}
| Field | Meaning |
|---|---|
| id | Stable certificate id; feed it to /v1/cert/{id} or https://ctlogs.dev/cert/{id}. |
| match | The name that matched: a SAN dNSName for domain methods, the subject organization for /v1/org, the subject CN for /v1/serial and /v1/spki. |
| not_before, not_after | Validity window, RFC 3339 UTC. |
| serial_hex | Serial number, minimal big-endian hex. |
| issuer, key_algo, san_count | Issuer CN, public-key algorithm (“RSA 2048”, “ECDSA P-256”), SAN count. Domain, serial and SPKI methods only. |
| subject_cn, domains, domains_more | Subject CN, a sample of the certificate’s domains and how many more it has. /v1/org only. |
{
"id": "000000b50624d0ab311558780b7d5213b9631831",
"issuer_dn": "C=US, O=DigiCert Inc, CN=DigiCert Global G3 …",
"subject_dn": "C=US, ST=California, O=Internet Corporation …",
"subject_cn": "www.example.org",
"serial_hex": "0624d0ab311558780b7d5213b9631831",
"san_dns": ["example.com", "www.example.com", "…"],
"not_before": "2026-07-29T22:10:08Z",
"not_after": "2026-10-27T22:17:21Z",
"sig_algo": "ECDSA-SHA384",
"pubkey_sha256": "b2b0fe1b…",
"is_ca": false,
"ext_key_usage": ["server_auth", "client_auth"],
"policy_oids": ["2.23.140.1.2.2"],
"precert_sha256": "56b93a54…",
"final_sha256": "91a44b23…",
"precert_first_seen": "2026-07-29T22:17:21Z",
"final_first_seen": "2026-07-29T22:19:03Z",
"duration_ms": 41,
"sightings": [
{
"log_id": "0DE1F2302BD30DC140621209EA552EFC47747CB1D7E930EF0E421EB47E4EAA34",
"log_url": "https://ct.googleapis.com/logs/us1/argon2026h2/",
"operator": "Google",
"entry_idx": 480932871,
"is_precert": true,
"sct_ts": "2026-07-29T22:17:21Z"
}
]
}
precert_first_seen / final_first_seen are the earliest SCT timestamps among the certificate’s sightings, per leaf form; a form that was never logged is null.
Errors are JSON with the HTTP status code set:
{"error": "you already have a request in progress"}
| Status | Meaning |
|---|---|
| 400 | Malformed query — bad hostname, non-hex serial or hash, invalid cursor. |
| 401 | The presented API key is unknown or revoked. |
| 403 | The method requires an API key (/v1/spki) and none was presented. |
| 404 | No certificate with that id or fingerprint. |
| 429 | Anonymous concurrency limit hit (retry after your previous request finishes, or use an API key), or a keyed monthly quota exhausted. |
| 503 | Server busy — retry with backoff. |