DisclosureLens
Developers

API documentation

Query every formally-filed breach disclosure over a REST/JSON API. Read-scoped keys are free for public-interest use.

A near-real-time, machine-readable feed of formally-filed breach disclosures worldwide — SEC Item 1.05, US state-AG notifications, HHS OCR, EU DPA decisions, and ransomware leak claims. REST, JSON, cursor-paginated. Every response carries meta.ai_assisted: true (EU AI Act Art. 50).

Base URL & authentication

All endpoints are under a single base. Send your key as a Bearer token on every request (health endpoints are the only ones that work without one).

https://api.disclosurelens.com/v1

Authorization: Bearer df_your_key_here

Keys are read-scoped, hashed at rest, and shown to you only once at creation. Create and manage them under Settings → API keys.

Quick start

curl -s "https://api.disclosurelens.com/v1/disclosures?limit=3&jurisdiction=us-ca" \
  -H "Authorization: Bearer $DL_API_KEY"

Response envelope — a data payload plus a meta block:

{
  "data": [ /* … */ ],
  "meta": {
    "cursor": { "next": "<opaque>", "prev": null },
    "ai_assisted": true
  }
}

Pagination

List endpoints return up to limit rows (1–200, default 50). Disclosures and incidents use keyset cursors — pass the value from meta.cursor.next (disclosures) or meta.next_cursor (incidents) back as ?cursor= for the next page; a null value means the last page under the default filed_desc sort. The change-feed sorts updated_asc and created_asc are cursor-paginated too; the ranking sorts (severity, ttd, confidence) and filed_asc are single-page and always return a null cursor. Threat actors use ?limit/?offset instead.

Change feed — keeping a copy in sync

To mirror the corpus, poll on updated_after — not filed_after. Filing dates belong to the regulator, and most of this corpus arrives through archive backfill, so a record's filing date is typically far older than the moment it reached us: the median gap between the two is over a year for state-AG notifications and over four years for HHS OCR. A poller keyed on filed_after would see leak-site claims and almost nothing else, and would miss backdated records permanently — they are born already behind a window that has moved past them.

updated_after covers new records and revisions (re-extraction, entity resolution, incident linkage). Each row carries record.updated_at and record.created_at; store the last value you durably wrote and pass it back on the next poll. Use created_after instead when you want a reproducible snapshot, where later revisions must not move a record into or out of your set.

# page forward through everything changed since your last sync
curl -H "Authorization: Bearer $DL_KEY" \
  "https://api.disclosurelens.com/v1/disclosures?updated_after=2026-07-30T00:00:00Z&sort=updated_asc&limit=200"

# then follow meta.cursor.next until it is null, and persist the last
# record.updated_at you saw as the watermark for the next run.

Records are delivered at least once: a row revised while you are paging reappears later in the walk under its new timestamp. Deduplicate on id.

Rate limits

The free research tier allows 60 requests per minute per key. Over the limit returns 429; the X-RateLimit-Remaining header tracks your budget. Higher throughput and signed PDF deliverables are part of the paid tier (bulk exports and webhooks are planned).

Endpoints

