API.

Programmatic access to the ctlogs.dev certificate index. JSON over HTTPS.

Anonymous use is limited to one concurrent request per client and is served from a queue shared by all keyless traffic — for an API key and higher limits write to [email protected].

Basics

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: *).

Authentication & limits

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'

Performance

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.

Endpoints

Exact domain lookup

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'

Subdomain lookup

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'

Hostname list (subdomain finder)

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
}

Organization search

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'

Serial lookup

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'

Public key (SPKI) search

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'

Certificate detail

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.

Pagination

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'

Response reference

Search envelope

{
  "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
}
FieldMeaning
idStable certificate id; feed it to /v1/cert/{id} or https://ctlogs.dev/cert/{id}.
matchThe 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_afterValidity window, RFC 3339 UTC.
serial_hexSerial number, minimal big-endian hex.
issuer, key_algo, san_countIssuer CN, public-key algorithm (“RSA 2048”, “ECDSA P-256”), SAN count. Domain, serial and SPKI methods only.
subject_cn, domains, domains_moreSubject CN, a sample of the certificate’s domains and how many more it has. /v1/org only.

Certificate detail

{
  "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

Errors are JSON with the HTTP status code set:

{"error": "you already have a request in progress"}
StatusMeaning
400Malformed query — bad hostname, non-hex serial or hash, invalid cursor.
401The presented API key is unknown or revoked.
403The method requires an API key (/v1/spki) and none was presented.
404No certificate with that id or fingerprint.
429Anonymous concurrency limit hit (retry after your previous request finishes, or use an API key), or a keyed monthly quota exhausted.
503Server busy — retry with backoff.