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 returned
(channel, file_name, source_type) only to a USER SESSION of an
organisation holding the source_reveal feature (on by default for a
workspace, off for a client), never to an API key. Otherwise 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",
"source_type": "string",
"channel": "string",
"file_name": "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.
Retrieve keyword-based leak statistics GET
Retrieve all leak statistics for the current organization's active keywords. Returns the **same format** as `/leaks/stats` (StatsResponse), computed from keyword search results on `hash_registry` (full ingestion history, folded per hash). The rows matching active keywords (excluding the org's own monitored root domains) are fed through the same `calculate_all_statistics()` pipeline. If the organization has no active keywords, returns an empty stats response. **Parameters:** | Parameter | Default | Description | |-----------|---------|-------------| | `granularity` | `day` | Time granularity (unused in keyword stats, kept for API consistency) | | `start_date` / `end_date` | all-time | Date range filter; an explicit value narrows the window | Plus all standard DynamicFilters. Accepts `?tenant_id_override=<child_tenant_id>` for an MSSP workspace: the active keywords, excluded roots and paid-plan check then resolve from the CHILD tenant instead of the caller's own org (`tenant_id_override=all` is rejected with 400, a keyword search has no defined meaning across a whole portfolio). **Example:** ```bash GET /leaks/keyword/stats?start_date=2026-03-01&end_date=2026-03-28 ```