Retrieve paginated leak details with optional search

GET
/leaks/details

Retrieve paginated, deduplicated leak details for the current organization.

Results are deduplicated by hash: each unique credential (username + password + domain) appears once, with aggregated metadata from all occurrences.

Specific Parameters:

ParameterDefaultDescription
identifier_columnrequiredroot_domain, email_domain, or username
page1Page number
page_size50Items per page (max: 200)
search-Full-text search across username, host, domain, email_domain, root_domain, ip_address, computer_name, machine_user
sort_bysource_countSort order: source_count (most seen first) or last_seen (most recent first)
min_occurrences-Only return credentials seen in N+ distinct sources

Plus all standard filters (see API description above).

Response Fields:

FieldTypeDescription
usernamestringUsername or email. The local part (before the @) is not returned separately, derive it client-side if needed
passwordstringMasked password: first and last character, one * per character in between (starred whole under 6 characters). See unmask for the clear value
typesstring[]All distinct leak types for this credential, e.g. ["Combo", "Stealer"]. A credential found in both Combo dumps and Stealer logs will have both values.
typestringDeprecated (flagged in the schema). Single type value, the most recent ingestion's. Use types.
last_seendatetimeMost recent detection date (aggregated max(upload_stealed) across all occurrences)
upload_datedatetimeDeprecated (flagged in the schema). Source file publication date. Duplicates last_seen on the default per-hash read; use last_seen
log_datedatetimeInfostealer log creation date, as declared by the malware: sparse and partly bogus (see the note below). Representative value
first_seendatetimeEarliest detection date (min(first_seen), the credential's global first discovery, NOT min(upload_stealed): it can predate this tenant's earliest occurrence)
source_countintDeprecated (flagged in the schema). Legacy proxy metric uniq(file_name, stealer_name, country). Prefer sources/occurrences/files.
sourcesintNumber of distinct sources reporting this credential (uniqExactIf(telegram_channel), floored by the backfill HLL snapshot and by 1), from leaks_matched
occurrencesintNumber of distinct (source, file) detections all-time (uniqExact((telegram_channel, file_name))) for this credential
filesintNumber of distinct named files (uniqExactIf(file_name, going-forward)) this credential was found in
domainstringAssociated domain
hoststringFull URL or host
email_domainstringEmail domain part
root_domainstringRoot domain
countrystringISO 2-letter country code. Representative value (see note below)
stealer_namestringStealer family (RedLine, Raccoon, etc.). Representative value
softwarestringBrowser/app. Representative value
protocolstringProtocol (https, android, ...). Representative value
ip_addressstringVictim IP address. Representative value
computer_namestringMachine name. Representative value
machine_idstringMachine id. Representative value
machine_userstringMachine user account. Representative value
hardware_idstringHardware id. Representative value
log_datedatetimeDate of the infostealer log. Representative value
countriesstring[]EVERY country this credential was seen from
stealersstring[]EVERY stealer family
softwaresstring[]EVERY browser/app
protocolsstring[]EVERY protocol
hostsstring[]EVERY host, normalised to a bare hostname
ip_addressesstring[]EVERY victim IP
machine_usersstring[]EVERY machine user account
computer_namesstring[]EVERY machine name
machine_idsstring[]EVERY machine id
hardware_idsstring[]EVERY hardware id
hashstringUnique deduplication hash

Note: Results are deduplicated by hash. The raw upload_stealed column is not returned directly. Instead, first_seen and last_seen are computed as min(upload_stealed) and max(upload_stealed) across all occurrences of each credential.

About log_date. It is written by the malware inside the stealer log, not by us: only ~0.2% of raw occurrences carry one (~44% of credentials once aggregated), and a few are dated in the future. Treat it as indicative.

Representative values vs sets. One credential (hash) is typically seen in several infostealer logs, and the machine-level values can differ between them. Each such field is returned twice: the singular field (software, country, ip_address, ...) carries ONE representative value, the most recent occurrence's, and the plural field (softwares, countries, ip_addresses, ...) carries the deduplicated set of EVERY value seen across all occurrences. Empty values are dropped, so a Combo-only credential returns [] and never [""]. A credential seen in three logs from three countries returns "country": "FR" and "countries": ["FR", "MX", "ID"].

These sets are materialised per credential in the read model, so they cost nothing extra to return. What they do NOT give is the pairing between values (which IP went with which browser, on which date): for that, call GET /leaks/details/occurrences?hash=..., which returns every (source, file) detection with its own machine metadata.

Response Format:

