Retrieve every occurrence (source + file) for a specific leak hash
Retrieve every distinct detection of a deduplicated credential — one row per
(telegram_channel, file_name) pair — read directly from the tenant-routed
leaks_matched_* table (no separate occurrence store, no JOIN). The row count
EQUALS the occurrences figure shown in the /leaks/details list (same grain),
across ALL types (Combo and Stealer).
Use this after /leaks/details to drill into a credential and see each
detection. The proprietary telegram_channel and file_name are NEVER
returned: the source is replaced by a stable opaque id (opaque_source_id,
"Source #" + md5(channel)[:6]) and, when the detection carried a published
file, an opaque lot id (opaque_lot_id, md5(file_name)[:6]). Backfilled /
historical detections have no file, hence no lot. computer_name is never
selected nor returned.
date is max(upload_stealed) for that detection, i.e. when Stealed
ingested it — the same clock as the list's first_seen / last_seen, so
the two views can never contradict each other. It is deliberately NOT the
source's own publication timestamp (upload_date, the Telegram message
date), which runs hours to days ahead of ingestion and is unset on some
rows.
Parameters:
hash(required): The leak hash from the/leaks/detailsresponsetenant_id_override(optional): workspace (MSSP) only — inspect a child tenant's detail; validated against the caller's portfolio (403/404 otherwise)
Tenant isolation: the query is scoped WHERE tenant_id = <server-derived> AND hash = <param>, so a hash belonging to another tenant returns nothing.
Response:
{
"total_occurrences": 2,
"occurrences": [
{"date": "2026-06-01T10:30:00", "source": "Source #a1b2c3", "lot": "d4e5f6",
"type": "Stealer", "country": "FR", "stealer_name": "RedLine",
"software": "Chrome", "ip_address": "1.2.3.4", "machine_user": "admin",
"machine_id": "…", "protocol": "https"},
{"date": "2026-05-20T08:00:00", "source": "Source #99aa11", "lot": null,
"type": "Combo", "country": null, "stealer_name": null, "software": null,
"ip_address": null, "machine_user": null, "machine_id": null,
"protocol": null}
]
}Example:
GET /leaks/details/occurrences?hash=5d41402abc4b2a76b9719d911017c592Authorization
ApiKeyAuth API key for authentication
In: header
Query Parameters
The leak hash to look up
Response Body
application/json
application/json
curl -X GET "https://api.stealed.io/leaks/details/occurrences?hash=string"{
"total_occurrences": 0,
"occurrences": [
{
"date": "2019-08-24T14:15:22Z",
"source": "string",
"lot": "string",
"type": "string",
"country": "string",
"stealer_name": "string",
"software": "string",
"ip_address": "string",
"machine_user": "string",
"machine_id": "string",
"protocol": "string"
}
]
}{
"detail": [
{
"loc": [
"string"
],
"msg": "string",
"type": "string"
}
]
}Reveal one credential's password, and record that it was read GET
The password behind a hash, in clear, for a credential belonging to the calling organisation. Every other endpoint returns it masked. This one does not, which is why it is the one act in the product that writes to the access journal as `sensitive`: a client asking a year later who read his collaborators' passwords gets an answer with a name, a time and an IP. Scoped like every other read: a hash belonging to another organisation returns 404, never someone else's secret.
Paginated, count-sorted distinct-value breakdown for a KPI tile drill-down GET
Drill into a KPI tile (Total Users / Total Domains / Total Sources) with a server-paginated, count-sorted list of distinct values. Reuses the exact same tenant scoping as `GET /leaks/details`: results come from the tenant-routed `leaks_matched_*` table, restricted to the org's active domains, plus all standard `DynamicFilters` (dates, type, domain, not_domain, country, etc.). **Parameters:** | Parameter | Default | Description | |-----------|---------|-------------| | `field` | *required* | `username`, `domain`, or `source` | | `identifier_column` | *required* | `root_domain`, `email_domain`, `username`, or `all` | | `page` | 1 | Page number | | `page_size` | 50 | Items per page (max 200) | | `search` | - | Substring filter on the returned `value` | | `leak_mode` | `new` | `new` (first-seen in period) or `all` (any occurrence in period) -- must match the `/leaks/stats` KPI tile being drilled into | `domain` only counts round-trippable hostnames (drops base64 "email-blob" parsing garbage), matching the Insights domain pie. `source` emits an opaque label (`Source-<md5 prefix>`) -- the real channel name never leaves the API. **Response:** ```json { "data": [{"value": "admin@example.com", "count": 42}], "total": 118, "page": 1, "page_size": 50, "total_pages": 3 } ``` `total` is the number of distinct `field` values matching the filters -- the same figure as the corresponding `/leaks/stats` KPI (`unique_usernames` / `unique_domains` / `unique_sources`). **Example:** ```bash GET /leaks/breakdown?field=domain&identifier_column=root_domain&page=1&page_size=50 ```