Disclosures — individual filings
GET/disclosuresList/filter filings (paginated).
GET/disclosures/{id}One disclosure with full extraction.
GET/disclosures/facetsFacet counts for the current filter set.
GET/disclosures/{id}/relatedCross-source siblings of the same breach.
GET/disclosures/{id}/extractionExtraction confidence + provenance.
GET/export/disclosures.ndjsonStreaming bulk export (see Bulk export).
Meta
GET/openapi.jsonMachine-readable OpenAPI spec (requires a key).
Incidents — deduplicated breaches
GET/incidentsList incidents (one per breach, across sources).
GET/incidents/{id}One incident with its member filings + timeline.
Entities — organizations
GET/entitiesSearch/list resolved organizations.
GET/entities/{id}One entity.
GET/entities/{id}/disclosuresAn entity's filing history.
GET/entities/{id}/scorecardCompliance scorecard (?window_days=365).
Threat actors
GET/threat-actorsList named actors / ransomware groups (offset paging).
GET/threat-actors/{slug}One actor with attributed incidents.
Stats — aggregate analytics
GET/stats/dailyFilings per day (?days=90).
GET/stats/jurisdictionsFilings by jurisdiction.
GET/stats/compliance_distributionOn-time vs. late notification mix.
GET/stats/late_disclosure_leaderboardSlowest notifiers.
GET/stats/time_to_disclose_percentilesDiscovery→notification percentiles.
Your account
GET/me/api-keysList your keys.
POST/me/api-keysCreate a key (secret returned once).
DELETE/me/api-keys/{id}Revoke a key.
GET/me/subscriptionsYour saved-filter email alerts.
Health — public, no key required
GET/health/sourcesPer-source ingestion health.
GET/health/extractExtraction pipeline health.
GET/health/dedupIncident-dedup health.

Filtering /disclosures

Combine any of these query params (all optional):

jurisdictione.g. us-ca, us-federal, global (canonical lowercase)
source_typesec_8k · state_ag · ocr · leak_site · press · gdpr_dpa
source_categoryclaim · report · filing · enforcement
severitylow · medium · high · critical
naics_sector2-digit NAICS, e.g. 62 (health care)
verticalrepeatable: ?vertical=healthcare&vertical=manufacturing
compliancee.g. ca_60day_late (repeatable)
cveexact CVE-YYYY-NNNN
actor_namedransomware group / actor name
searchfull-text over victim + summary
filed_after / filed_beforeISO-8601. Bounds the REGULATOR's filing date — see Change feed
updated_after / updated_beforeISO-8601. Bounds when we created or last revised the record
created_after / created_beforeISO-8601. Bounds when we first ingested the record
sortfiled_desc (default) · filed_asc · severity · ttd · confidence · updated_asc · created_asc
limit1–200 (default 50)
cursoropaque keyset cursor from meta.cursor.next

Errors

Every error — auth, rate limit, not-found, validation, paid-tier — returns the same envelope. Branch on error.code, which is stable, rather than on the human-readable message.

{ "error": { "code": "unauthorized", "message": "missing bearer token" } }
  • 401 unauthorized — missing / invalid / revoked key
  • 403 paid_tier_required — endpoint needs a paid plan
  • 404 not_found — no such resource
  • 422 validation_error — invalid parameter; adds an error.fields[] array of { field, message, type }
  • 429 rate_limited — see Retry-After

Bulk export

Paging the whole corpus through /disclosures takes hundreds of sequential requests. To take a copy, stream it instead — one JSON object per line, in exactly the same record shape the list endpoint returns:

curl -H "Authorization: Bearer $DL_KEY" \
  "https://api.disclosurelens.com/v1/export/disclosures.ndjson?updated_after=2026-01-01T00:00:00Z" \
  > corpus.ndjson

The last line is a trailer, not a record — an object under the reserved key _export carrying count, truncated, and resume_after. Always check truncated before treating a file as complete: a stream that hit the row cap, or that died mid-transfer, is otherwise indistinguishable from a finished one, because the response has already committed to 200 OK before the first row is written. To continue, pass resume_after back as updated_after.

Accepts updated_after/updated_before, source_type, source_category, jurisdiction, include_leak_site, and max_rows (default 100,000). Records are delivered at least once — deduplicate on id.

There is no CSV endpoint on purpose: the record is deeply nested, and every flattening is a lossy editorial choice we would rather leave to you. Project the columns you want, e.g. jq -r 'select(._export==null)|[.id,.victim_entity.raw_name]|@csv'.

Use & attribution

Public-interest use is free; attribution is requested when you republish. Disclosures are reported as filed, with provenance — the platform draws no legal conclusions. See the methodology and terms.

Ready? Create your API key.