{
  "data": [
    {
      "username": "user@example.com",
      "password": "p****d",
      "types": ["Stealer"],
      "type": "Stealer",
      "first_seen": "2025-01-15T08:30:00",
      "last_seen": "2025-03-01T12:00:00",
      "host": "login.example.com",
      "domain": "login.example.com",
      "protocol": "https",
      "email_domain": "example.com",
      "root_domain": "example.com",
      "upload_date": "2025-02-28T10:00:00",
      "log_date": "2025-02-27T14:00:00",
      "machine_id": "DESKTOP-ABC1234",
      "computer_name": "LAPTOP-XYZ",
      "hardware_id": "hwid-1234-5678",
      "machine_user": "john.doe",
      "ip_address": "192.168.1.10",
      "country": "FR",
      "software": "chrome, profile: 0",
      "stealer_name": "RedLine",
      "countries": ["FR", "BE"],
      "stealers": ["RedLine", "Lumma"],
      "softwares": ["chrome, profile: 0", "Opera GX, Profile: 0"],
      "protocols": ["https"],
      "hosts": ["login.example.com"],
      "ip_addresses": ["192.168.1.10", "10.0.0.4"],
      "machine_users": ["john.doe"],
      "computer_names": ["LAPTOP-XYZ"],
      "machine_ids": ["DESKTOP-ABC1234"],
      "hardware_ids": ["hwid-1234-5678"],
      "hash": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
      "source_count": 3,
      "sources": 2,
      "occurrences": 3,
      "files": 3
    }
  ],
  "total": 12345,
  "page": 1,
  "page_size": 50,
  "total_pages": 247
}

Examples:

# Basic paginated request
GET /leaks/details?identifier_column=root_domain&page=1&page_size=50

# Sort by most recent
GET /leaks/details?identifier_column=root_domain&sort_by=last_seen

# Sort by number of distinct sources (leaks_matched-derived)
GET /leaks/details?identifier_column=root_domain&sort_by=sources

# Only credentials seen 3+ times
GET /leaks/details?identifier_column=root_domain&min_occurrences=3

# Full-text search with filters
GET /leaks/details?identifier_column=root_domain&search=john&type=Stealer&country=FR

# Custom date range
GET /leaks/details?identifier_column=root_domain&start_date=2024-01-01&end_date=2024-12-31&page_size=200

Pagination (retrieve all results):

# Page 1
GET /leaks/details?identifier_column=root_domain&page=1&page_size=200
# Page 2
GET /leaks/details?identifier_column=root_domain&page=2&page_size=200
# ... continue until page >= total_pages

Authorization

ApiKeyAuth
X-API-Key<token>

API key for authentication

In: header

Query Parameters

identifier_column*Identifier Column

Column to use for identifying the domain. Use 'all' for the cockpit cross-scope view (External + Internal union, dedup by hash).

Value in"root_domain" | "email_domain" | "username" | "all"
page?Page

Page number

Default1
Range1 <= value
page_size?Page Size

Items per page (max 200)

Default50
Range1 <= value <= 200
search?Search

Search term for full-text filtering

Lengthlength <= 200
sort_by?Sort By

Column to sort on. source_count is the legacy default (most-seen first). Other supported columns: last_seen, first_seen, username, host, sources (distinct sources from leaks_matched), occurrences (distinct source+file detections).

Default"source_count"
Value in"source_count" | "last_seen" | "first_seen" | "username" | "host" | "sources" | "occurrences"
sort_dir?Sort Dir

Sort direction: desc (default) or asc.

Default"desc"
Value in"asc" | "desc"
scope?array<>

Multi-select scope filter (repeat the query param for several values). external/internal filter on match_type (root_domain / email_domain) and only apply to the leaks_matched_* tables — raw leaks ignores them. stealer/combo filter on type. Within each pair, selecting both is treated as no filter (the union is the full set). An empty list means no scope filter.

leak_mode?Leak Mode

Leak counting mode: 'new' counts only credentials first discovered in the period, 'all' counts any credential with at least one occurrence in the period.

Default"new"
Value in"new" | "all"
preview_domain?Preview Domain

Workspace-only: when set together with tenant_id_override, force the redacted preview path (querying leaks_history) scoped to a single domain. Used to preview a pending domain BEFORE running the backfill.

Lengthlength <= 253
unmask?Unmask

