List all certificates for a domain
Returns paginated certificates associated with the exact domain name. Use valid_only=true to filter to currently-valid certificates.
Pagination signals (task #852): every response carries a has_more boolean — true when at least one more page of rows exists — in BOTH offset (page) mode and cursor mode. next_cursor is a cursor-mode-only field: it is minted only for requests that included the cursor= parameter (pass cursor='*' to start), because cursor pages are keyset-ordered by the monotone internal id while offset pages are ordered by not_before — a cursor cannot be minted from an offset page. Offset-mode callers should use has_more (or total/limit) to detect further pages.
Precert flag (GH #253): every result row carries an is_precert boolean — True for CT precertificates (poison-extension entries), False for final leaves — so callers can classify or count precerts directly from list rows without per-certificate detail roundtrips. Precert and final-leaf twins have different SHA-256 hashes and are returned as separate rows (no dedup across the pair). This raw twin-per-row behavior is intentional and unchanged by GH #262 — this is the complete observation log; use get_expiring_certs (which collapses twin pairs by default) or filter on is_precert client-side when you need distinct-certificate counts.
Self-healing for cold domains (task #113 / GH #266): if CertIndex has not indexed this domain yet, the response contains zero results plus a backfill_status sentinel. When backfill_status == 'in_progress' wait retry_after_seconds seconds and call this tool again. Use eta_basis to understand the projection: 'live' = measured entry-throughput ETA; 'jobs_coarse' = approximate estimate from job-completion rate (treat as lower-precision); 'unknown' = no projection yet — terminal_at_latest (ISO-8601 UTC) and seconds_until_terminal are emitted so you know the latest point at which the run is guaranteed to be closed out — stop polling then at the latest. in_progress_over_budget (no retry_after_seconds) means the projection — live or coarse — exceeds the interactive budget: do NOT poll in a loop; retry in a later session (see hint). When backfill_status is previously_completed, failed, not_triggered, refused_over_budget, or domain_not_found, do NOT retry — the result will not change. domain_not_found means the registrable domain does not exist in DNS — check the spelling. refused_over_budget means the domain is valid but too large to backfill inline (read the hint); it is NOT a 'no such domain' answer.
Progress reporting: while an on-demand backfill is in flight the response carries a top-level progress block, and the coverage block carries a subset of the SAME snapshot under coverage.progress. Both are rendered from one snapshot under one emission rule, so shared quantities always agree. Shard-scan counters that carry no signal are OMITTED, not reported as zeros: an ABSENT shard_scan_progress_pct / shard_scan_entries_scanned means 'not measurable yet', not 'no progress'. shard_scan_progress_pct falls back to the jobs ratio when the entry-level aggregate is unavailable, so it can never read 0.0 while shard_scan_jobs_done climbs, and shard_scan_status can never read pending next to a non-zero shard_scan_jobs_done.
Coverage confidence: responses carry an advisory coverage block ({confidence, basis, hint, as_of}). coverage.basis is EXACTLY one of the values below — never prefix-match domain_backfill_completed: that prefix spans one trustworthy verdict, two scope-limited ones and an empty-inventory one, so mapping the prefix onto 'the inventory is exhaustive' reads the WORST case as a confirmed-complete answer. Match the exact string. domain_backfill_completed_full (HIGH — a backfill completed having walked the FULL certificate-transparency history and found records; the inventory is exhaustive as far as CertIndex can enumerate), domain_backfill_completed_active_only (RETAINED — no longer emitted), domain_backfill_completed_active_since_ingestion (MEDIUM — the free pass enumerated names since CertIndex's ingestion horizon; coverage.total_is_lower_bound is true with lower_bound_reason: ingestion_horizon), domain_backfill_completed_scope_unknown (MEDIUM — the pass finished but did not establish what it reached: a lane truncated its enumeration, or the row predates scope recording; the inventory is a LOWER BOUND), domain_backfill_completed (RETIRED — no longer emitted, but payloads minted by an older fleet still carry it. It could not distinguish the three cases above, so do NOT read it as high-confidence exhaustive), domain_backfill_completed_empty (LOW — backfill completed but enumerated zero records; treat as UNKNOWN, NOT as a confirmed empty surface — absence of records is not evidence of absence of certificates), domain_backfill_never_ran (LOW — the row reads terminal with zero records but the scan never actually executed, i.e. a dispatch failure; weaker evidence still than completed-empty — re-trigger), domain_backfill_in_progress (LOW — backfill running, inventory growing; re-query later; total_is_lower_bound is true and lower_bound_reason is backfill_in_progress), domain_backfill_stalled (LOW — the backfill stopped advancing; TERMINAL, stop polling — an operator re-trigger or a paid deep-historical run forces a fresh attempt), domain_backfill_failed (LOW — historical backfill failed; the historical inventory may be incomplete, but live CT-log ingestion continues so newly issued certificates still appear), inherited_from_apex_backfill (confidence varies — no backfill for this exact name, but a backfill for the ancestor named in coverage.inherited_from covers it; backfills are enumerated apex-wide, so a subdomain inherits its apex's verdict rather than falling back to the global signal), global_active_coverage (confidence varies — no per-domain or ancestor backfill history; confidence derives from global index coverage), or no_signal (UNKNOWN — no signal available). The block also carries as_of (UTC timestamp of the verdict).
Completeness scope (GH #300 / #304 / #536): every completed-basis block, and the global_active_coverage / no_signal fallbacks for a name with no domain_backfills row, also carries coverage.enumeration_scope — full, active_only or unknown, i.e. what the pass that produced this inventory could actually reach — plus coverage.scope_limited (bool; true for anything but full) and coverage.total_is_lower_bound (bool). The NESTED coverage.total_is_lower_bound is true when the scan is incomplete WITHIN the advertised product scope (enumeration_scope=unknown, an unfinished pass, or no domain_backfills row — a never-scanned name). A zero total with total_is_lower_bound: true means "we cannot tell"; 0 with total_is_lower_bound: false means "none exist within the advertised enumeration scope". A completed active_only pass is exact for currently-valid names: missing expired-only names are outside this product (read enumeration_scope / scope_limited), not a coverage floor. The TOP-LEVEL field of the same name is the OR of that nested flag and this response's own row-scan cap, so the top-level flag can be true while the nested one is false — never the reverse. This envelope also carries top-level total_is_lower_bound and lower_bound_reason (omitted when the total is exact). An in-progress backfill always floors the total (lower_bound_reason=backfill_in_progress); re-query later. Completed active_only does not.
Verdict precedence: the verdict tracks the live backfill record — it changes as the scan progresses — with fixed precedence: in-progress (any active scan always outranks completed-empty — a running scan and a completed-empty verdict carry opposite instructions, so they must never coexist) > completed (non-empty) > completed-with-follow-up-pass-running (with sub_verdicts: {active: completed, historical: in_progress} — never a silent high-to-low regression) > completed-empty (low, terminal only) > failed > global coverage. Field precedence when retry_after_seconds is present in the sentinel: the backfill is still running; coverage.confidence reflects the state of the data available NOW.
Request
- Base URL: https://api.ctindex.io
- URL: https://api.ctindex.io/mcp-api/get_domain_certificates/{domain}
- Auth: one of:
- API key in header X-API-Key
- HTTP bearer
Path parameters
Query parameters
When true, return only non-expired certificates
When true, attach RDAP + DNS + ASN/hosting context for the domain under enrichment (task #501).
When true, attach derived attribution signals under enrichment.signals: issuer_diversity_score, wildcard_breadth, third_party_vendors (from the domain's TXT/SPF inventory), and weak_crypto_reasons. Implies enrichment computation. Requires a paid plan (Pro or higher); the free tier receives HTTP 403 tier_not_entitled.
Opaque keyset cursor (task #635 / 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. In cursor mode limit may go up to the caller's tier ceiling (base 200 free, 1000 paid) instead of the offset-mode maximum of 50. 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.
When false, cold domains return a 'not_indexed' sentinel immediately without triggering a background backfill. Default true preserves the existing self-healing behaviour.
Cookies
Response
Successful Response
Changes
No recorded changes to this operation across all 1 revision of this API.