---
title: "Get Domain Certificates"
method: GET
path: "/v1/domains/{domain}/certificates"
tags: ["domains"]
---

# Get Domain Certificates

`GET /v1/domains/{domain}/certificates`

## Request

- Base URL: `https://api.ctindex.io`
- URL: `https://api.ctindex.io/v1/domains/{domain}/certificates`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Return all certificates associated with the given domain name.

`valid_only=true` is a convenience alias for `expired=false`.
When both `expired` and `valid_only` are supplied, `expired` takes precedence.

Every row carries `first_seen` (earliest CT observation) and `last_seen`
(latest CT observation — **observation-based**: a stale value means the
cert rolled out of our observation window, not that it was revoked).

## Path parameters

- `domain` string, required

## Query parameters

- `page` integer
- `limit` integer
- `cursor` string, nullable: Opaque keyset cursor (task #615 / GH #263). Pass ``cursor='*'`` (the coalescing-proof start token) to start a cursor enumeration, then the ``next_cursor`` from each response until it is ``null`` (cursor-mode pages ALWAYS carry the ``next_cursor`` key; an explicit ``null`` marks the terminal page). Cursor pages are ordered newest-first by observation (monotone internal id) and are stable against concurrent inserts — new certificates never shift, repeat, or drop rows on later pages. When ``cursor`` is supplied, ``page`` is ignored. Omit ``cursor`` entirely for the classic offset pagination (unchanged). Legacy: ``cursor=`` (empty string) also starts enumeration but may be coalesced to null by some transports.
- `expired` boolean, nullable: true → only expired certs, false → only currently valid, omit → all
- `valid_only` boolean, nullable: Convenience alias: true is equivalent to expired=false (valid certs only)
- `include_enrichment` boolean: When true, attach RDAP registration + live DNS + ASN/hosting context for the domain under ``enrichment`` (task #501). Opt-in; a slow/failed source degrades to a partial block and never fails the lookup.
- `include_signals` boolean: When true, attach derived attribution signals under ``enrichment.signals``: ``issuer_diversity_score`` (0.0 single CA → 1.0 at 5+ distinct CAs), ``wildcard_breadth`` (distinct wildcard patterns, with ``wildcard_names``), ``third_party_vendors`` (SaaS vendors inferred from the domain's TXT/SPF inventory), and ``weak_crypto_reasons`` (explicit reasons, e.g. RSA < 2048 or SHA-1 signatures). Implies enrichment computation. Requires a paid plan (Pro or higher); the free tier receives HTTP 403 ``tier_not_entitled``.
- `as_of` string, nullable: ISO-8601 datetime — point-in-time view (task #613): only certificates first observed at or before ``as_of`` are returned, and each row's ``last_seen`` is capped at ``as_of``. Inventory is observation-based (CT sightings), so this is 'what CertIndex knew then', not what was live then.
- `logged_at_after` string, nullable: ISO-8601 datetime — only certificates whose CT-log timestamp (logged_at, RFC 6962 SCT leaf time) is on or after this instant. This is the buyer-visible freshness probe (GH #308 Decision 4): domain listing, not a SHA-256 lookup. Rows with logged_at NULL are excluded by the predicate.
- `logged_at_before` string, nullable: ISO-8601 datetime — only certificates whose logged_at is on or before this instant. See logged_at_after.

## Cookies

- `ctix_session` string, nullable

## Response `200`

Successful Response

- DomainCertResponse
  - `domain` string, required
  - `total` integer, required
  - `page` integer, required
  - `limit` integer, required
  - `results` CertificateSummary[], required
    - `sha256` string, required
    - `subject_cn` string, nullable
    - `subject_o` string, nullable
    - `sans` string[]
    - `issuer_cn` string, nullable
    - `issuer_o` string, nullable
    - `not_before` string, date-time, nullable
    - `not_after` string, date-time, nullable
    - `is_wildcard` boolean
    - `is_precert` boolean: True when this row is a CT precertificate (poison-extension log entry), False for a final leaf certificate. Precert and final-leaf twins have different SHA-256 hashes and are returned as separate rows; present on every list and detail row. Exception (GH #262): get_expiring_certs collapses twin pairs to the final leaf by default.
    - `precert_sha256` string, nullable: SHA-256 fingerprint of the CT precertificate twin that was collapsed into this final-leaf row (get_expiring_certs default twin collapsing, GH #262). Omitted when no twin was collapsed.
    - `key_type` string, nullable
    - `key_size` integer, nullable
    - `signature_algorithm` string, nullable
    - `is_weak_crypto` boolean, nullable
    - `key_fields_source` string, nullable
    - `first_seen` string, date-time, nullable: When CertIndex wrote this certificate to its database (batch-write timestamp). Multiple unrelated certificates often share an identical first_seen within the same ingestion batch. It is not the CT-log append time and is never substituted for a missing logged_at. Compare with logged_at to measure detection latency.
    - `logged_at` string, date-time, nullable: Timestamp from the CT log entry itself (MerkleTreeLeaf TimestampedEntry.timestamp, RFC 6962 §3.4) — when the log committed this certificate, NOT when CertIndex ingested it. Null for rows indexed before this field shipped, for crt.sh-sourced rows (reconciled_from_crtsh=true), and for any path that could not supply it. A null value means the CT-log timestamp is unavailable for that row; it does not mean the certificate was observed at first_seen, and first_seen is never synthesized into logged_at. Compare with first_seen (CertIndex write time) to measure detection latency. See logged_at_coverage disclosure block.
    - `reconciled_from_crtsh` boolean, nullable
    - `last_seen` string, date-time, nullable
    - `match_class` string, nullable: Attribution class when match_class_filter was supplied on search: first_party, lookalike_candidate, third_party_integration, substring_collision, or shared_hosting. Null on unfiltered search.
    - `match_reason` string, nullable: Semicolon-separated rule=value audit string for match_class. Null on unfiltered search.
    - `from_cold_tier` boolean, nullable: True when this certificate was served exclusively from the Tier 1 cold ClickHouse index (historical data not present in the hot Neon index). Absent/null for certificates served from the primary index. Cold-sourced rows carry key_fields_source='unavailable' and no raw_pem.
  - `has_more` boolean
  - `next_cursor` string, nullable
  - `enrichment` DomainEnrichmentOutput: RDAP + DNS + ASN context for a single domain (task #501).
    - `domain` string, required
    - `registration` EnrichmentRegistration
      - `registrar` string, nullable
      - `created` string, nullable
      - `updated` string, nullable
      - `expires` string, nullable
      - `status` string[]
      - `nameservers` string[]
    - `dns` EnrichmentDNS
      - `a` string[]
      - `aaaa` string[]
      - `mx` EnrichmentMX[]
        - `preference` integer, required
        - `exchange` string, required
      - `ns` string[]
      - `txt` string[]
    - `asn` EnrichmentASN[]
      - `ip` string, required
      - `asn` integer, nullable
      - `prefix` string, nullable
      - `country` string, nullable
      - `network_owner` string, nullable
    - `signals` EnrichmentSignals: Derived attribution signals for a domain. Attached under ``enrichment.signals`` only when the caller passes ``include_signals=true`` AND holds a paid plan (Pro or higher — the free tier gets a 403 ``tier_not_entitled``). Signals are derived from the domain's currently-valid certificates and TXT inventory: issuer diversity, wildcard breadth, third-party vendor fingerprints, and explicit weak-crypto reasons.
      - `issuer_diversity_score` number
      - `distinct_issuers` integer
      - `issuers` string[]
      - `wildcard_breadth` integer
      - `wildcard_names` string[]
      - `third_party_vendors` ThirdPartyVendor[]
        - `vendor` string, required
        - `source` string, required
        - `evidence` string, nullable
      - `weak_crypto_reasons` string[]
      - `certs_considered` integer
      - `certs_capped` boolean
      - `partial` boolean
      - `errors` string, nullable
    - `fetched_at` string, nullable
    - `partial` boolean
    - `errors` object, nullable
    - `cached` boolean
  - `backfill_status` string, nullable
  - `retry_after_seconds` integer, nullable
  - `hint` string, nullable
  - `progress` object, nullable
  - `typical_eta_seconds` integer, nullable
  - `eta_seconds` integer, nullable
  - `eta_basis` string, nullable
  - `coverage` object, nullable
  - `total_is_lower_bound` boolean: True when `total` is a FLOOR rather than an exact count. An in-progress backfill (`lower_bound_reason=backfill_in_progress`) always sets this — the inventory is still growing. Other causes match the subdomain envelope: `coverage_scope` (scan did not finish or never ran), `query_budget` (this page did not fit). Completed `active_only` is exact within that window and does NOT set this flag.
  - `lower_bound_reason` string, nullable: Why `total` is a lower bound. `backfill_in_progress` = an on-demand backfill is still running; re-query later. `coverage_scope` = the scan did not finish within the advertised product scope, did not establish what it reached, or never ran; retrying returns the identical count. `query_budget` = this page did not fit; repaginate. `query_budget_and_backfill_in_progress` = both truncated page and running backfill. **Absent when `total_is_lower_bound` is false.**
  - `cold_tier_supplement_status` string, nullable: Tier 1 cold exact-name supplement for this page (GH #1462). `ok` = cold answered or confirmed no extra rows within the latency budget; `unavailable` = configured but unreachable (timeout, breaker, or misconfigured URL); `pending` = still in flight when the response was assembled. Omitted when cold was not consulted (filters on) or `TIER1_COLD_QUERY_URL` unset.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/ctindex/apis/certindex-api.md) · [All operations](https://skmtc.dev/ctindex/apis/certindex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ctindex/apis/certindex-api/revisions/d5bc0ec85bc9?raw)