Return passwords in CLEAR instead of masked. Three conditions, all required: an API key must carry the leaks:secrets scope (a JWT caller must hold the org ADMIN role instead -- a member reads what happens, he does not walk out with his colleagues' credentials), and each credential's own domain must have proved its DNS ownership or been granted access by Stealed. Rows whose domain did neither stay masked in the same response — this is decided per row, not per request. Every call that actually returns cleartext is written to the access journal as a sensitive read.

When the answer comes back fully masked anyway, the response carries password_lock_reason saying which condition was not met: domain_not_open (no domain here proved its DNS or was granted access), caller_not_admin (reading a password in clear is an admin act; an API key counts as one), client_consent_missing, partner_not_admin or tenant_referenced.

Defaultfalse
username?|array<string>|

Username of the leaked user (can be phone, email, ID, ...)

type?|array<string>|

Type of leak (combo, stealer)

hash?|array<string>|

Uniq identifier for the concatenation of : username, password and domain

upload_stealed?||array<|string>|

Upload date on stealed, ISO 8601, pattern YYYY-MM-DD

upload_date?||array<|string>|

Upload date on the plateform the credential was found, ISO 8601, pattern YYYY-mm-dd

log_date?||array<|string>|

Log date of the device at compromized moment (if applicable, stealer only)

start_date?||

Start date to search from leaks, format: YYYY-mm-dd (default: today - 14days at 0:00am)

end_date?||

End date to search leaks from, format YYYY-mm-dd (default: today)

host?|array<string>|

URL of the leaked data

domain?|array<string>|

FQDN of the leaked data

local_part?|array<string>|

Local part of the username section (if applicable, email only)

protocol?|array<string>|

Protocol identified (if applicable)

email_domain?|array<string>|

Email domain to filter on (if multiple email domains declared)

root_domain?|array<string>|

Root domain to filter on (if multiple root domains declared)

machine_id?|array<string>|

Machine ID (if applicable, stealer only)

computer_name?|array<string>|

Computer name (if applicable, stealer only)

hardware_id?|array<string>|

Hardware ID (if applicable, stealer only)

machine_user?|array<string>|

Machine user (if applicable, stealer only)

ip_address?|array<string>|

IP address (if applicable, stealer only)

country?|array<string>|

Country (if applicable, stealer only)

software?|array<string>|

Software (if applicable, stealer only)

stealer_name?|array<string>|

Stealer name (if applicable)

source?|array<string>|

Opaque source label(s) to filter on, of the form Source-<6 hex> (as returned by the sources breakdown). The raw telegram_channel is never exposed: the label is matched back against the same MD5-prefix expression server-side.

keyword?|array<string>|

Keyword to filter on (only active keywords for tenant)

match_type?|array<string>|

Match type for leaks_matched table (root_domain or email_domain)

not_root_domain?|array<string>|

Root domains to exclude (NOT IN filter)

not_email_domain?|array<string>|

Email domains to exclude (NOT IN filter)

not_domain?|array<string>|

Domains to exclude (NOT IN filter)

not_type?|array<string>|

Types to exclude

not_software?|array<string>|

Software to exclude

not_stealer_name?|array<string>|

Stealer names to exclude

not_protocol?|array<string>|

Protocols to exclude

not_country?|array<string>|

Countries to exclude

first_seen_date?|

Show only hashes whose first appearance (min upload_stealed) falls on this exact date. Format: YYYY-MM-DD.

first_seen_since?|

Show only hashes whose first appearance (min upload_stealed) is on or after this date. Format: YYYY-MM-DD.

limit?|

Limit result length

min_occurrences?|

Minimum number of sources (source_count >= N)

Response Body

application/json

application/json

curl -X GET "https://api.stealed.io/leaks/details?identifier_column=root_domain"
{
  "data": [
    {
      "username": "string",
      "password": "string",
      "hash": "string",
      "password_locked": true,
      "domain": "string",
      "root_domain": "string",
      "email_domain": "string",
      "first_seen": "2019-08-24T14:15:22Z",
      "last_seen": "2019-08-24T14:15:22Z",
      "upload_date": "2019-08-24T14:15:22Z",
      "log_date": "2019-08-24T14:15:22Z",
      "types": [
        "string"
      ],
      "type": "string",
      "scope": "string",
      "host": "string",
      "country": "string",
      "stealer_name": "string",
      "software": "string",
      "protocol": "string",
      "ip_address": "string",
      "computer_name": "string",
      "machine_id": "string",
      "machine_user": "string",
      "hardware_id": "string",
      "hosts": [
        "string"
      ],
      "countries": [
        "string"
      ],
      "stealers": [
        "string"
      ],
      "softwares": [
        "string"
      ],
      "protocols": [
        "string"
      ],
      "ip_addresses": [
        "string"
      ],
      "computer_names": [
        "string"
      ],
      "machine_ids": [
        "string"
      ],
      "machine_users": [
        "string"
      ],
      "hardware_ids": [
        "string"
      ],
      "sources": 0,
      "occurrences": 0,
      "files": 0,
      "source_count": 0,
      "tenant_id": "string",
      "tenant_name": "string"
    }
  ],
  "total": 0,
  "page": 0,
  "page_size": 0,
  "total_pages": 0,
  "preview": true,
  "redacted": true
}
{
  "detail": [
    {
      "loc": [
        "string"
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}

Retrieve all leak statistics in a single request GET

Retrieve all leak statistics for the current organization in a single request. This endpoint fetches all leak data once and calculates multiple statistics server-side, reducing the number of queries needed. If `start_date` and `end_date` are not provided, defaults to **the last 14 days**. Includes trend data comparing the current period to the previous period of the same length (e.g. if querying 30 days, trends compare to the 30 days before that). **Returns a dictionary with the following statistics:** - `total_leaks`: Total number of leaks - `unique_usernames`: Number of unique usernames - `unique_domains`: Number of unique domains - `unique_passwords`: Number of unique passwords - `unique_leaks`: Number of unique leak hashes - `leaks_by_type`: Number of leaks grouped by type (combo/stealer) - `leaks_by_day`: Number of leaks grouped by day and identifier (limited globally to 10 top identifiers + other) - `leaks_by_domain`: Number of leaks grouped by domain (limited globally to 10 top domains + other) - `leaks_by_domain_all`: Number of leaks grouped by domain (all domains, no limit) - `leaks_by_inverse_identifier`: Number of leaks grouped by the inverse identifier (email_domain if root_domain, or vice-versa) (limited globally to 10 top + other) - `top_domains`: Top domains with most leaks (limited globally to 10) - `top_users`: Top users with most leaks (all users, no limit) - `password_strength`: Average password length - `password_reuse`: Number of reused passwords - `password_length_distribution`: Distribution of passwords by length - `external_accounts`: Detailed list of external accounts (username, masked password, type, domain, upload_stealed; capped at 50, most recent first) - `external_accounts_count`: Total count of external accounts - `admin_accounts`: Detailed list of admin/privileged accounts (same shape as `external_accounts`; capped at 50, most recent first) - `admin_accounts_count`: Total count of admin accounts - `unique_sources`: Total number of unique sources (opaque count, channel identity not exposed) - `sources`: List of sources with leak counts (all sources, no limit) - `latest_leak_date`: Date of the latest leak - `leak_details`: All leak details (same as org_leaks_detail_per_identifier, limited to 20000) - `leaks_by_country`: Top 50 countries by leak count (non-empty only) - `leaks_by_stealer_name`: Top 50 stealer families by leak count (non-empty only) - `leaks_by_software`: Top 50 software/browsers by leak count (non-empty only) - `leaks_by_computer_name`: Top 30 computer names by leak count (non-empty only) **Trend Fields** (percentage change vs previous period of same length): - `total_leaks_trend`, `unique_usernames_trend`, `unique_domains_trend` - `unique_sources_trend`, `admin_accounts_count_trend`, `external_accounts_count_trend` **Examples:** ```bash # All stats for the last 14 days (default) GET /leaks/stats?identifier_column=root_domain # With custom date range GET /leaks/stats?identifier_column=root_domain&start_date=2024-01-01&end_date=2024-12-31 # With filters GET /leaks/stats?identifier_column=root_domain&type=Stealer&country=FR ```

Retrieve all occurrences of a specific leak by hash GET

Retrieve all individual occurrences (raw records) for a specific leak hash. Use this after `/leaks/details` to drill down into a deduplicated leak and see every source where the credential was found. **Parameters:** - `hash` (required): The leak hash from the `/leaks/details` response - `identifier_column` (required): `root_domain`, `email_domain`, or `username` **Response:** ```json { "data": [ { "type": "Stealer", "username": "user@example.com", "password": "p****d", "domain": "example.com", "upload_stealed": "2025-01-15T10:30:00", "upload_date": "2025-01-14T08:00:00", "stealer_name": "RedLine", "country": "FR", "ip_address": "1.2.3.4", "computer_name": "DESKTOP-ABC", "software": "Chrome" }, ... ] } ``` **Example:** ```bash GET /leaks/details/by-hash?hash=5d41402abc4b2a76b9719d911017c592&identifier_column=root_domain ```