# LeakRadar > Investigate exposed accounts across stealer logs, combolists, raw leak files and dark web forums. Inspect plaintext passwords when available in the source. Published by Radar Forge SASU, France. Product information reviewed 2026-09-09. Inspect the exposed password alongside the account and, for stealer logs, its service URL. When the source includes plaintext, that detail helps your team scope password resets and investigate exposed access. Compare plans for plaintext access and included unlock points. Get notified via Email, Slack, Discord, Telegram or Webhook when new exposed credentials match your monitored domains or email addresses. Public domain reports show aggregate figures without an account, when available. Record-level investigation requires an authenticated member account. Remediation is performed by the customer's team in its own systems. Coverage and dated aggregate volume: https://leakradar.io/en/data-coverage. Raw file lines are not a count of unique accounts. Examples on public pages are fictional. Editorial comparisons are published by LeakRadar using official product sources; they are not an independent coverage benchmark. Investigate exposed accounts across stealer logs, combolists, raw leak files and dark web forums. Inspect plaintext passwords when available in the source. 776,570,491,621 lines across indexed leak files. File lines, not a count of unique accounts. Updated: 2026-10-20T20:16:11.173Z ## Explore the source behind a match Fictional examples illustrate the formats. They are not search results. ### Stealer logs Connect a captured login to its service URL, username and password to understand which access is exposed. Example format: ```text https://portal.example.com:alex@example.com:Demo-only-42! ``` [API documentation](https://docs.leakradar.io/#tag/Search-Email) ### Combolists Find email/password and username/password pairs, including records without a service URL. Example format: ```text alex@example.com:Demo-only-42! alex_demo:Demo-only-42! ``` [API documentation](https://docs.leakradar.io/#tag/Search-Combolists) ### Raw leak files Search the text of leaked files, database dumps and unstructured records beyond normalized credential fields. Example format: ```text email,service,password alex@example.com,portal.example.com,Demo-only-42! ``` [API documentation](https://docs.leakradar.io/#tag/Raw-Search) ### Dark web forums Search indexed forum posts for mentions of your organization and examine the discussion's source context. Example format: ```text title: example.com content: demo source: forum.example.org ``` [API documentation](https://docs.leakradar.io/#tag/Dark-Web-Search) ## Plaintext passwords, with their account context Inspect the exposed password alongside the account and, for stealer logs, its service URL. When the source includes plaintext, that detail helps your team scope password resets and investigate exposed access. Compare plans for plaintext access and included unlock points. ## Access that fits your work Start with a free account, then choose the plan that fits your datasets, monitoring, exports and team. Get notified via Email, Slack, Discord, Telegram or Webhook when new exposed credentials match your monitored domains or email addresses. ## Sources and access - [How our data is organized](https://leakradar.io/en/methodology) - [Trust center](https://leakradar.io/en/trust) - [Alternatives](https://leakradar.io/en/alternatives) - [Compare plans](https://leakradar.io/en#pricing-plans) # LEAKRADAR MCP The MCP (Model Context Protocol) server connects your assistant to LeakRadar to search, monitor and unlock results with your account. You need a LeakRadar account and a client that supports remote MCP with OAuth. Features depend on your LeakRadar plan and your assistant or workspace settings. ## Connect your assistant Choose your client to see its configuration. MCP server URL: https://mcp.leakradar.io/mcp No API key to copy ### Claude In Claude on the web or Desktop, open Customize > Connectors > Add custom connector. Name it LeakRadar and paste the server URL. Choose OAuth and, if asked for an OAuth client, Register automatically. Connect, then enable the connector in your conversation. When redirected, sign in to LeakRadar, review the requested access and approve. Return to your assistant to finish. Never paste your password or a token into the conversation. [Official client guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) ### ChatGPT In ChatGPT on the web, open Plugins, then + > Add custom MCP server. Name it LeakRadar, paste the server URL and choose OAuth. Create the plugin, install it, then select it with @ in your conversation. Workspace permissions may restrict this option. When redirected, sign in to LeakRadar, review the requested access and approve. Return to your assistant to finish. Never paste your password or a token into the conversation. [Official client guide](https://developers.openai.com/api/docs/guides/custom-mcp-server) ### Codex Run the commands below in your terminal. The login command opens the LeakRadar authorization page. Use codex mcp list or /mcp in the Codex CLI to check the server. ``` codex mcp add leakradar --url https://mcp.leakradar.io/mcp codex mcp login leakradar ``` When redirected, sign in to LeakRadar, review the requested access and approve. Return to your assistant to finish. Never paste your password or a token into the conversation. [Official client guide](https://learn.chatgpt.com/docs/extend/mcp?surface=cli) ### Claude Code Run this command in your terminal, then open /mcp in Claude Code and authenticate LeakRadar. Enable the server before starting your investigation. ``` claude mcp add --transport http leakradar https://mcp.leakradar.io/mcp ``` When redirected, sign in to LeakRadar, review the requested access and approve. Return to your assistant to finish. Never paste your password or a token into the conversation. [Official client guide](https://code.claude.com/docs/en/mcp) ### Cursor Add the entry below to .cursor/mcp.json in your project, preserving any existing servers. Save, restart Cursor and complete the sign-in prompt. Enable LeakRadar in the agent tools. ``` { "mcpServers": { "leakradar": { "url": "https://mcp.leakradar.io/mcp" } } } ``` When redirected, sign in to LeakRadar, review the requested access and approve. Return to your assistant to finish. Never paste your password or a token into the conversation. [Official client guide](https://prod.cursor.com/help/customization/mcp) ### VS Code In the Command Palette, run MCP: Add Server, choose HTTP, paste the URL and name it leakradar. Alternatively merge the entry below into .vscode/mcp.json. Start the server, authorize it and enable its tools in agent mode. ``` { "servers": { "leakradar": { "type": "http", "url": "https://mcp.leakradar.io/mcp" } } } ``` When redirected, sign in to LeakRadar, review the requested access and approve. Return to your assistant to finish. Never paste your password or a token into the conversation. [Official client guide](https://code.visualstudio.com/docs/agent-customization/mcp-servers) Documentation updated on 2026-10-16 ## Try your first request Check your plan, points and quotas first, without unlocking results. Use LeakRadar to show my plan, points balance and current quotas. Do not unlock or change anything. ## Requests to try ### Exposure Replace example.com with a domain you are authorized to assess. With LeakRadar, summarize exposure for my authorized domain example.com by category, without unlocking results. ### Monitoring With LeakRadar, list my monitors and summarize their latest alerts, without changing them or spending points. ### Unlocking Unlocks may use points. Confirmation depends on your assistant’s settings. With LeakRadar, prepare to unlock at most 10 results from the last search on my authorized domain. Show the selected results and cost, then wait for my approval before unlocking them. ## Understand what the connection allows The connector acts on your behalf, within your account permissions. Access is granted as a whole; the categories below are explanations, not separate permission switches. ### Search and read Exposure, searches, profile, monitors, alerts and job status. Reads spend no points, remain subject to quotas and show masked credentials unless already unlocked. `account_status`, `domain_overview`, `list_monitors`, `monitoring_alerts`, `job_status`, `api_catalog`, `api_describe`, `api_read` ### Unlock with points Unlock results with an explicit maximum. Points may be used depending on your plan; reads never unlock automatically. `api_spend` ### Export and download Queue exports and retrieve download links. Export and raw download allowances depend on your plan, unlocked data and any applicable GB balance. `api_export` ### Create and modify Manage monitors, alert channels, lists, statuses and profile fields exposed by the public API. Auto-unlock monitors require a points budget. `api_create`, `api_update` ### Delete and revoke Delete monitors, channels, lists or comments, revoke invitations and manage team access where your account permits it. `api_delete` ### Send messages Test alert channels and send or resend team invitations. These actions can notify other people. `api_send` The connector instructs the assistant to ask before spending, exporting, changing, deleting or sending. Actual approval prompts depend on your client settings; the server does not enforce a separate human approval for every action. Revoke access at any time from LeakRadar > Settings > MCP > Connected applications. Data already shared with your assistant remains subject to that provider’s policies. ## Help with the connection ### The connection failed or expired Reconnect from your assistant and sign in to the correct LeakRadar environment. A 401 can indicate expired or revoked access. Restart an invalid authorization request from the assistant. ### A feature is unavailable or results are masked Check your LeakRadar plan with account_status. Masked results need unlocking; a 402 indicates insufficient points or GB, and a 403 can indicate an account or plan restriction. ### A request is rate limited On a 429, respect Retry-After before retrying and reduce concurrent requests. Use smaller pages. Do not repeatedly replay failing requests. ### I cannot add the server or see its tools Check client and workspace permissions, enable LeakRadar in the conversation and refresh its tools. Remote clients need to reach the MCP and OAuth endpoints; a VPN-only service or Cloudflare login challenge can block them. ## API reference Generated from the public OpenAPI snapshot reviewed on 2026-09-08. Schema version: `1.0.0`. Operations: 114. Schema SHA-256: `18a8c4415b7690bcd2f8280ebbf7df60c027fd221d70208cb70517f3b161f14c`. Source: https://api.leakradar.io/openapi.json Request parameters and permissions below belong to this snapshot. Consult the current OpenAPI for subsequent changes. Authenticated operations use the account credentials and permissions described by the API. Public aggregate endpoints do not grant access to individual records. Rate limits are endpoint- and plan-dependent. Clients must handle HTTP 429 and any Retry-After header explicitly. ### Dark Web Search #### POST `/search/dark-web` **Search dark web mentions** Look for mentions of a company, domain, IP, or email across scraped dark web/forum posts. **Simple mode**: Use `query` to search across title + content. **Advanced mode**: Use specific fields (`title`, `content`, `author`, `source_url`) with AND/OR logic. - Pagination: `page` starts at 1, `page_size` max 100. - Fields: title, content, author, source, source_ref, target, published_at, ingested_at. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–100, default 25). _(minimum=1, maximum=100, default=`25`)_ | | `sources` | string \| null | no | Comma-separated list of sources to filter by | | `date_from` | string \| null | no | Filter posts published after this date | | `date_to` | string \| null | no | Filter posts published before this date | | `sort_by` | string | no | Sort field: ingested_at or published_at _(default=`ingested_at`)_ | | `sort_order` | string | no | Sort order: asc or desc _(default=`desc`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `query` | string \| null | no | Global search term (simple mode). | | `title` | string \| null | no | Search in title only (advanced mode). | | `content` | string \| null | no | Search in content only (advanced mode). | | `author` | string \| null | no | Exact author match (advanced mode). | | `source_url` | string \| null | no | Source URL contains (advanced mode). | | `logic` | string \| null | no | Logic for combining fields: AND or OR. _(default=`AND`)_ | **Responses:** - `200` : Search results (censored on free plans). Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of dark web posts for the current page. | | `total` | integer | yes | Total number of matching posts. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | - `400` : Pagination limit exceeded (offset + page_size > 10,000). - `401` : Authentication required, or invalid/expired API key. - `403` : Source filtering or source_url search requires a paid plan with dark web access; account banned, or pending email verification. - `422` : Validation error in search parameters. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Dark web index unavailable, under maintenance, or public API temporarily disabled. #### GET `/search/dark-web/post/{post_id}` **Get a single dark web post by ID** Returns the full content of a dark web post without truncation. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `post_id` | string | yes | The post ID (document _id) | **Responses:** - `200` : Post content (censored on free plans). Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | string \| null | no | Unique post identifier. | | `title` | string \| null | no | Post title. | | `content` | string \| null | no | Post content. May be truncated for long posts. | | `source` | string \| null | no | Source identifier (forum/market). | | `source_name` | string \| null | no | Human-readable source name. | | `target` | string \| null | no | Target entity mentioned in the post. | | `source_ref` | string \| null | no | Original reference URL on the source. | | `author` | string \| null | no | Post author. | | `published_at` | string \| null | no | Original publication date. | | `ingested_at` | string \| null | no | Date when the post was indexed by LeakRadar. | | `truncated` | boolean | no | Whether the content was truncated due to length. _(default=`False`)_ | | `censored` | boolean | no | Whether sensitive content was censored. _(default=`False`)_ | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : Post not found. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Dark web index unavailable, under maintenance, or public API temporarily disabled. #### GET `/search/dark-web/sources` **Get available dark web sources** Returns a list of all scraped forum sources with their post counts. **Responses:** - `200` : List of sources (names censored on free plans). Response shape: | Field | Type | Required | Description | |---|---|---|---| | `sources` | array | yes | | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Dark web index unavailable, under maintenance, or public API temporarily disabled. #### GET `/search/dark-web/stats` **Get dark web statistics** Returns overall statistics about the dark web index. **Responses:** - `200` : Dark web statistics. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `total_posts` | integer | yes | | | `total_sources` | integer | yes | | | `latest_ingested_at` | string \| null | no | | | `oldest_published_at` | string \| null | no | | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Dark web index unavailable, under maintenance, or public API temporarily disabled. ### Exports #### GET `/exports` **List export jobs** Return the current account's export jobs ordered by most recent first. Supports pagination. **Pagination** - `page` starts at 1. - `page_size` default and maximum are defined by server settings (default 20, max 100 if not configured). **Response** `PaginatedResponse[ExportBase]` with `items`, `total`, `page`, `page_size`. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 Page out of range. - 422 Request validation error. - 429 Rate limit exceeded. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number starting at 1 _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Number of items per page _(minimum=1, maximum=100, default=`20`)_ | **Responses:** - `200` : Exports retrieved successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Unauthorized. The user is not authenticated. - `404` : Page out of range. - `422` : Validation error: invalid request parameters. - `429` : Too many requests: rate limit exceeded. ### Notifications #### GET `/notification_methods` **List notification methods** Return all notification methods configured for the current account. Method types - `email` - `slack` (Incoming Webhook URL) - `telegram` (`value` = bot token, `second_value` = chat_id) - `webhook` (generic HTTP webhook) - `discord` (Discord Webhook URL) Response Array of objects with `id`, `type`, `value`, `second_value`, `timestamp`. **Responses:** - `200` : Notification methods listed successfully. - `401` : Authentication required. #### POST `/notification_methods` **Create notification method** Create a new notification method for the current account. Body fields - `type` one of: `email`, `slack`, `telegram`, `webhook`, `discord`. - `value` string. For Slack/Discord use the webhook URL. For Telegram use the bot token. - `second_value` optional. Required for Telegram (`chat_id`). Response Object with `id`, `type`, `value`, `second_value`, `timestamp`. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `type` | string ('email', 'slack', 'telegram', 'webhook', 'discord') | yes | NNotificationMethodTypeEnum | | `value` | string | yes | Primary value (e.g., webhook URL, bot token, or email address). _(maxLength=500)_ | | `second_value` | string \| null | no | Secondary value when required by the method (e.g., Telegram chat_id). | | `headers` | array<(recursive)> \| null | no | Optional custom HTTP headers for webhook methods (max 10). | | `extra_emails` | array \| null | no | Up to 4 additional email recipients (delivered as BCC). Only allowed when type=email. Total recipients (value + extra_emails) must be ≤ 5. | **Responses:** - `200` : Method created successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | Notification method ID. | | `type` | string ('email', 'slack', 'telegram', 'webhook', 'discord') | yes | NNotificationMethodTypeEnum | | `value` | string | yes | Primary destination (email address, webhook URL, channel ID, etc.). | | `second_value` | string \| null | no | Secondary value (e.g. Telegram chat ID). | | `headers` | array<(recursive)> \| null | no | Custom HTTP headers for webhook methods. | | `extra_emails` | array \| null | no | Additional email recipients delivered as BCC (email methods only, max 4). | | `timestamp` | string | yes | Creation date of this notification method. | - `401` : Authentication required. - `403` : Feature not available on your plan (free tier restricts non-email channels and extra recipients). - `422` : Validation error. #### POST `/notification_methods/test` **Test notification method** Validate that the provided parameters allow sending a test notification. Email: `value`=recipient email address. Slack: Incoming Webhook URL. Telegram: `value`=bot token, `second_value`=chat_id. Discord: Webhook URL. Webhook: sends a JSON payload to the provided URL. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `type` | string ('email', 'slack', 'telegram', 'webhook', 'discord') | yes | NNotificationMethodTypeEnum | | `value` | string | yes | Primary value (e.g., webhook URL, bot token, or email address). _(maxLength=500)_ | | `second_value` | string \| null | no | Secondary value when required by the method (e.g., Telegram chat_id). | | `headers` | array<(recursive)> \| null | no | Optional custom HTTP headers for webhook methods (max 10). | | `extra_emails` | array \| null | no | Up to 4 additional email recipients (delivered as BCC). Only allowed when type=email. Total recipients (value + extra_emails) must be ≤ 5. | **Responses:** - `200` : Test notification sent successfully. - `400` : Invalid parameters or delivery failed. - `401` : Authentication required. - `422` : Validation Error - `429` : Rate limit exceeded (30 tests per hour). - `500` : Internal error while sending the test notification. #### DELETE `/notification_methods/{method_id}` **Delete notification method** Delete a notification method owned by the current account. **Errors** - 401 Authentication required. - 404 Notification method not found. - 409 Method is still used by active notifications. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `method_id` | integer | yes | ID of the notification method. | **Responses:** - `200` : Method deleted successfully. - `401` : Authentication required. - `404` : Notification method not found. - `409` : Method is still used by active notifications. - `422` : Validation Error #### PATCH `/notification_methods/{method_id}` **Update notification method** Update an existing notification method owned by the current account. **Errors** - 401 Authentication required. - 404 Notification method not found. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `method_id` | integer | yes | ID of the notification method. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `type` | string ('email', 'slack', 'telegram', 'webhook', 'discord') | yes | NNotificationMethodTypeEnum | | `value` | string | yes | Primary value (e.g., webhook URL, bot token, or email address). _(maxLength=500)_ | | `second_value` | string \| null | no | Secondary value when required by the method (e.g., Telegram chat_id). | | `headers` | array<(recursive)> \| null | no | Optional custom HTTP headers for webhook methods (max 10). | | `extra_emails` | array \| null | no | Up to 4 additional email recipients (delivered as BCC). Only allowed when type=email. Total recipients (value + extra_emails) must be ≤ 5. | **Responses:** - `200` : Method updated successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | Notification method ID. | | `type` | string ('email', 'slack', 'telegram', 'webhook', 'discord') | yes | NNotificationMethodTypeEnum | | `value` | string | yes | Primary destination (email address, webhook URL, channel ID, etc.). | | `second_value` | string \| null | no | Secondary value (e.g. Telegram chat ID). | | `headers` | array<(recursive)> \| null | no | Custom HTTP headers for webhook methods. | | `extra_emails` | array \| null | no | Additional email recipients delivered as BCC (email methods only, max 4). | | `timestamp` | string | yes | Creation date of this notification method. | - `401` : Authentication required. - `404` : Notification method not found. - `422` : Validation error. #### POST `/notification_methods/{method_id}/test-with-data` **Test notification method with fake data** Send a test notification with realistic fake data. Supports all notification types via the `type` query parameter: - `leak` (default): Test with fake credential leaks - `raw`: Test with fake raw search results - `dark_web`: Test with fake dark web posts Rate limit: 30 tests per hour. Supported for all method types: email, slack, telegram, webhook, discord. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `method_id` | integer | yes | ID of the notification method. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `notification_type` | string | no | Fake data type: leak (default), raw, or dark_web. _(default=`leak`)_ | **Responses:** - `200` : Test notification sent successfully. - `400` : Delivery failed. - `401` : Authentication required. - `404` : Notification method not found. - `422` : Validation Error - `429` : Rate limit exceeded (30 tests per hour). - `500` : Internal error while sending the test notification. #### GET `/notifications` **List notifications** Return all notifications configured for the current account, including linked method info. Response Array of objects with fields like `id`, `method_id`, `type`, `second_type`, `value`, `filters`, `is_active`, `timestamp`, `method_type`, `method_value`, `auto_unlock`, `max_points`, `daily_spent`, `daily_reset_at`, `total_notifications`, `total_cost`. **Responses:** - `200` : Notifications listed successfully. - `401` : Authentication required. #### POST `/notifications` **Create notification** Create a new notification bound to an existing notification method. Supported types - `email` requires `value` (email). `second_type` is not allowed. - `domain` requires `value` (domain). `second_type` optional: one of domain leak types, default `ALL`. - `advanced` requires `filters` (no `value`). Bulk is not allowed. - `raw` and `dark_web` require `filters` (no `value`). Bulk is not allowed. Datasets - `dataset` picks the corpus of `email`, `domain` and `advanced` alerts: `stealer_logs` (default) or `combolist`. - `raw` alerts always monitor `raw_files` and `dark_web` alerts always monitor `dark_web`: the server sets it from the type and rejects any other value. Bulk creation - For `email` and `domain`, set `bulk=true` and provide newline-separated values in `value` to create several notifications at once. Limit - Max notifications = plan limit + extra monitoring assets purchased. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `method_id` | integer \| null | no | Notification method ID to use. | | `dataset` | string ('stealer_logs', 'combolist', 'raw_files', 'dark_web') | no | Dataset to monitor. For `email`, `domain` and `advanced`: `stealer_logs` (default) or `combolist`. `raw` alerts are bound to `raw_files` and `dark_web` alerts to `dark_web`: the server sets it from the type and rejects any other value (`stealer_logs` is still accepted there as the historical default). _(default=`stealer_logs`)_ | | `type` | string ('email', 'domain', 'advanced', 'raw', 'dark_web') | yes | NNotificationTypeEnum | | `value` | string \| null | no | Value to monitor (required for EMAIL/DOMAIN types). | | `filters` | object \| null | no | Filters when type=ADVANCED or RAW. Parsed according to type. | | `auto_unlock` | boolean \| null | no | If true, automatically unlock new matching leaks (consumes points). | | `max_points` | integer \| null | no | Daily points cap for auto-unlock (if enabled). | | `second_type` | string ('ALL', 'EMPLOYEES', 'THIRD_PARTIES', 'CUSTOMERS') \| null | no | For DOMAIN notifications: which leak category to monitor. | | `bulk` | boolean \| null | no | Create multiple notifications at once (e.g., newline-separated values). _(default=`False`)_ | **Responses:** - `200` : Notification created successfully. - `400` : Invalid parameters (missing value, invalid type, etc.). - `401` : Authentication required. - `403` : Feature or channel not allowed on your plan (free-tier restrictions or per-type plan gates), or monitoring limit reached (buy assets / upgrade). - `404` : Referenced notification method not found. - `409` : Duplicate notification already exists. - `422` : Validation error. #### DELETE `/notifications/bulk` **Delete multiple notifications** Delete multiple notifications owned by the current account. Body - `ids` array of notification IDs to delete. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `ids` | array | yes | Notification IDs to delete. | **Responses:** - `200` : Notifications deleted successfully. - `400` : No notification IDs provided. - `401` : Authentication required. - `422` : Validation Error #### GET `/notifications/stats` **Get notification stats** Return monitored assets usage and limits (plan + extra assets) at account level. **Responses:** - `200` : Stats returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `total_notifications_asset` | integer | yes | | | `max_notification_asset` | integer \| null | yes | | | `plan_limit` | integer \| null | no | | | `extra_limit` | integer \| null | no | | | `total_limit` | integer \| null | no | | | `available` | integer \| null | no | | - `401` : Authentication required. #### DELETE `/notifications/{notification_id}` **Delete notification** Delete a single notification owned by the current account. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `notification_id` | integer | yes | ID of the notification. | **Responses:** - `200` : Notification deleted successfully. - `401` : Authentication required. - `404` : Notification not found. - `422` : Validation Error #### PATCH `/notifications/{notification_id}` **Update notification** Update an existing notification owned by the current account. All type-specific rules from creation apply. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `notification_id` | integer | yes | ID of the notification. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `method_id` | integer \| null | no | Notification method ID to use. | | `dataset` | string ('stealer_logs', 'combolist', 'raw_files', 'dark_web') | no | Dataset to monitor. For `email`, `domain` and `advanced`: `stealer_logs` (default) or `combolist`. `raw` alerts are bound to `raw_files` and `dark_web` alerts to `dark_web`: the server sets it from the type and rejects any other value (`stealer_logs` is still accepted there as the historical default). _(default=`stealer_logs`)_ | | `type` | string ('email', 'domain', 'advanced', 'raw', 'dark_web') | yes | NNotificationTypeEnum | | `value` | string \| null | no | Value to monitor (required for EMAIL/DOMAIN types). | | `filters` | object \| null | no | Filters when type=ADVANCED or RAW. Parsed according to type. | | `auto_unlock` | boolean \| null | no | If true, automatically unlock new matching leaks (consumes points). | | `max_points` | integer \| null | no | Daily points cap for auto-unlock (if enabled). | | `second_type` | string ('ALL', 'EMPLOYEES', 'THIRD_PARTIES', 'CUSTOMERS') \| null | no | For DOMAIN notifications: which leak category to monitor. | | `bulk` | boolean \| null | no | Create multiple notifications at once (e.g., newline-separated values). _(default=`False`)_ | **Responses:** - `200` : Notification updated successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | Notification ID. | | `method_id` | integer | yes | ID of the notification method used for delivery. | | `type` | string ('email', 'domain', 'advanced', 'raw', 'dark_web') | yes | NNotificationTypeEnum | | `dataset` | string ('stealer_logs', 'combolist', 'databases', 'raw_files', 'dark_web') | no | Dataset monitored by this notification: `stealer_logs`, `combolist` or `databases` for credential alerts (email, domain, advanced), `raw_files` for raw alerts, `dark_web` for dark web alerts. _(default=`stealer_logs`)_ | | `second_type` | string ('ALL', 'EMPLOYEES', 'THIRD_PARTIES', 'CUSTOMERS') \| null | no | Sub-type for domain notifications: employees, customers, or third_parties. | | `value` | string \| null | no | Monitored value (email address, domain, search query, etc.). | | `filters` | object \| null | no | Additional search filters applied to this notification. | | `last_searched_at` | string \| null | no | Last time this notification was checked for new results. | | `is_active` | boolean | yes | Whether this notification is currently active. | | `timestamp` | string | yes | Creation date of this notification. | | `method_type` | string ('email', 'slack', 'telegram', 'webhook', 'discord') \| null | no | Delivery type of the associated method. | | `method_value` | string \| null | no | Destination of the associated method. | | `method_extra_emails` | array \| null | no | Additional BCC recipients of the associated method (email methods only). | | `auto_unlock` | boolean \| null | no | Whether new results are automatically unlocked. | | `max_points` | integer \| null | no | Maximum points to spend per notification run for auto-unlock. | | `daily_spent` | integer \| null | no | Points spent today by auto-unlock on this notification. | | `daily_reset_at` | string \| null | no | Next daily reset time for the spent counter. | | `total_notifications` | integer \| null | no | Total number of notification runs executed. | | `total_cost` | integer \| null | no | Total points consumed by this notification across all runs. | - `400` : Invalid parameters. - `401` : Authentication required. - `403` : Feature not included in your plan. - `404` : Notification not found. - `422` : Validation error. #### PATCH `/notifications/{notification_id}/active` **Set notification active** Enable or disable a notification by ID. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `notification_id` | integer | yes | ID of the notification. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `is_active` | boolean | yes | Set the notification as active/inactive. | **Responses:** - `200` : Status updated successfully. - `401` : Authentication required. - `404` : Notification not found. - `422` : Validation Error ### Notifications Runs #### GET `/notification_runs` **List notification runs** Return paginated notification runs for the current user ordered by most recent first. **Pagination** - `page` starts at 1. - `page_size` is 1 to 1000 (default 20). **Response** `PaginatedResponse[NotificationRunBase]` with `items`, `total`, `page`, `page_size`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 20). _(minimum=1, maximum=1000, default=`20`)_ | **Responses:** - `200` : Notification runs listed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Authentication required. - `422` : Validation Error #### GET `/notification_runs/{run_id}/export` **Export leaks from a notification run to CSV/TXT/JSON** Queue an export of leaks associated with a specific notification run. Optional `search` and `is_email` filters apply. The job runs asynchronously and the file will appear in the Exports section when ready. **Limits** - You can have at most 5 exports with status PENDING or IN_PROGRESS. **Query parameters** - `format` optional output format: `csv` (default), `txt`, or `json`. **Response** `ExportResponse` with `status`, `message`, and `export_id`. **Errors** - 400 Too many exports in progress. - 401 Authentication required, or invalid/expired API key. - 404 Run not found. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `run_id` | integer | yes | ID of the notification run. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional free-text filter on URL or username. | | `is_email` | boolean \| null | no | Filter by identifier type: true=emails, false=usernames, null=both. | | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Responses:** - `200` : Export queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : Too many exports in progress. - `401` : Authentication required. - `404` : Run not found. - `422` : Validation Error #### GET `/notification_runs/{run_id}/items` **List items for a notification run (unified endpoint)** Return items associated with a specific notification run. Supports all notification types: - **Credential leaks** (EMAIL, DOMAIN, ADVANCED): Fetches leak details from ES - **RAW**: Returns file metadata stored in DB - **DARK_WEB**: Fetches post details from ES **Pagination** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). **Response** `PaginatedRunItemsResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`, `item_type`. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 Run not found. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `run_id` | integer | yes | ID of the notification run. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | **Responses:** - `200` : Items returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of run items (LeakRunItem, RawRunItem, or DarkWebRunItem). | | `total` | integer | yes | Total number of items in this run. | | `total_unlocked` | integer | yes | Number of already-unlocked items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `item_type` | string \| null | no | Primary item type in this run: 'leak', 'raw', or 'dark_web'. | - `401` : Authentication required. - `404` : Run not found. - `422` : Validation Error #### POST `/notification_runs/{run_id}/leaks/unlock` **Unlock leaks from a notification run** Unlock leaks that belong to a specific notification run. You can optionally filter by `search` and `is_email`, and cap the number of new unlocks with `max`. The effective limit is capped by your available points. **Behavior** - Only items not previously unlocked in this run are attempted. - Points are charged only for newly unlocked items. - The response contains the final unlocked state for the requested IDs. **Errors** - 400 No leaks for this run, all data already unlocked, or no data matches the search. - 401 Authentication required, or invalid/expired API key. - 403 The target monitored by this run is blocked. - 404 Run not found. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `run_id` | integer | yes | ID of the notification run. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `max` | integer \| null | no | Max items to unlock (capped by your available points). | | `search` | string \| null | no | Optional free-text filter on URL or username. | | `is_email` | boolean \| null | no | Filter by identifier type: true=emails, false=usernames, null=both. | | `list_id` | integer \| null | no | Optional unlocked-list ID assigned to newly unlocked items. | **Responses:** - `200` : Leaks unlocked successfully. - `400` : No leaks, all already unlocked, or no data matches filter. - `401` : Authentication required. - `403` : The target monitored by this run is blocked. - `404` : Run not found. - `422` : Validation Error #### PUT `/notification_runs/{run_id}/status` **Update the status of a notification run** Set the triage status of a notification run. Valid values: `new`, `in_progress`, `fixed`, `accepted_risk`. The status is propagated to all unlocked leaks in the run. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `run_id` | integer | yes | ID of the notification run. _(minimum=1)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `status` | string \| null | no | | **Responses:** - `200` : Run status updated successfully. - `400` : Invalid status value. - `401` : Authentication required, or invalid/expired API key. - `404` : Run not found. - `422` : Invalid path parameter or request body. ### Password Range #### GET `/password-range` **Search password hashes by prefix** Search leaked password hashes (SHA-1) by prefix or full hash and get the distinct matches with their occurrence counts. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `prefix` | string | yes | SHA-1 prefix or full hash to search for. _(maxLength=40, minLength=5)_ | | `limit` | integer | no | Maximum number of matching hashes to return. _(minimum=1, maximum=10000, default=`1000`)_ | | `suffix_only` | boolean | no | If true, return only hash suffixes (after the first 5 chars) instead of full hashes. _(default=`False`)_ | **Responses:** - `200` : Matching hashes returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `prefix` | string | yes | | | `total` | integer | yes | | | `hashes` | array | yes | | - `400` : Prefix must be hexadecimal. - `401` : Authentication required, or invalid/expired API key. - `403` : Account pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. ### Profile #### GET `/profile` **Get user profile** Return the profile of the authenticated user. **Responses:** - `200` : User profile retrieved successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | | | `first_name` | string | yes | | | `last_name` | string | yes | | | `organization` | string \| null | yes | | | `email` | string | yes | | | `address` | string \| null | yes | | | `address_line2` | string \| null | yes | | | `city` | string \| null | yes | | | `region` | string \| null | yes | | | `zip_code` | string \| null | yes | | | `country` | string \| null | yes | | | `phone` | string \| null | yes | | | `tva_number` | string \| null | yes | | | `admin` | boolean | yes | | | `is_uploader` | boolean \| null | no | _(default=`False`)_ | | `timestamp` | string | yes | | | `subscription_start_date` | string \| null | yes | | | `subscription_end_date` | string \| null | yes | | | `subscription_active` | boolean \| null | yes | | | `subscription_points` | integer \| null | yes | | | `subscription_gb` | integer \| null | no | | | `extra_points` | integer \| null | no | | | `extra_gb` | integer \| null | no | | | `last_gb_reset` | string \| null | no | | | `stripe_subscription_id` | string \| null | yes | | | `plan_id` | integer \| null | yes | | | `can_prepare_databases` | boolean | no | _(default=`False`)_ | | `invoices` | array | yes | | | `plan` | object (PlanBase) \| null | yes | | | `parent_user_id` | integer \| null | no | | | `team_role` | string \| null | no | | | `email_confirmed` | boolean | yes | | | `last_login` | string \| null | yes | | | `last_login_ip` | string \| null | yes | | | `registration_ip` | string \| null | yes | | | `campaign_code` | string \| null | yes | | | `register_by` | string \| null | yes | | | `banned` | boolean | yes | | | `api` | string \| null | yes | | | `twofa_enabled` | boolean | yes | | | `twofa_pin_enabled` | boolean | no | _(default=`False`)_ | | `twofa_totp_enabled` | boolean | no | _(default=`False`)_ | | `has_password` | boolean | no | _(default=`True`)_ | | `referral_code` | string \| null | no | | | `referral_percentage` | number \| null | no | | | `referral_percentage_renewal` | number \| null | no | | | `last_points_reset` | string \| null | no | | - `401` : Unauthorized. Invalid or missing authentication token. - `404` : User not found. - `422` : Validation error: invalid request parameters. - `429` : Too many requests: rate limit exceeded. #### PATCH `/profile` **Update user profile** Update the authenticated user's profile with fields defined by `ProfileUpdate` (name, organization, address, country, VAT number, etc.). Email is not editable here. **Behavior** - Only whitelisted fields from `ProfileUpdate` are applied. **Response** Returns the updated profile. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `first_name` | string \| null | no | | | `last_name` | string \| null | no | | | `organization` | string \| null | no | | | `address` | string \| null | no | | | `address_line2` | string \| null | no | | | `phone` | string \| null | no | | | `zip_code` | string \| null | no | | | `region` | string \| null | no | | | `city` | string \| null | no | | | `country` | string \| null | no | | | `tva_number` | string \| null | no | | **Responses:** - `200` : User profile updated successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | | | `first_name` | string | yes | | | `last_name` | string | yes | | | `organization` | string \| null | yes | | | `email` | string | yes | | | `address` | string \| null | yes | | | `address_line2` | string \| null | yes | | | `city` | string \| null | yes | | | `region` | string \| null | yes | | | `zip_code` | string \| null | yes | | | `country` | string \| null | yes | | | `phone` | string \| null | yes | | | `tva_number` | string \| null | yes | | | `admin` | boolean | yes | | | `is_uploader` | boolean \| null | no | _(default=`False`)_ | | `timestamp` | string | yes | | | `subscription_start_date` | string \| null | yes | | | `subscription_end_date` | string \| null | yes | | | `subscription_active` | boolean \| null | yes | | | `subscription_points` | integer \| null | yes | | | `subscription_gb` | integer \| null | no | | | `extra_points` | integer \| null | no | | | `extra_gb` | integer \| null | no | | | `last_gb_reset` | string \| null | no | | | `stripe_subscription_id` | string \| null | yes | | | `plan_id` | integer \| null | yes | | | `can_prepare_databases` | boolean | no | _(default=`False`)_ | | `invoices` | array | yes | | | `plan` | object (PlanBase) \| null | yes | | | `parent_user_id` | integer \| null | no | | | `team_role` | string \| null | no | | | `email_confirmed` | boolean | yes | | | `last_login` | string \| null | yes | | | `last_login_ip` | string \| null | yes | | | `registration_ip` | string \| null | yes | | | `campaign_code` | string \| null | yes | | | `register_by` | string \| null | yes | | | `banned` | boolean | yes | | | `api` | string \| null | yes | | | `twofa_enabled` | boolean | yes | | | `twofa_pin_enabled` | boolean | no | _(default=`False`)_ | | `twofa_totp_enabled` | boolean | no | _(default=`False`)_ | | `has_password` | boolean | no | _(default=`True`)_ | | `referral_code` | string \| null | no | | | `referral_percentage` | number \| null | no | | | `referral_percentage_renewal` | number \| null | no | | | `last_points_reset` | string \| null | no | | - `400` : Invalid VAT number. - `401` : Unauthorized. Invalid or missing authentication token. - `404` : User not found. - `422` : Validation error: invalid request parameters. - `429` : Too many requests: rate limit exceeded. #### GET `/profile/activities/balance-logs` **List balance history for the current account** Return a paginated list of balance log entries (points and GB changes) for the authenticated user's account. Team members see the owner's aggregated logs. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page. _(minimum=1, maximum=100, default=`20`)_ | | `order_by` | string | no | Field to sort by. _(default=`created_at`)_ | | `order_dir` | string | no | Sort direction: asc or desc. _(default=`desc`)_ | **Responses:** - `200` : Balance logs returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Authentication required. - `404` : Page out of range. - `422` : Validation Error #### GET `/profile/activities/tasks` **List task history for the current account** Return a paginated list of background tasks (exports, bulk unlocks, etc.) for the authenticated user's account. Team members see the owner's aggregated tasks. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page. _(minimum=1, maximum=100, default=`20`)_ | | `order_by` | string | no | Field to sort by. _(default=`id`)_ | | `order_dir` | string | no | Sort direction: asc or desc. _(default=`desc`)_ | **Responses:** - `200` : Tasks returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Authentication required. - `404` : Page out of range. - `422` : Validation Error #### GET `/profile/preferences` **Get user preferences** Return the authenticated user's preferences. Defaults are used when no preferences have been saved yet. **Responses:** - `200` : Preferences returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `hide_passwords` | boolean | no | When true, plaintext passwords are redacted from search results, the unlocked-leaks list, and exports on email/domain/advanced scopes. _(default=`False`)_ | - `401` : Unauthorized. #### PATCH `/profile/preferences` **Update user preferences** Partial update of the authenticated user's preferences. Only fields provided in the body are modified. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `hide_passwords` | boolean \| null | no | Toggle password redaction. Omit to leave unchanged. | **Responses:** - `200` : Preferences updated. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `hide_passwords` | boolean | no | When true, plaintext passwords are redacted from search results, the unlocked-leaks list, and exports on email/domain/advanced scopes. _(default=`False`)_ | - `401` : Unauthorized. - `422` : Validation error. #### GET `/profile/usage` **Get plan limits and current usage** Return every plan limit alongside its current consumption for the authenticated account. Scope - All counters are account-owner scoped: a team member sees the owner's consumption, since quotas are shared across the team. Semantics - `limit: null` means unlimited. - `used: null` means the counter is momentarily unreadable (never 0 by default, so the UI can tell 'unknown' from 'nothing consumed'). - `caps_enforced: false` means the caps of your plan are not being applied. - `legacy_caps: true` means the daily request cap is not enforced on this account (concurrency caps still are). This endpoint never mutates a counter: reading it does not consume quota. **Responses:** - `200` : Usage returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `caps_enforced` | boolean | no | False when the caps of your plan are not being applied. _(default=`True`)_ | | `legacy_caps` | boolean | no | The daily request cap is not enforced on this account. _(default=`False`)_ | | `is_team_member` | boolean | no | _(default=`False`)_ | | `plan_name` | string \| null | no | | | `is_paid` | boolean | no | _(default=`False`)_ | | `requests_daily` | object (UsageMetric) | yes | One plan dimension: how much of it is consumed right now. ``limit = None`` means unlimited, ``used = None`` means the counter could not be read right now and the UI should show "unavailable" rather than 0. | | `requests_daily_reset_epoch` | integer \| null | no | Unix epoch of the next hourly rollover. | | `concurrent_raw` | object (UsageMetric) | yes | One plan dimension: how much of it is consumed right now. ``limit = None`` means unlimited, ``used = None`` means the counter could not be read right now and the UI should show "unavailable" rather than 0. | | `concurrent_advanced` | object (UsageMetric) | yes | One plan dimension: how much of it is consumed right now. ``limit = None`` means unlimited, ``used = None`` means the counter could not be read right now and the UI should show "unavailable" rather than 0. | | `unlocked_lists` | object (UsageMetric) | yes | One plan dimension: how much of it is consumed right now. ``limit = None`` means unlimited, ``used = None`` means the counter could not be read right now and the UI should show "unavailable" rather than 0. | | `team` | object (TeamUsageMetric) | yes | TeamUsageMetric | | `monitoring` | object (MonitoringUsageMetric) | yes | MonitoringUsageMetric | - `401` : Authentication required, or invalid/expired API key. ### Profile Downloads #### GET `/profile/raw/downloads` **List your raw file downloads** Return a paginated list of your raw file downloads within the retention window. Team members see the account owner's downloads. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page. _(minimum=1, maximum=100, default=`20`)_ | **Responses:** - `200` : Downloads listed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Authentication required, or invalid/expired API key. - `404` : Page out of range. - `422` : Validation Error #### POST `/profile/raw/downloads/{download_id}/file` **Download a raw file through the platform** Resolve a previously unlocked raw file download and redirect (307) to a short-lived download URL for the file. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `download_id` | integer | yes | ID of the download record (from GET /profile/raw/downloads). | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `token` | string \| null | no | Optional one-time download token. | **Responses:** - `307` : Temporary redirect to a download URL for the raw file. - `401` : Authentication is required. - `403` : Account banned. - `404` : Download not found or raw file unavailable. - `410` : Download expired or previous attempt failed. Unlock the file again to regenerate access. - `422` : Validation Error #### GET `/profile/raw/unlocked-parts` **List your unlocked raw files** Return a paginated list of the raw file entries you have unlocked (one row per entry: file name, path, category, content preview), most recent first. Optional `q` filters by inner file name, path, source file or category. Team members see the account owner's unlocks. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page. _(minimum=1, maximum=100, default=`20`)_ | | `q` | string \| null | no | Filter by file name, path, source or category. | **Responses:** - `200` : Unlocked raw files listed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Authentication required, or invalid/expired API key. - `422` : Validation Error ### Raw Search #### GET `/container/file_info` **Fetch metadata for a raw entry** Return the stored metadata for a raw entry located in the container tree. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `container_id` | integer | yes | Container identifier. _(minimum=1)_ | | `entry_path` | string | yes | Path of the entry inside the container. _(minLength=1)_ | **Responses:** - `200` : Entry metadata returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `container_id` | string | yes | Identifier of the container that owns the entry. | | `entry_path` | string | yes | Entry path looked up in the index. | | `entry_name` | string \| null | no | Base name of the entry, when known. | | `size_bytes` | integer \| null | no | Size of the entry in bytes, if provided by the index. | | `sha256_original` | string \| null | no | SHA-256 hash of the original raw file, when known. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : Entry not found in the index. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Search error. - `503` : Raw search under maintenance, or public API temporarily disabled. #### GET `/container/subfolders` **List immediate subfolders** Aggregate the immediate subfolders located under the provided prefix. Pagination is `page`/`page_size`. The server hides any cursor and handles deep paging internally. Uniform response: { items, total, page, page_size } + has_more. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `container_id` | integer | yes | Container identifier. _(minimum=1)_ | | `prefix` | string | no | folder prefix ('' for root) _(default=``)_ | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–100000, default 1000). _(minimum=1, maximum=100000, default=`1000`)_ | **Responses:** - `200` : Subfolders aggregated successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of immediate subfolders found below the prefix. | | `total` | integer | yes | Total count of distinct immediate subfolders estimated precisely. | | `page` | integer | yes | Requested page index (1-based). | | `page_size` | integer | yes | Page size used for the listing. | | `has_more` | boolean | yes | True if there are more subfolders beyond this page. | | `container_id` | string | yes | Identifier of the container that was browsed. | | `prefix` | string | yes | Folder prefix used for the aggregation. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Aggregation failed. - `503` : Raw search under maintenance, or public API temporarily disabled. #### GET `/container/tree` **Browse the container tree** List entries located under a given prefix within a container. Pagination uses `page`/`page_size`. Uniform response: { items, total, page, page_size }. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `container_id` | integer | yes | Container identifier. _(minimum=1)_ | | `prefix` | string | no | folder prefix, ex: 'db/users' ('' = root of container) _(default=``)_ | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 200). _(minimum=1, maximum=1000, default=`200`)_ | **Responses:** - `200` : Entries listed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | Entries located under the requested prefix. | | `total` | integer | yes | Total entries under the prefix. | | `page` | integer | yes | Requested page index (1-based). | | `page_size` | integer | yes | Maximum number of entries returned in this response. | | `container_id` | string | yes | Identifier of the container that was browsed. | | `prefix` | string | yes | Folder prefix used for the listing. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Search query failed. - `503` : Raw search under maintenance, or public API temporarily disabled. #### POST `/container/tree/resolve_path` **Resolve a full folder chain and prefetch its contents** Resolve an entry path by hydrating every folder segment leading to it. The resolver fetches the immediate subfolders and a page of files for each segment in the chain. It also can prefetch parts around a target sequence. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `container_id` | integer | yes | Identifier of the container that owns the entry. _(minimum=1.0)_ | | `entry_path` | string | no | Entry path to resolve from the root of the container. _(default=``)_ | | `options` | object (ResolvePathOptions) \| null | no | Fine-grained resolver options. | **Responses:** - `200` : Folder chain resolved successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `container_id` | string | yes | Identifier of the container that owns the resolved path. | | `entry_path` | string | yes | Entry path requested by the caller. | | `segments` | array | yes | Ordered list of segments composing the folder chain. | | `target_entry` | object (TreeEntry) \| null | no | Entry metadata for the resolved file when found. | | `target_parts` | object (PartsResponse) \| null | no | Prefetched parts for the resolved file when requested. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : Folder or entry not found. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Search query failed. - `503` : Raw search under maintenance, or public API temporarily disabled. #### POST `/raw/download` **Generate a download URL for a raw file** Return a short-lived URL that allows downloading a raw file. Either provide the `sha256_original` hash directly or specify both `container_id` and `entry_path` so the hash can be resolved. You can pass parameters either as query string or in the JSON body. If both are provided, the JSON body values take precedence. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `sha256_original` | string \| null | no | SHA-256 of the original file (alternative to container_id + entry_path). | | `container_id` | integer \| null | no | Container identifier (with entry_path, resolves the file hash). | | `entry_path` | string \| null | no | Path of the entry inside the container. | | `expires_in` | integer | no | Download URL lifetime in seconds (60–3600, default 900). _(minimum=60, maximum=3600, default=`900`)_ | **Request body** (optional, `application/json`): Type: `object \| null` **Responses:** - `200` : Download URL generated successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `url` | string | yes | Temporary URL that can be used to download the file. | | `expires_in` | integer | no | Number of seconds before the download URL expires. _(default=`900`)_ | | `file_name` | string \| null | no | Suggested file name for the downloaded file. | | `size_bytes` | integer \| null | no | Size of the file in bytes, when known. | | `mime` | string \| null | no | MIME type of the file, when available. | | `already_unlocked` | boolean | no | True when the user had an active unlock for the file prior to this request. _(default=`False`)_ | | `downloaded_before` | boolean | no | Indicates whether the user has successfully downloaded this file in the past. _(default=`False`)_ | - `400` : Missing or invalid identifier (container_id / expires_in). - `401` : Authentication required, or invalid/expired API key. - `402` : Insufficient GB quota to unlock the file. - `403` : Raw download requires an active raw plan, account banned, or pending email verification. - `404` : File not found, sha256 unknown, or raw file not registered. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Storage or search error. - `503` : Raw search under maintenance, or public API temporarily disabled. #### GET `/raw/download/preview` **Preview unlock cost for a raw file** Return the quota impact of unlocking a raw file. Either provide `sha256_original` or the tuple (`container_id`, `entry_path`). **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `sha256_original` | string \| null | no | SHA-256 of the original file (alternative to container_id + entry_path). | | `container_id` | integer \| null | no | Container identifier (with entry_path, resolves the file hash). | | `entry_path` | string \| null | no | Path of the entry inside the container. | **Responses:** - `200` : Preview computed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `sha256_original` | string | yes | SHA-256 hash of the raw file that would be downloaded. | | `raw_file_id` | integer | yes | Identifier of the raw file. | | `file_name` | string \| null | no | Proposed download file name, when available. | | `original_file_name` | string \| null | no | Original file name when it differs from the stored name. | | `source_size_bytes` | integer | yes | Original file size in bytes. | | `already_unlocked` | boolean | yes | Whether the user has already unlocked the complete file. | | `cost_bytes` | integer | yes | Number of bytes that will be debited if the user unlocks the file. | | `user_gb_available_bytes` | integer | yes | Remaining quota in bytes for the current user. | | `can_unlock` | boolean | yes | True when the user has enough quota to unlock the file. | | `file_password` | string \| null | no | Password required to open the downloaded archive, when present. | | `downloaded_before` | boolean | no | True if the user has downloaded this file previously, regardless of unlock expiration. _(default=`False`)_ | - `400` : Missing or invalid identifier. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : File not found, sha256 unknown, or raw file not registered. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Search error. - `503` : Raw search under maintenance, or public API temporarily disabled. #### GET `/raw/files` **List indexed raw files** Return raw files that are done, non-duplicate and have indexed lines. **Responses:** - `200` : Raw files listed successfully. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. #### POST `/search/raw` **Search raw leak blocks** Perform a full-text search across raw leak blocks. Provide the search payload in the JSON body. **Request body** - `q`: Optional search string (minimum 4 characters once quotes are removed). - `container_id`: Optional container identifier. - `exts` / `exts_not`: File extensions to include / exclude. - `categories` / `categories_not`: Raw file categories to include / exclude. - `file_name` / `file_name_not`: Match on `entry_name` (max 50 items, min 4 chars each). - `file_name_match_type` / `file_name_not_match_type`: How each list is matched, one mode per list: `contains` (default), `starts_with`, `ends_with` or `exact` (the whole entry name). - `folder_name` / `folder_name_not`: Match on the folder holding the block (max 20 items, min 4 chars each, surrounding slashes ignored). `folder_name_not` is not a standalone selector. - `folder_name_match_type` / `folder_name_not_match_type`: `contains` (default, substring of the path), `starts_with` (the path begins with the value), `ends_with` (the path ends with the value) or `exact` (a whole folder at any depth: `data` matches `a/b/data`, not `a/b/database`). - `file_name_exact` / `folder_name_exact`: Deprecated. `true` means `exact` on both the include and the exclude list when no match type is given. - `include_terms`: Extra content terms that must ALL be present, on top of `q` (substring, case-insensitive, max 20 items, 4 to 256 chars each). This is the AND: `q=acme.com` with `include_terms=["admin", "vpn"]` keeps only the blocks holding the three of them. Unlike `exclude_terms` it is a positive selector, so it works without `q`. - `exclude_terms`: Content terms to exclude (substring, case-insensitive, max 20 items, min 4 chars each). Not a standalone selector. - `force_and`: Apply AND logic within `file_name` and within `folder_name` (default OR). - `ingested_at_min` / `ingested_at_max`: Datetime range filter (ISO 8601 with timezone). If `q` is omitted, you must provide at least one filter among `container_id`, `exts`, `categories`, `file_name`, `folder_name`. **Query parameters** - `page` starts at 1. - `page_size` ranges from 1 to 100 (default 10). - `cursor`: Cursor returned in the previous response to fetch the next page of results. **Rate limit:** 5 requests per second per user. **Response:** `{ items, total, page, page_size }` --- **Limits:** | Constraint | Value | |------------|-------| | Max file name filters | 50 | | Max folder name filters | 20 | | Min characters per file / folder name | 4 | | Max export results | 5 000 | | Max export files | 25 | | Download link retention | 7 days | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–100, default 10). _(minimum=1, maximum=100, default=`10`)_ | | `cursor` | string \| null | no | Cursor from previous response to fetch the next page | | `dedup` | boolean | no | Collapse blocks with identical content (same block_fp64) into a single result. Dedup is per-page: with cursor pagination the same content may resurface on a later page. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `q` | string \| null | no | Optional full text query to match within raw blocks. Quotes are ignored for the length check. When omitted or empty, you must provide at least one filter among container_id, exts, categories, file_name. | | `container_id` | integer \| null | no | Restrict the search to a specific container id. | | `exts` | array \| null | no | List of file extensions to include (case-insensitive, without leading dot). | | `exts_not` | array \| null | no | List of file extensions to exclude (case-insensitive, without leading dot). | | `categories` | array \| null | no | Filter results by raw file category. Applied after the search query. | | `categories_not` | array \| null | no | Exclude these raw file categories. Applied after the search query. | | `file_name` | array \| null | no | Case-insensitive wildcards that must match the entry name (OR by default, AND if force_and=true). | | `file_name_not` | array \| null | no | Case-insensitive wildcards that must NOT match the entry name. | | `folder_name` | array \| null | no | Case-insensitive match on the folder holding the block (OR by default, AND if force_and=true). By default the value must appear anywhere in the folder path; with folder_name_exact=true it must be a whole folder at any depth. Leading and trailing slashes are ignored. | | `folder_name_not` | array \| null | no | Folders to exclude, same matching rules as folder_name. Not a standalone selector: a search using only folder_name_not is rejected, provide q or another positive filter. | | `force_and` | boolean \| null | no | When true, require all file_name values to match, and all folder_name values to match (AND instead of OR within each list). | | `file_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name value is matched against the entry name (case-insensitive): `contains` (default, substring), `starts_with`, `ends_with`, or `exact` (the whole name). `*` and `?` are literal in `exact` and ignored in the other modes. | | `file_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name_not value is matched. Same modes as file_name_match_type, default `contains`. | | `folder_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name value is matched against the folder path (case-insensitive): `contains` (default, substring of the path), `starts_with` (the path begins with the value), `ends_with` (the path ends with the value), or `exact` (a whole folder at any depth: 'data' matches 'a/b/data' but not 'a/b/database'; a value with slashes matches a whole run of folders). | | `folder_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name_not value is matched. Same modes as folder_name_match_type, default `contains`. | | `file_name_exact` | boolean \| null | no | Deprecated, use file_name_match_type and file_name_not_match_type. When true and neither is given, both file_name and file_name_not are matched as `exact`. | | `folder_name_exact` | boolean \| null | no | Deprecated, use folder_name_match_type and folder_name_not_match_type. When true and neither is given, both folder_name and folder_name_not are matched as `exact`. | | `q_exact` | boolean \| null | no | When true, the q term must match on word boundaries: '4.4.4.4' will not match '4.4.4.400'. Applied as a content post-filter (no effect without q). | | `include_terms` | array \| null | no | Extra content terms that must ALL be present, on top of q: a block is kept only if its content contains q AND every term listed here (substring, case-insensitive). Min 4 characters per term, max 20 terms. Unlike exclude_terms this IS a positive selector, so it can be used without q. | | `exclude_terms` | array \| null | no | Content terms to exclude: any block whose content contains one of these (substring, case-insensitive) is removed from the results. Min 4 characters per term, max 20 terms. Not a standalone selector: a search using only exclude_terms is rejected, provide q or another positive filter. | | `ingested_at_min` | string \| null | no | Filter results ingested on or after this datetime (inclusive). | | `ingested_at_max` | string \| null | no | Filter results ingested on or before this datetime (inclusive). | **Responses:** - `200` : Search executed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of matching raw blocks. | | `total` | integer | yes | Total number of results (-1 if using cursor mode). | | `page` | integer | yes | Requested page index (1-based). | | `page_size` | integer | yes | Maximum number of items returned in this page. | | `has_more` | boolean \| null | no | True if more results available (cursor mode only). | | `next_cursor` | string \| null | no | Cursor for next page (cursor mode only). | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `400` : Invalid query (too short, too long, or empty with no filters), expired search cursor. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded (5 req/sec). See Retry-After / X-RateLimit-* headers. - `500` : Upstream search error. - `503` : Raw search under maintenance, pagination temporarily unavailable, or public API temporarily disabled. #### POST `/search/raw/count` **Count matching raw blocks (mass-unlock cost estimate)** Return how many raw blocks match the given filters, i.e. the maximum number of parts (1 point each) a mass unlock would consume. Uses the **same** payload as `/search/raw` so the count matches exactly what search and unlock traverse. This is intended to be called on demand (e.g. when opening the mass-unlock dialog), NOT on every search: the count is heavier than a page fetch and is cached. - `exact=true`: `total` is the exact count. - `exact=false`: `total` is an upper bound (`q_exact` and/or `dedup` shrink the real set). - `capped=true`: the count hit its upper limit, so `total` is a lower bound. **Rate limit:** shares the raw search limit, including its concurrent-request slot: a count fans out over every shard of the raw index, so it is billed against the same budget as `/search/raw` rather than the wider generic search limit. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `dedup` | boolean | no | Mark the count as an upper bound because the deduplicated view folds identical content (block_fp64) app-side. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `q` | string \| null | no | Optional full text query to match within raw blocks. Quotes are ignored for the length check. When omitted or empty, you must provide at least one filter among container_id, exts, categories, file_name. | | `container_id` | integer \| null | no | Restrict the search to a specific container id. | | `exts` | array \| null | no | List of file extensions to include (case-insensitive, without leading dot). | | `exts_not` | array \| null | no | List of file extensions to exclude (case-insensitive, without leading dot). | | `categories` | array \| null | no | Filter results by raw file category. Applied after the search query. | | `categories_not` | array \| null | no | Exclude these raw file categories. Applied after the search query. | | `file_name` | array \| null | no | Case-insensitive wildcards that must match the entry name (OR by default, AND if force_and=true). | | `file_name_not` | array \| null | no | Case-insensitive wildcards that must NOT match the entry name. | | `folder_name` | array \| null | no | Case-insensitive match on the folder holding the block (OR by default, AND if force_and=true). By default the value must appear anywhere in the folder path; with folder_name_exact=true it must be a whole folder at any depth. Leading and trailing slashes are ignored. | | `folder_name_not` | array \| null | no | Folders to exclude, same matching rules as folder_name. Not a standalone selector: a search using only folder_name_not is rejected, provide q or another positive filter. | | `force_and` | boolean \| null | no | When true, require all file_name values to match, and all folder_name values to match (AND instead of OR within each list). | | `file_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name value is matched against the entry name (case-insensitive): `contains` (default, substring), `starts_with`, `ends_with`, or `exact` (the whole name). `*` and `?` are literal in `exact` and ignored in the other modes. | | `file_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name_not value is matched. Same modes as file_name_match_type, default `contains`. | | `folder_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name value is matched against the folder path (case-insensitive): `contains` (default, substring of the path), `starts_with` (the path begins with the value), `ends_with` (the path ends with the value), or `exact` (a whole folder at any depth: 'data' matches 'a/b/data' but not 'a/b/database'; a value with slashes matches a whole run of folders). | | `folder_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name_not value is matched. Same modes as folder_name_match_type, default `contains`. | | `file_name_exact` | boolean \| null | no | Deprecated, use file_name_match_type and file_name_not_match_type. When true and neither is given, both file_name and file_name_not are matched as `exact`. | | `folder_name_exact` | boolean \| null | no | Deprecated, use folder_name_match_type and folder_name_not_match_type. When true and neither is given, both folder_name and folder_name_not are matched as `exact`. | | `q_exact` | boolean \| null | no | When true, the q term must match on word boundaries: '4.4.4.4' will not match '4.4.4.400'. Applied as a content post-filter (no effect without q). | | `include_terms` | array \| null | no | Extra content terms that must ALL be present, on top of q: a block is kept only if its content contains q AND every term listed here (substring, case-insensitive). Min 4 characters per term, max 20 terms. Unlike exclude_terms this IS a positive selector, so it can be used without q. | | `exclude_terms` | array \| null | no | Content terms to exclude: any block whose content contains one of these (substring, case-insensitive) is removed from the results. Min 4 characters per term, max 20 terms. Not a standalone selector: a search using only exclude_terms is rejected, provide q or another positive filter. | | `ingested_at_min` | string \| null | no | Filter results ingested on or after this datetime (inclusive). | | `ingested_at_max` | string \| null | no | Filter results ingested on or before this datetime (inclusive). | **Responses:** - `200` : Count computed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `total` | integer | yes | Number of matching raw blocks for the same filters as /search/raw. This is the maximum number of parts (and, at 1 point per part, the maximum points) a mass unlock would consume. It counts all matches, including any the caller already unlocked (those are not re-charged), so treat it as an upper bound on cost. | | `exact` | boolean | yes | True when total is the exact match count. False when it is only an upper bound: q_exact (word-boundary post-filter) or dedup (app-side fold) shrink the real set. | | `capped` | boolean | no | True if the count reached its upper limit; total is then a lower bound ("at least"). _(default=`False`)_ | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, the matched value (total is 0). | - `400` : Invalid query (too short, too long, or empty with no filters). - `401` : Authentication required, or invalid/expired API key. - `403` : Raw search not available on your plan, account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Raw search under maintenance, or public API temporarily disabled. #### POST `/search/raw/export` **Queue a raw export job** Queue an asynchronous export job for raw search results. Mode 'rows' exports a CSV of matching lines, mode 'parts' exports a text snapshot of matching parts. **Important**: This export only includes results that have been previously unlocked via Mass Unlock. No points are consumed during export. Use `/search/raw/unlock/task` to unlock results first. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `export` | string | yes | Export mode: `rows` (CSV of matching lines) or `parts` (text snapshot of matching parts). | | `dedup` | boolean | no | Deduplicate the export. `rows`: drop duplicate lines (whitespace-normalized, case-insensitive). `parts`: drop blocks with identical content. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `q` | string \| null | no | Optional full text query to match within raw blocks. Quotes are ignored for the length check. When omitted or empty, you must provide at least one filter among container_id, exts, categories, file_name. | | `container_id` | integer \| null | no | Restrict the search to a specific container id. | | `exts` | array \| null | no | List of file extensions to include (case-insensitive, without leading dot). | | `exts_not` | array \| null | no | List of file extensions to exclude (case-insensitive, without leading dot). | | `categories` | array \| null | no | Filter results by raw file category. Applied after the search query. | | `categories_not` | array \| null | no | Exclude these raw file categories. Applied after the search query. | | `file_name` | array \| null | no | Case-insensitive wildcards that must match the entry name (OR by default, AND if force_and=true). | | `file_name_not` | array \| null | no | Case-insensitive wildcards that must NOT match the entry name. | | `folder_name` | array \| null | no | Case-insensitive match on the folder holding the block (OR by default, AND if force_and=true). By default the value must appear anywhere in the folder path; with folder_name_exact=true it must be a whole folder at any depth. Leading and trailing slashes are ignored. | | `folder_name_not` | array \| null | no | Folders to exclude, same matching rules as folder_name. Not a standalone selector: a search using only folder_name_not is rejected, provide q or another positive filter. | | `force_and` | boolean \| null | no | When true, require all file_name values to match, and all folder_name values to match (AND instead of OR within each list). | | `file_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name value is matched against the entry name (case-insensitive): `contains` (default, substring), `starts_with`, `ends_with`, or `exact` (the whole name). `*` and `?` are literal in `exact` and ignored in the other modes. | | `file_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name_not value is matched. Same modes as file_name_match_type, default `contains`. | | `folder_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name value is matched against the folder path (case-insensitive): `contains` (default, substring of the path), `starts_with` (the path begins with the value), `ends_with` (the path ends with the value), or `exact` (a whole folder at any depth: 'data' matches 'a/b/data' but not 'a/b/database'; a value with slashes matches a whole run of folders). | | `folder_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name_not value is matched. Same modes as folder_name_match_type, default `contains`. | | `file_name_exact` | boolean \| null | no | Deprecated, use file_name_match_type and file_name_not_match_type. When true and neither is given, both file_name and file_name_not are matched as `exact`. | | `folder_name_exact` | boolean \| null | no | Deprecated, use folder_name_match_type and folder_name_not_match_type. When true and neither is given, both folder_name and folder_name_not are matched as `exact`. | | `q_exact` | boolean \| null | no | When true, the q term must match on word boundaries: '4.4.4.4' will not match '4.4.4.400'. Applied as a content post-filter (no effect without q). | | `include_terms` | array \| null | no | Extra content terms that must ALL be present, on top of q: a block is kept only if its content contains q AND every term listed here (substring, case-insensitive). Min 4 characters per term, max 20 terms. Unlike exclude_terms this IS a positive selector, so it can be used without q. | | `exclude_terms` | array \| null | no | Content terms to exclude: any block whose content contains one of these (substring, case-insensitive) is removed from the results. Min 4 characters per term, max 20 terms. Not a standalone selector: a search using only exclude_terms is rejected, provide q or another positive filter. | | `ingested_at_min` | string \| null | no | Filter results ingested on or after this datetime (inclusive). | | `ingested_at_max` | string \| null | no | Filter results ingested on or before this datetime (inclusive). | **Responses:** - `200` : Export job queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : Invalid query (too short, too long, or empty with no filters), maximum concurrent exports reached. - `401` : Authentication required, or invalid/expired API key. - `403` : Raw export requires an active raw plan, search blocked, account banned, or pending email verification. - `409` : An identical export is already queued/running, or an identical Raw Export is still in its quarantine cooling-off period. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Error while creating the export job. - `503` : Raw search under maintenance, or public API temporarily disabled. #### GET `/search/raw/part` **Retrieve a raw entry part** Fetch the textual content for a specific raw entry block. If the block is not unlocked, the content is censored unless `auto_unlock=true` succeeds. **Query parameters** - `container_id`: Required container identifier. - `entry_path`: Path of the entry inside the container. - `seq`: Block sequence number (0-based). - `trim_overlap`: When true, drop the overlapping prefix shared with the previous block. - `overlap_chars`: Number of characters to trim when `trim_overlap=true` (default from configuration). - `auto_unlock`: Attempt to unlock the block using points when necessary. **Response** Returns `PartResponse` describing the block and its (possibly censored) content. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `container_id` | integer | yes | Container identifier. _(minimum=1)_ | | `entry_path` | string | yes | Path of the entry inside the container. _(minLength=1)_ | | `seq` | integer | yes | Block sequence number (0-based). _(minimum=0)_ | | `trim_overlap` | boolean | no | Drop the overlapping prefix shared with the previous block. _(default=`False`)_ | | `overlap_chars` | integer | no | Characters to trim when trim_overlap=true. _(minimum=0, maximum=8192, default=`256`)_ | | `auto_unlock` | boolean | no | Attempt to unlock the block using points when necessary. _(default=`False`)_ | **Responses:** - `200` : Block returned successfully (content censored unless unlocked). Response shape: | Field | Type | Required | Description | |---|---|---|---| | `container_id` | string | yes | Identifier of the container that owns the part. | | `entry_path` | string | yes | Entry path that contains the part. | | `seq` | integer | yes | Sequential part number requested. | | `offset` | integer | yes | Byte offset of the part inside the entry. | | `size_bytes` | integer \| null | no | Size of the returned block in bytes, if available. | | `ingested_at` | string \| null | no | ISO timestamp of the ingestion time for the block. | | `trim_overlap` | boolean | yes | True if the overlapping prefix was trimmed from the returned content. | | `overlap_chars` | integer | yes | Number of characters trimmed from the start when `trim_overlap=true`. | | `text` | string | yes | Text content of the block. May be censored. | | `censored` | boolean | yes | True if the user has not unlocked the content and it was redacted. | - `400` : Insufficient points during auto-unlock. - `401` : Authentication required, or invalid/expired API key. - `403` : auto_unlock requires an active raw plan, account banned, or pending email verification. - `404` : Block not found or unavailable. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Search query failed. - `503` : Raw search under maintenance, or public API temporarily disabled. #### GET `/search/raw/parts` **List parts for a raw entry** Return metadata about the blocks (parts) of a raw entry. **Query parameters** - `container_id`: Required container identifier. - `entry_path`: Path of the entry inside the container. - `page`/`page_size`: Pagination parameters. **Response** Uniform shape: { items, total, page, page_size } and optional `window`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `container_id` | integer | yes | Container identifier. _(minimum=1)_ | | `entry_path` | string | yes | Path of the entry inside the container. _(minLength=1)_ | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | **Responses:** - `200` : Parts listed successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | Sequence of parts found for the requested slice. | | `total` | integer | yes | Total number of parts available for the entry. | | `page` | integer | yes | Requested page index (1-based). | | `page_size` | integer | yes | Number of parts returned in this page. | | `window` | object (PartsWindow) \| null | no | Span of part indexes covered by the response. | | `container_id` | string | yes | Identifier of the container queried. | | `entry_path` | string | yes | Entry path for which the parts are listed. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Search query failed. - `503` : Raw search under maintenance, or public API temporarily disabled. #### POST `/search/raw/unlock/task` **Queue an async mass unlock task for raw search** Creates an asynchronous unlock **Task** for the given raw search filters. Returns a `task_id` you can poll with the task-status endpoint. **Request body** - `q`: Optional search string (minimum 4 characters if provided). Required if no other filter is provided. - `container_id`: Optional container identifier. - `exts`: Optional list of file extensions to keep. - `exts_not`: Optional list of file extensions to exclude. - `categories`: Optional list of raw file categories. - `categories_not`: Optional list of raw file categories to exclude. - `file_name`: Optional match on `entry_name`. - `file_name_not`: Optional match that must NOT hold on `entry_name`. - `file_name_match_type` / `file_name_not_match_type`: `contains` (default), `starts_with`, `ends_with` or `exact`, one per list. - `folder_name`: Optional match on the folder holding the block (max 20 items, min 4 chars each). - `folder_name_not`: Optional folders to exclude. Not a standalone selector. - `folder_name_match_type` / `folder_name_not_match_type`: `contains` (default), `starts_with`, `ends_with` or `exact` (a whole folder at any depth), one per list. - `file_name_exact` / `folder_name_exact`: Deprecated, `true` means `exact` on both lists when no match type is given. - `include_terms`: Optional extra content terms that must ALL be present, on top of `q` (substring, case-insensitive, max 20, 4 to 256 chars each). Positive selector, usable without `q`. - `exclude_terms`: Optional content terms to exclude (substring, case-insensitive, max 20, min 4 chars each). - `ingested_at_min`: Optional datetime to filter results ingested on or after. - `ingested_at_max`: Optional datetime to filter results ingested on or before. **Query parameters** - `max`: Optional cap of new unlocks. 0 or omitted = use all your points. **Plan requirement** Requires a paid plan with `raw_search` enabled. **Concurrency limit** - raw: max 1 running and 1 queued task per account. - identical active requests return the existing task id. - email / domain: max 5 concurrent tasks per user. - Returns 429 if limit exceeded. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `max` | integer \| null | no | 0 or omitted = use all your points | | `dedup` | boolean | no | Skip blocks with identical content (same block_fp64) so each unique content is unlocked once. Matches the deduplicated search/export view. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `q` | string \| null | no | Optional full text query to match within raw blocks. Quotes are ignored for the length check. When omitted or empty, you must provide at least one filter among container_id, exts, categories, file_name. | | `container_id` | integer \| null | no | Restrict the search to a specific container id. | | `exts` | array \| null | no | List of file extensions to include (case-insensitive, without leading dot). | | `exts_not` | array \| null | no | List of file extensions to exclude (case-insensitive, without leading dot). | | `categories` | array \| null | no | Filter results by raw file category. Applied after the search query. | | `categories_not` | array \| null | no | Exclude these raw file categories. Applied after the search query. | | `file_name` | array \| null | no | Case-insensitive wildcards that must match the entry name (OR by default, AND if force_and=true). | | `file_name_not` | array \| null | no | Case-insensitive wildcards that must NOT match the entry name. | | `folder_name` | array \| null | no | Case-insensitive match on the folder holding the block (OR by default, AND if force_and=true). By default the value must appear anywhere in the folder path; with folder_name_exact=true it must be a whole folder at any depth. Leading and trailing slashes are ignored. | | `folder_name_not` | array \| null | no | Folders to exclude, same matching rules as folder_name. Not a standalone selector: a search using only folder_name_not is rejected, provide q or another positive filter. | | `force_and` | boolean \| null | no | When true, require all file_name values to match, and all folder_name values to match (AND instead of OR within each list). | | `file_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name value is matched against the entry name (case-insensitive): `contains` (default, substring), `starts_with`, `ends_with`, or `exact` (the whole name). `*` and `?` are literal in `exact` and ignored in the other modes. | | `file_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each file_name_not value is matched. Same modes as file_name_match_type, default `contains`. | | `folder_name_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name value is matched against the folder path (case-insensitive): `contains` (default, substring of the path), `starts_with` (the path begins with the value), `ends_with` (the path ends with the value), or `exact` (a whole folder at any depth: 'data' matches 'a/b/data' but not 'a/b/database'; a value with slashes matches a whole run of folders). | | `folder_name_not_match_type` | string ('contains', 'starts_with', 'ends_with', 'exact') \| null | no | How each folder_name_not value is matched. Same modes as folder_name_match_type, default `contains`. | | `file_name_exact` | boolean \| null | no | Deprecated, use file_name_match_type and file_name_not_match_type. When true and neither is given, both file_name and file_name_not are matched as `exact`. | | `folder_name_exact` | boolean \| null | no | Deprecated, use folder_name_match_type and folder_name_not_match_type. When true and neither is given, both folder_name and folder_name_not are matched as `exact`. | | `q_exact` | boolean \| null | no | When true, the q term must match on word boundaries: '4.4.4.4' will not match '4.4.4.400'. Applied as a content post-filter (no effect without q). | | `include_terms` | array \| null | no | Extra content terms that must ALL be present, on top of q: a block is kept only if its content contains q AND every term listed here (substring, case-insensitive). Min 4 characters per term, max 20 terms. Unlike exclude_terms this IS a positive selector, so it can be used without q. | | `exclude_terms` | array \| null | no | Content terms to exclude: any block whose content contains one of these (substring, case-insensitive) is removed from the results. Min 4 characters per term, max 20 terms. Not a standalone selector: a search using only exclude_terms is rejected, provide q or another positive filter. | | `ingested_at_min` | string \| null | no | Filter results ingested on or after this datetime (inclusive). | | `ingested_at_max` | string \| null | no | Filter results ingested on or before this datetime (inclusive). | **Responses:** - `200` : Unlock task queued; returns a task_id to poll. - `400` : Invalid query (too short, too long, or empty with no filters), insufficient points, unlock not needed on your plan. - `401` : Authentication required, or invalid/expired API key. - `403` : Raw search not available on your plan, this search is blocked, account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded, or the account already has a queued raw unlock task. - `503` : Raw search under maintenance, or public API temporarily disabled. ### Search Advanced #### POST `/search/advanced` **Search leaks with advanced filters (JSON body)** Search leaked credentials using any combination of fields defined in `LeakSearchFilters`. **Pagination:** `page` ≥ 1, `page_size` in [1, 1 000] (default 100). **Access:** If your plan includes `advanced_search`, full URLs and item IDs are returned; otherwise redacted. **Rate limit:** 5 requests per second per user. **Auto-unlock:** Pass `auto_unlock=true` to automatically unlock locked items on the current page. Points are consumed per newly unlocked item. If insufficient points, partial unlock is performed. The response field `auto_unlock_points_consumed` indicates how many points were used. --- **Filter limits:** | Constraint | Value | |------------|-------| | Max values per filter field | 50 | | Min characters per value (default) | 3 | | Min characters for `url_scheme`, `url_tld`, `email_tld` | 2 | | Min characters for `username_hash`, `password_hash` | 4 | | Max characters per string value | 100 | | Max characters for `url_scheme` | 20 | | Max characters for `url_tld`, `email_tld` | 10 | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock locked items on the current page using your points. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | array \| null | no | Username values to match. | | `username_not` | array \| null | no | Username values to exclude. | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Username matching mode. Omitted means contains. | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded username matching mode. Omitted means contains. | | `password` | array \| null | no | Password values to match. | | `password_not` | array \| null | no | Password values to exclude. | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Password matching mode. Omitted means contains. | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded password matching mode. Omitted means contains. | | `url` | array \| null | no | URL values to match. | | `url_not` | array \| null | no | URL values to exclude. | | `url_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL matching mode. Omitted means contains. | | `url_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL matching mode. Omitted means contains. | | `url_domain` | array \| null | no | URL domain values to match. | | `url_domain_not` | array \| null | no | URL domain values to exclude. | | `url_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL domain matching mode. Omitted means contains. | | `url_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL domain matching mode. Omitted means contains. | | `url_host` | array \| null | no | URL host values to match. | | `url_host_not` | array \| null | no | URL host values to exclude. | | `url_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL host matching mode. Omitted means contains. | | `url_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL host matching mode. Omitted means contains. | | `username_hash` | array \| null | no | Username SHA-1 hash prefix (hex). | | `password_hash` | array \| null | no | Password SHA-1 hash prefix (hex). | | `url_scheme` | array \| null | no | URL scheme(s) to include (multi). | | `url_scheme_not` | array \| null | no | URL scheme(s) to exclude (multi). | | `url_port` | array \| null | no | URL port(s) to include (multi). | | `url_port_not` | array \| null | no | URL port(s) to exclude (multi). | | `url_tld` | array \| null | no | URL TLD(s) to include (multi). | | `url_tld_not` | array \| null | no | URL TLD(s) to exclude (multi). | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | | `email_domain` | array \| null | no | Email domain values to match. | | `email_domain_not` | array \| null | no | Email domain values to exclude. | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email domain matching mode. Omitted means contains. | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email domain matching mode. Omitted means contains. | | `email_host` | array \| null | no | Email host values to match. | | `email_host_not` | array \| null | no | Email host values to exclude. | | `email_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email host matching mode. Omitted means contains. | | `email_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email host matching mode. Omitted means contains. | | `email_tld` | array \| null | no | Email TLD(s) to include (multi). | | `email_tld_not` | array \| null | no | Email TLD(s) to exclude (multi). | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | Filter by password strength category. too_weak = score 0-2, weak = 3-4, medium = 5-7, strong = 8+. | | `added_from` | string \| null | no | Only include leaks indexed on/after this UTC datetime. | | `added_to` | string \| null | no | Only include leaks indexed on/before this UTC datetime. | | `force_and` | boolean \| null | no | When true, require all values within each field (AND within field). | **Responses:** - `200` : Search results returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of leak records for the current page. | | `total` | integer | yes | Total number of matching leak records. | | `total_unlocked` | integer | yes | Number of already-unlocked records in the total results. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | | `auto_unlock_points_consumed` | integer \| null | no | Number of points consumed by auto-unlock on this request. | - `400` : Pagination too deep (offset > 1,000,000 — use `/search/advanced/export`). - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation error in filter parameters. - `429` : Rate limit exceeded (5 req/sec). Response includes `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers. - `503` : Advanced search under maintenance, or public API temporarily disabled. #### POST `/search/advanced/export` **Export leaks to CSV/TXT/JSON** Queue an export of all leaks that match the provided advanced search filters. At least one filter is required. Request body - JSON payload following the `LeakSearchFilters` schema. Query parameters - `format` optional output format: `csv` (default), `txt`, or `json`. Limits and plan requirement - You can have at most 5 exports with status PENDING or IN_PROGRESS per account. - Requires a paid plan with `advanced_search` enabled. Response `ExportResponse` with `status`, `message`, and `export_id`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | array \| null | no | Username values to match. | | `username_not` | array \| null | no | Username values to exclude. | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Username matching mode. Omitted means contains. | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded username matching mode. Omitted means contains. | | `password` | array \| null | no | Password values to match. | | `password_not` | array \| null | no | Password values to exclude. | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Password matching mode. Omitted means contains. | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded password matching mode. Omitted means contains. | | `url` | array \| null | no | URL values to match. | | `url_not` | array \| null | no | URL values to exclude. | | `url_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL matching mode. Omitted means contains. | | `url_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL matching mode. Omitted means contains. | | `url_domain` | array \| null | no | URL domain values to match. | | `url_domain_not` | array \| null | no | URL domain values to exclude. | | `url_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL domain matching mode. Omitted means contains. | | `url_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL domain matching mode. Omitted means contains. | | `url_host` | array \| null | no | URL host values to match. | | `url_host_not` | array \| null | no | URL host values to exclude. | | `url_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL host matching mode. Omitted means contains. | | `url_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL host matching mode. Omitted means contains. | | `username_hash` | array \| null | no | Username SHA-1 hash prefix (hex). | | `password_hash` | array \| null | no | Password SHA-1 hash prefix (hex). | | `url_scheme` | array \| null | no | URL scheme(s) to include (multi). | | `url_scheme_not` | array \| null | no | URL scheme(s) to exclude (multi). | | `url_port` | array \| null | no | URL port(s) to include (multi). | | `url_port_not` | array \| null | no | URL port(s) to exclude (multi). | | `url_tld` | array \| null | no | URL TLD(s) to include (multi). | | `url_tld_not` | array \| null | no | URL TLD(s) to exclude (multi). | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | | `email_domain` | array \| null | no | Email domain values to match. | | `email_domain_not` | array \| null | no | Email domain values to exclude. | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email domain matching mode. Omitted means contains. | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email domain matching mode. Omitted means contains. | | `email_host` | array \| null | no | Email host values to match. | | `email_host_not` | array \| null | no | Email host values to exclude. | | `email_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email host matching mode. Omitted means contains. | | `email_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email host matching mode. Omitted means contains. | | `email_tld` | array \| null | no | Email TLD(s) to include (multi). | | `email_tld_not` | array \| null | no | Email TLD(s) to exclude (multi). | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | Filter by password strength category. too_weak = score 0-2, weak = 3-4, medium = 5-7, strong = 8+. | | `added_from` | string \| null | no | Only include leaks indexed on/after this UTC datetime. | | `added_to` | string \| null | no | Only include leaks indexed on/before this UTC datetime. | | `force_and` | boolean \| null | no | When true, require all values within each field (AND within field). | **Responses:** - `200` : Export queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : No filters provided, maximum concurrent exports reached. - `401` : Authentication required, or invalid/expired API key. - `403` : Advanced search not available on your plan, search blocked, account banned, or pending email verification. - `409` : An identical export is already queued or running. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Advanced search under maintenance, or public API temporarily disabled. #### POST `/search/advanced/export_urls` **Export distinct URLs to CSV/TXT/JSON** Queue an export of the distinct URLs from leaks that match the provided advanced search filters. Provide the filters in the JSON request body. Request body - `filters` payload following `LeakSearchFilters` fields. Query parameters - `format` optional output format: `csv` (default), `txt`, or `json`. Limits and plan requirement - At least one filter is required. - Capped at 5,000,000 distinct URLs. When a filter matches more, the file holds the alphabetically first 5,000,000 and the export is flagged as limited. - You can have at most 1 export with status PENDING or IN_PROGRESS per account. - Requires a paid plan with `advanced_search` enabled. Response `ExportResponse` with `status`, `message`, and `export_id`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | array \| null | no | Username values to match. | | `username_not` | array \| null | no | Username values to exclude. | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Username matching mode. Omitted means contains. | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded username matching mode. Omitted means contains. | | `password` | array \| null | no | Password values to match. | | `password_not` | array \| null | no | Password values to exclude. | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Password matching mode. Omitted means contains. | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded password matching mode. Omitted means contains. | | `url` | array \| null | no | URL values to match. | | `url_not` | array \| null | no | URL values to exclude. | | `url_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL matching mode. Omitted means contains. | | `url_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL matching mode. Omitted means contains. | | `url_domain` | array \| null | no | URL domain values to match. | | `url_domain_not` | array \| null | no | URL domain values to exclude. | | `url_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL domain matching mode. Omitted means contains. | | `url_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL domain matching mode. Omitted means contains. | | `url_host` | array \| null | no | URL host values to match. | | `url_host_not` | array \| null | no | URL host values to exclude. | | `url_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL host matching mode. Omitted means contains. | | `url_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL host matching mode. Omitted means contains. | | `username_hash` | array \| null | no | Username SHA-1 hash prefix (hex). | | `password_hash` | array \| null | no | Password SHA-1 hash prefix (hex). | | `url_scheme` | array \| null | no | URL scheme(s) to include (multi). | | `url_scheme_not` | array \| null | no | URL scheme(s) to exclude (multi). | | `url_port` | array \| null | no | URL port(s) to include (multi). | | `url_port_not` | array \| null | no | URL port(s) to exclude (multi). | | `url_tld` | array \| null | no | URL TLD(s) to include (multi). | | `url_tld_not` | array \| null | no | URL TLD(s) to exclude (multi). | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | | `email_domain` | array \| null | no | Email domain values to match. | | `email_domain_not` | array \| null | no | Email domain values to exclude. | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email domain matching mode. Omitted means contains. | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email domain matching mode. Omitted means contains. | | `email_host` | array \| null | no | Email host values to match. | | `email_host_not` | array \| null | no | Email host values to exclude. | | `email_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email host matching mode. Omitted means contains. | | `email_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email host matching mode. Omitted means contains. | | `email_tld` | array \| null | no | Email TLD(s) to include (multi). | | `email_tld_not` | array \| null | no | Email TLD(s) to exclude (multi). | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | Filter by password strength category. too_weak = score 0-2, weak = 3-4, medium = 5-7, strong = 8+. | | `added_from` | string \| null | no | Only include leaks indexed on/after this UTC datetime. | | `added_to` | string \| null | no | Only include leaks indexed on/before this UTC datetime. | | `force_and` | boolean \| null | no | When true, require all values within each field (AND within field). | **Responses:** - `200` : Export queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : No filters provided, maximum concurrent exports reached (max 1 for URL exports). - `401` : Authentication required, or invalid/expired API key. - `403` : Advanced search not available on your plan, account banned, or pending email verification. - `409` : An identical export is already queued or running. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Advanced search under maintenance, or public API temporarily disabled. #### POST `/search/advanced/unlock` **Unlock leaks for advanced search** Unlock leaks returned by the same advanced search filters as `/search/advanced`. Provide filters in the JSON body. Optional query parameter `max` limits how many new leaks to unlock. **Behavior** - If `max` is omitted, the service unlocks as many as your available points allow (subscription + extra). - Only previously locked items are unlocked. Already unlocked items remain unchanged. - Only newly unlocked items consume points. - **Hard cap**: synchronous unlocks are limited to 10,000. Use `/search/advanced/unlock/task` for higher volumes. **Plan requirement** Requires a paid plan with `advanced_search` enabled. **Errors** - 400 Insufficient points, no matching data, all items already unlocked, more than 10,000 unlocks (use /search/advanced/unlock/task), unlock not needed on your plan. - 401 Authentication required, or invalid/expired API key. - 403 Advanced search not available on your plan, this search is blocked, account banned, or pending email verification. - 404 The provided list_id does not exist or is not yours. - 422 Request validation error. - 429 Rate limit exceeded. - 503 Advanced search under maintenance, or public API temporarily disabled. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `max` | integer \| null | no | 0 or omitted = use all your points | | `list_id` | integer \| null | no | Assign this list id to new unlocks. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | array \| null | no | Username values to match. | | `username_not` | array \| null | no | Username values to exclude. | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Username matching mode. Omitted means contains. | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded username matching mode. Omitted means contains. | | `password` | array \| null | no | Password values to match. | | `password_not` | array \| null | no | Password values to exclude. | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Password matching mode. Omitted means contains. | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded password matching mode. Omitted means contains. | | `url` | array \| null | no | URL values to match. | | `url_not` | array \| null | no | URL values to exclude. | | `url_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL matching mode. Omitted means contains. | | `url_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL matching mode. Omitted means contains. | | `url_domain` | array \| null | no | URL domain values to match. | | `url_domain_not` | array \| null | no | URL domain values to exclude. | | `url_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL domain matching mode. Omitted means contains. | | `url_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL domain matching mode. Omitted means contains. | | `url_host` | array \| null | no | URL host values to match. | | `url_host_not` | array \| null | no | URL host values to exclude. | | `url_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL host matching mode. Omitted means contains. | | `url_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL host matching mode. Omitted means contains. | | `username_hash` | array \| null | no | Username SHA-1 hash prefix (hex). | | `password_hash` | array \| null | no | Password SHA-1 hash prefix (hex). | | `url_scheme` | array \| null | no | URL scheme(s) to include (multi). | | `url_scheme_not` | array \| null | no | URL scheme(s) to exclude (multi). | | `url_port` | array \| null | no | URL port(s) to include (multi). | | `url_port_not` | array \| null | no | URL port(s) to exclude (multi). | | `url_tld` | array \| null | no | URL TLD(s) to include (multi). | | `url_tld_not` | array \| null | no | URL TLD(s) to exclude (multi). | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | | `email_domain` | array \| null | no | Email domain values to match. | | `email_domain_not` | array \| null | no | Email domain values to exclude. | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email domain matching mode. Omitted means contains. | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email domain matching mode. Omitted means contains. | | `email_host` | array \| null | no | Email host values to match. | | `email_host_not` | array \| null | no | Email host values to exclude. | | `email_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email host matching mode. Omitted means contains. | | `email_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email host matching mode. Omitted means contains. | | `email_tld` | array \| null | no | Email TLD(s) to include (multi). | | `email_tld_not` | array \| null | no | Email TLD(s) to exclude (multi). | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | Filter by password strength category. too_weak = score 0-2, weak = 3-4, medium = 5-7, strong = 8+. | | `added_from` | string \| null | no | Only include leaks indexed on/after this UTC datetime. | | `added_to` | string \| null | no | Only include leaks indexed on/before this UTC datetime. | | `force_and` | boolean \| null | no | When true, require all values within each field (AND within field). | **Responses:** - `200` : Unlocked leaks returned successfully. - `400` : Insufficient points, no matching data, all items already unlocked, more than 10,000 unlocks (use the /unlock/task endpoint), unlock not needed on your plan. - `401` : Authentication required, or invalid/expired API key. - `403` : Advanced search not available on your plan, this search is blocked, account banned, or pending email verification. - `404` : The provided list_id does not exist or is not yours. - `422` : Validation Error - `429` : Rate limit exceeded (10 req/sec). See Retry-After / X-RateLimit-* headers. - `503` : Advanced search under maintenance, or public API temporarily disabled. #### POST `/search/advanced/unlock/task` **Queue an async mass unlock task for advanced search** Creates an asynchronous unlock **Task** for the given advanced filters. Returns a `task_id` you can poll with the task-status endpoint. **Concurrency limit** - advanced / raw: max 2 concurrent tasks per user. - email / domain: max 5 concurrent tasks per user. - Returns 429 if limit exceeded. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `max` | integer \| null | no | Optional cap of new unlocks. | | `list_id` | integer \| null | no | Assign this list id to new unlocks. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | array \| null | no | Username values to match. | | `username_not` | array \| null | no | Username values to exclude. | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Username matching mode. Omitted means contains. | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded username matching mode. Omitted means contains. | | `password` | array \| null | no | Password values to match. | | `password_not` | array \| null | no | Password values to exclude. | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Password matching mode. Omitted means contains. | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded password matching mode. Omitted means contains. | | `url` | array \| null | no | URL values to match. | | `url_not` | array \| null | no | URL values to exclude. | | `url_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL matching mode. Omitted means contains. | | `url_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL matching mode. Omitted means contains. | | `url_domain` | array \| null | no | URL domain values to match. | | `url_domain_not` | array \| null | no | URL domain values to exclude. | | `url_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL domain matching mode. Omitted means contains. | | `url_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL domain matching mode. Omitted means contains. | | `url_host` | array \| null | no | URL host values to match. | | `url_host_not` | array \| null | no | URL host values to exclude. | | `url_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL host matching mode. Omitted means contains. | | `url_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL host matching mode. Omitted means contains. | | `username_hash` | array \| null | no | Username SHA-1 hash prefix (hex). | | `password_hash` | array \| null | no | Password SHA-1 hash prefix (hex). | | `url_scheme` | array \| null | no | URL scheme(s) to include (multi). | | `url_scheme_not` | array \| null | no | URL scheme(s) to exclude (multi). | | `url_port` | array \| null | no | URL port(s) to include (multi). | | `url_port_not` | array \| null | no | URL port(s) to exclude (multi). | | `url_tld` | array \| null | no | URL TLD(s) to include (multi). | | `url_tld_not` | array \| null | no | URL TLD(s) to exclude (multi). | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | | `email_domain` | array \| null | no | Email domain values to match. | | `email_domain_not` | array \| null | no | Email domain values to exclude. | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email domain matching mode. Omitted means contains. | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email domain matching mode. Omitted means contains. | | `email_host` | array \| null | no | Email host values to match. | | `email_host_not` | array \| null | no | Email host values to exclude. | | `email_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email host matching mode. Omitted means contains. | | `email_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email host matching mode. Omitted means contains. | | `email_tld` | array \| null | no | Email TLD(s) to include (multi). | | `email_tld_not` | array \| null | no | Email TLD(s) to exclude (multi). | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | Filter by password strength category. too_weak = score 0-2, weak = 3-4, medium = 5-7, strong = 8+. | | `added_from` | string \| null | no | Only include leaks indexed on/after this UTC datetime. | | `added_to` | string \| null | no | Only include leaks indexed on/before this UTC datetime. | | `force_and` | boolean \| null | no | When true, require all values within each field (AND within field). | **Responses:** - `200` : Unlock task queued; returns a task_id to poll. - `400` : Insufficient points, or unlock not needed on your plan (all data already accessible). - `401` : Authentication required, or invalid/expired API key. - `403` : Advanced search not available on your plan, this search is blocked, account banned, or pending email verification. - `404` : The provided list_id does not exist or is not yours. - `422` : Validation Error - `429` : Rate limit exceeded, or too many concurrent unlock tasks (max 2 for advanced). - `503` : Advanced search under maintenance, or public API temporarily disabled. ### Search Combolists #### POST `/search/combolist/advanced` **Search combolists with mapping-safe advanced filters** Combine multiple filters over the Combolists dataset. Provide the filters in the JSON body. **Filterable fields** Only the fields that exist in the combolist mapping are accepted, which is a strict subset of stealer-log advanced search: `username`, `password`, `email_domain` (each with a `_not` variant and a `_match_type` of `contains`, `starts_with` or `ends_with`), `username_hash`, `password_hash`, `is_email`, `password_strength`, `added_from`, `added_to` and `force_and`. There is no `url` filter: combolist records carry no URL. Sending a stealer-only field is a 422 rather than a silently ignored filter. **Boolean logic** - Values inside one field are OR by default; `force_and=true` requires every positive value of the same field to match. Excluded values stay OR whatever the setting. - Different fields are always AND. **Pagination** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - Deep interactive paging is capped. Past the offset limit the call answers 400 and you should queue `POST /search/combolist/export`. **Rate limit** This endpoint counts as a heavy search: it is bound by the advanced per-second limit and by your plan's concurrent heavy search slots, not by the lighter limit of the email and domain endpoints. **Access and visibility** - The Combolists dataset is sold from the Enterprise tier upwards. Without it the search still runs, but identifiers come back masked, passwords are omitted and item IDs are not returned. **Auto-unlock** Pass `auto_unlock=true` to unlock the locked pairs of the current page. 1 point per newly unlocked pair, partial unlock on insufficient balance, amount reported in `auto_unlock_points_consumed`. **Response** `CombolistSearchResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1-1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock the locked credentials of the current page using your points. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | array \| null | no | | | `username_not` | array \| null | no | | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | | | `password` | array \| null | no | | | `password_not` | array \| null | no | | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | | | `email_domain` | array \| null | no | | | `email_domain_not` | array \| null | no | | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | | | `username_hash` | array \| null | no | | | `password_hash` | array \| null | no | | | `is_email` | boolean \| null | no | | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | | | `added_from` | string \| null | no | | | `added_to` | string \| null | no | | | `force_and` | boolean | no | _(default=`False`)_ | **Responses:** - `200` : Search results successfully returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | | | `total` | integer | yes | Total pairs matching the query. _(minimum=0.0)_ | | `total_unlocked` | integer | yes | How many of them you have already unlocked. _(minimum=0.0)_ | | `page` | integer | yes | _(minimum=1.0)_ | | `page_size` | integer | yes | _(minimum=1.0)_ | | `blacklisted_value` | string \| null | no | Set instead of any result when the query matches one of your blacklist rules. | | `auto_unlock_points_consumed` | integer | no | Points consumed by Combo auto-unlock on this request. _(minimum=0.0, default=`0`)_ | - `400` : Pagination too deep for interactive search. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : Combolist search is not enabled on this deployment. - `422` : Validation error, including a filter field absent from the combolist mapping. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Combolist search under maintenance, or public API temporarily disabled. #### GET `/search/combolist/domain/{domain}` **Search combolists by exact email domain** List the Combolist pairs whose identifier is an email address on the given root domain. **Dataset** - Matches on the indexed `email_domain`, so only pairs with an email identifier are returned. Username-only pairs are out of scope by construction, unlike stealer-log domain search which also matches on the leaked URL. - The domain is normalized (lowercased, trailing dot removed) and must be a valid hostname without `@`. **Pagination** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - Deep interactive paging is capped. Past the offset limit the call answers 400 and you should queue `POST /search/combolist/export`. **Access and visibility** - The Combolists dataset is sold from the Enterprise tier upwards. Without it the search still runs, but identifiers come back masked, passwords are omitted and item IDs are not returned. **Auto-unlock** Pass `auto_unlock=true` to unlock the locked pairs of the current page. 1 point per newly unlocked pair, partial unlock on insufficient balance, amount reported in `auto_unlock_points_consumed`. **Response** `CombolistSearchResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Root email domain to search, for example `example.com`. _(maxLength=253)_ | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Filter the matched pairs further on username or password. | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1-1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock the locked credentials of the current page using your points. _(default=`False`)_ | **Responses:** - `200` : Search results successfully returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | | | `total` | integer | yes | Total pairs matching the query. _(minimum=0.0)_ | | `total_unlocked` | integer | yes | How many of them you have already unlocked. _(minimum=0.0)_ | | `page` | integer | yes | _(minimum=1.0)_ | | `page_size` | integer | yes | _(minimum=1.0)_ | | `blacklisted_value` | string \| null | no | Set instead of any result when the query matches one of your blacklist rules. | | `auto_unlock_points_consumed` | integer | no | Points consumed by Combo auto-unlock on this request. _(minimum=0.0, default=`0`)_ | - `400` : Pagination too deep for interactive search. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : Combolist search is not enabled on this deployment. - `422` : Invalid domain, or invalid search filter. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Combolist search under maintenance, or public API temporarily disabled. #### GET `/search/combolist/domain/{domain}/report` **Get the dedicated combolist exposure report for a domain** Aggregate Combolist exposure for a root email domain: total pairs, distinct email addresses, password strength breakdown, and the first and last dates a pair for that domain entered the index. **Access** - Authentication is optional. An anonymous caller gets the same aggregate figures; the report exposes counts only, never a credential. - A domain matching one of your blacklist rules returns a zeroed report rather than an error. **Caching** Cached for one hour per domain and shared across callers. Send `x-no-cache: 1` to force a recompute. **Response** `CombolistDomainReportResponse`. `dataset` is always `combolist`, which is what distinguishes it from the stealer-log report at `GET /search/domain/{domain}`. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Root email domain to report on, for example `example.com`. _(maxLength=253)_ | **Responses:** - `200` : Report returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | | | `total_credentials` | integer | yes | _(minimum=0.0)_ | | `unique_emails` | integer | yes | Distinct email identifiers, approximate on large domains. _(minimum=0.0)_ | | `password_strength` | object (CombolistPasswordStrengthReport) | yes | CombolistPasswordStrengthReport | | `first_seen` | string \| null | no | Earliest date a pair for this domain entered the index. | | `last_seen` | string \| null | no | Most recent date a pair for this domain entered the index. | | `dataset` | string | no | _(default=`combolist`)_ | - `404` : Combolist search is not enabled on this deployment. - `422` : Invalid domain. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Combolist search under maintenance, or public API temporarily disabled. #### POST `/search/combolist/email` **Search combolists by exact email** Search the Combolists dataset for an exact email address. Provide the search payload in the JSON body. **Dataset** - Combolists is a separate index from stealer logs: a pair returned here does not appear in `POST /search/email`, and unlocking it stores the record under `/profile/unlocked/combolists`, not `/profile/unlocked`. - Records carry no URL. A result is an identifier, a password, a password strength score, the email domain, and the date the pair was first added to the index. - Matching is exact and case-insensitive on the identifier. Use `search` to filter the matched set further on username or password. **Pagination** - `page` starts at 1. - `page_size` is 1 to 100 (default 100). - Deep interactive paging is capped. Past the offset limit the call answers 400 and you should queue `POST /search/combolist/export`. **Access and visibility** - The Combolists dataset is sold from the Enterprise tier upwards. Without it the search still runs, but identifiers come back masked, passwords are omitted and item IDs are not returned, so nothing can be unlocked. - Credentials are returned in clear only when `unlocked=true`. **Auto-unlock** Pass `auto_unlock=true` to automatically unlock the locked pairs of the current page. 1 point is consumed per newly unlocked pair. If your balance is insufficient, a partial unlock is performed. The response field `auto_unlock_points_consumed` reports how many points were used. **Response** `CombolistSearchResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. `blacklisted_value` is set instead of any result when the query matches one of your blacklist rules. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1-100, default 100). _(minimum=1, maximum=100, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock the locked credentials of the current page using your points. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `email` | string | yes | Exact email address to search. _(maxLength=254)_ | | `search` | string \| null | no | Optional substring in the identifier or password. | **Responses:** - `200` : Search results successfully returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | | | `total` | integer | yes | Total pairs matching the query. _(minimum=0.0)_ | | `total_unlocked` | integer | yes | How many of them you have already unlocked. _(minimum=0.0)_ | | `page` | integer | yes | _(minimum=1.0)_ | | `page_size` | integer | yes | _(minimum=1.0)_ | | `blacklisted_value` | string \| null | no | Set instead of any result when the query matches one of your blacklist rules. | | `auto_unlock_points_consumed` | integer | no | Points consumed by Combo auto-unlock on this request. _(minimum=0.0, default=`0`)_ | - `400` : Pagination too deep for interactive search. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : Combolist search is not enabled on this deployment. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Combolist search under maintenance, or public API temporarily disabled. #### POST `/search/combolist/export` **Queue a Combolist search export** Export every Combolist pair matching a search scope, as a background job. **Scope** Same body as `POST /search/combolist/unlock/task`: `scope` is one of `email`, `username`, `domain` or `advanced`, with `value` for the first three and `filters` for the last. **What the file contains** On a normal plan the export contains **only the pairs you have already unlocked** inside that scope, never the locked ones. To export a full result set, queue `POST /search/combolist/unlock/task` first and wait for it to finish. Plans that never spend points (Unlimited) get every matching pair directly. **Limits** - Formats: `csv`, `txt`, `json`. - At most 5 exports pending or in progress per account, all types combined. - An identical export already queued is refused rather than duplicated. **Retrieval** The response carries an `export_id`. Follow it and download the file from the Exports endpoints. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `format` | string ('csv', 'txt', 'json') | no | Output format: `csv`, `txt` or `json`. _(enum: 'csv', 'txt', 'json')_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `scope` | string ('email', 'username', 'domain', 'advanced') | yes | Which search the job replays: `email`, `username` and `domain` take `value`, `advanced` takes `filters`. | | `value` | string \| null | no | Exact email, username or root email domain, per `scope`. | | `search` | string \| null | no | Optional filter on username or password, applied on top of `value`. Not allowed on the advanced scope. | | `filters` | object (CombolistAdvancedSearchRequest) \| null | no | Advanced filter set. Required by, and reserved to, the advanced scope. | **Responses:** - `200` : Export queued. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : Too many exports already in progress. - `401` : Authentication required, or invalid/expired API key. - `403` : The Combolists dataset is not on your plan for this scope, or the search is blocked by one of your blacklist rules. - `404` : Combolist search is not enabled on this deployment. - `409` : An identical export is already queued. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Combolist search under maintenance, or public API temporarily disabled. #### POST `/search/combolist/unlock` **Unlock selected combolist credentials** Unlock a list of Combolist credential IDs and return them in clear. **Identifiers** Only combolist IDs are accepted, that is the `cl1_` namespace followed by 60 hexadecimal characters. A stealer-log ID in the payload is a 400, never a silent skip: the two datasets never share an unlock endpoint. At most 10 000 IDs per call. **Billing** - 1 point per pair newly unlocked, taken from the account owner's balance. Pairs you already own are free and are returned again without charge. - If Elasticsearch rejects part of the batch, only the pairs that were actually written are billed; the rest is refunded. - The Combolists dataset is sold from the Enterprise tier upwards. Unlocking without it is a 403, even though the search itself answers. **Bulk** For a whole result set rather than a selection, queue `POST /search/combolist/unlock/task` instead: it takes a scope rather than a list of IDs and reports progress as a task. **Response** A list of `CombolistUnlockResult`, one per credential whose unlock state could be confirmed. Passwords are omitted when your account preference hides them. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `leak_ids` | array | yes | List of leak document IDs to unlock. | | `target_list_id` | integer \| null | no | Optional list id to assign to newly unlocked items. | **Responses:** - `200` : Credentials unlocked and returned in clear. - `400` : No IDs provided, more than 10 000 IDs, an ID outside the combolist namespace, insufficient points, or every selected credential was already unlocked. - `401` : Authentication required, or invalid/expired API key. - `403` : No active subscription, the Combolists dataset is not on your plan, account banned, or pending email verification. - `404` : Combolist search is not enabled on this deployment. - `410` : The selected credentials are no longer in the index. Refresh your search results and retry with the new IDs. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `500` : Unlock state could not be verified, or indexation error. - `503` : Combolist search under maintenance, or public API temporarily disabled. #### POST `/search/combolist/unlock/task` **Queue an asynchronous Combolist mass unlock** Unlock every Combolist pair matching a search scope, as a background task. **Scope** The body is the search itself, not a list of IDs: `scope` is one of `email`, `username`, `domain` or `advanced`, with `value` for the first three and `filters` for the last. The scope is revalidated by the worker, so a task cannot widen its own perimeter. **Cost and cap** - 1 point per pair newly unlocked. The task is capped at your current point balance, and further capped by `max` when you pass it. - Pairs you already own are skipped and cost nothing. - Plans that never spend points (Unlimited) get a 400: everything is already readable, use the export instead. **Progress** The response carries a `task_id`. Poll it on `GET /profile/activities/tasks` to follow `updated` against `total`. **Concurrency** One combolist unlock task at a time per account. Queuing a second one while the first runs is a 429. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `max` | integer \| null | no | Upper bound on the number of pairs to unlock. Omit to use the whole point balance. | | `list_id` | integer \| null | no | Assign every newly unlocked pair to this unlocked list. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `scope` | string ('email', 'username', 'domain', 'advanced') | yes | Which search the job replays: `email`, `username` and `domain` take `value`, `advanced` takes `filters`. | | `value` | string \| null | no | Exact email, username or root email domain, per `scope`. | | `search` | string \| null | no | Optional filter on username or password, applied on top of `value`. Not allowed on the advanced scope. | | `filters` | object (CombolistAdvancedSearchRequest) \| null | no | Advanced filter set. Required by, and reserved to, the advanced scope. | **Responses:** - `200` : Task queued. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | | | `running` | boolean | yes | | | `completed` | boolean | yes | | | `total` | integer \| null | no | | | `updated` | integer \| null | no | | | `version_conflicts` | integer \| null | no | | - `400` : Insufficient points, or the plan does not spend points. - `401` : Authentication required, or invalid/expired API key. - `403` : No active subscription, or the Combolists dataset is not on your plan for this scope. - `404` : Combolist search is not enabled on this deployment, or the target list does not exist. - `422` : Validation error. - `429` : Too many concurrent unlock tasks, or rate limit exceeded. - `503` : Combolist search under maintenance, or public API temporarily disabled. #### POST `/search/combolist/username` **Search combolists by exact username** Search the Combolists dataset for an exact username, that is a pair whose identifier is not an email address. Provide the search payload in the JSON body. **Dataset** - Same index and same isolation rules as `POST /search/combolist/email`; only the identifier shape differs. This endpoint filters on `is_email = false`, so an email address never matches here, and `email_domain` is always null in the results. - Matching is exact and case-insensitive. Use `search` to filter the matched set further on username or password. **Pagination** - `page` starts at 1. - `page_size` is 1 to 100 (default 100). - Deep interactive paging is capped. Past the offset limit the call answers 400 and you should queue `POST /search/combolist/export`. **Access and visibility** - The Combolists dataset is sold from the Enterprise tier upwards. Without it the search still runs, but identifiers come back masked, passwords are omitted and item IDs are not returned, so nothing can be unlocked. **Auto-unlock** Pass `auto_unlock=true` to unlock the locked pairs of the current page. 1 point per newly unlocked pair, partial unlock on insufficient balance, amount reported in `auto_unlock_points_consumed`. **Response** `CombolistSearchResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1-100, default 100). _(minimum=1, maximum=100, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock the locked credentials of the current page using your points. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | string | yes | Exact non-email username to search. _(maxLength=254)_ | | `search` | string \| null | no | Optional substring in the identifier or password. | **Responses:** - `200` : Search results successfully returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | | | `total` | integer | yes | Total pairs matching the query. _(minimum=0.0)_ | | `total_unlocked` | integer | yes | How many of them you have already unlocked. _(minimum=0.0)_ | | `page` | integer | yes | _(minimum=1.0)_ | | `page_size` | integer | yes | _(minimum=1.0)_ | | `blacklisted_value` | string \| null | no | Set instead of any result when the query matches one of your blacklist rules. | | `auto_unlock_points_consumed` | integer | no | Points consumed by Combo auto-unlock on this request. _(minimum=0.0, default=`0`)_ | - `400` : Pagination too deep for interactive search. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `404` : Combolist search is not enabled on this deployment. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Combolist search under maintenance, or public API temporarily disabled. ### Search Domain #### GET `/search/domain/{domain}` **Get leak report for a domain** Return a domain leak report. If unauthenticated, `light=true` is forced. **Modes** - `light=true`: returns estimated counts and a non-sampled existence check when the sample is empty. - `light=false` (authenticated): returns exact counts plus password stats per group. **Parameters** - `include_search_count=true`: include the number of distinct users who searched this domain in the last 7 days (`searched_by_count`). Disabled by default to avoid an expensive lookup. **Caching** - Light up to 24 hours, full up to 1 hour. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to report on, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `light` | boolean | no | Force light mode (sampled counts only). Always forced when unauthenticated. _(default=`False`)_ | | `include_search_count` | boolean | no | Include `searched_by_count` (distinct users who searched this domain in the last 7 days). Full mode only; adds a heavier lookup. _(default=`False`)_ | **Responses:** - `200` : Domain leak report returned successfully. - `400` : Empty domain. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### GET `/search/domain/{domain}/all` **List all leaks for a domain (employees + customers + third parties)** Return a paginated list of **all** leaks linked to a domain, regardless of category (employees, customers, third parties). Each item includes a `category` field indicating whether the leak belongs to employees, customers, or third parties. **Query parameters** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - `search` optional term to narrow results. - `is_email` optional boolean to filter emails (true), usernames (false), or both (null). **Access and visibility** - If your plan includes `domain_search`, full URLs and item IDs are returned. - Otherwise URLs are redacted except for the first 10 items per page and IDs are omitted. - Credentials are included only for items with `unlocked=true` and if your plan includes `domain_search`. **Response** `PaginatedAllLeaksResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. **Auto-unlock** Pass `auto_unlock=true` to automatically unlock locked items on the current page. Points are consumed per newly unlocked item. If insufficient points, partial unlock is performed. The response field `auto_unlock_points_consumed` indicates how many points were used. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to list leaks for, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional search term to filter results. | | `is_email` | boolean \| null | no | Filter by identifier type: true=emails only, false=usernames only, null=both. | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock locked items on the current page using your points. _(default=`False`)_ | **Responses:** - `200` : Leaks returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of leak records for the current page. | | `total` | integer | yes | Total number of matching leak records. | | `total_unlocked` | integer | yes | Number of already-unlocked records in the total results. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | | `auto_unlock_points_consumed` | integer \| null | no | Number of points consumed by auto-unlock on this request. | - `400` : Pagination too deep (offset > 1,000,000 — use the matching `/export` endpoint). - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. Response includes `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### GET `/search/domain/{domain}/customers` **List leaks for customers of a domain** Return a paginated list of leaks linked to customers of the given domain. **Query parameters** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - `search` optional term to narrow results. - `is_email` optional boolean to filter emails (true), usernames (false), or both (null). **Access and visibility** - If your plan includes `domain_search`, full URLs and item IDs are returned. - Otherwise URLs are redacted except for the first 10 items per page and IDs are omitted. - Credentials are included only for items with `unlocked=true` and if your plan includes `domain_search`. **Response** `PaginatedLeaksResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. **Auto-unlock** Pass `auto_unlock=true` to automatically unlock locked items on the current page. Points are consumed per newly unlocked item. If insufficient points, partial unlock is performed. The response field `auto_unlock_points_consumed` indicates how many points were used. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain whose customers' leaks to list, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `search` | string \| null | no | Optional search term to filter results. | | `is_email` | boolean \| null | no | Filter by identifier type: true=emails only, false=usernames only, null=both. | | `auto_unlock` | boolean | no | Automatically unlock locked items on the current page using your points. _(default=`False`)_ | **Responses:** - `200` : Results returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of leak records for the current page. | | `total` | integer | yes | Total number of matching leak records. | | `total_unlocked` | integer | yes | Number of already-unlocked records in the total results. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | | `auto_unlock_points_consumed` | integer \| null | no | Number of points consumed by auto-unlock on this request. | - `400` : Pagination too deep (offset > 1,000,000 — use the matching `/export` endpoint). - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. Response includes `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### GET `/search/domain/{domain}/employees` **List leaks for employees of a domain** Return a paginated list of leaks linked to employees of the given domain. **Query parameters** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - `search` optional term to narrow results. - `is_email` optional boolean to filter emails (true), usernames (false), or both (null). **Access and visibility** - If your plan includes `domain_search`, full URLs and item IDs are returned. - Otherwise URLs are redacted except for the first 10 items per page and IDs are omitted. - Credentials are included only for items with `unlocked=true` and if your plan includes `domain_search`. **Response** `PaginatedLeaksResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. **Auto-unlock** Pass `auto_unlock=true` to automatically unlock locked items on the current page. Points are consumed per newly unlocked item. If insufficient points, partial unlock is performed. The response field `auto_unlock_points_consumed` indicates how many points were used. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain whose employees' leaks to list, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional search term to filter results. | | `is_email` | boolean \| null | no | Filter by identifier type: true=emails only, false=usernames only, null=both. | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock locked items on the current page using your points. _(default=`False`)_ | **Responses:** - `200` : Results returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of leak records for the current page. | | `total` | integer | yes | Total number of matching leak records. | | `total_unlocked` | integer | yes | Number of already-unlocked records in the total results. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | | `auto_unlock_points_consumed` | integer \| null | no | Number of points consumed by auto-unlock on this request. | - `400` : Pagination too deep (offset > 1,000,000 — use the matching `/export` endpoint). - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. Response includes `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### GET `/search/domain/{domain}/report/pdf` **Download domain report as PDF** Generate and download a PDF report for the given domain. **Content** - Aggregated counts for compromised employees, third parties, and customers. - Aggregated password statistics per group when available. **Errors** - 400 Empty domain. - 401 Authentication required, or invalid/expired API key. - 403 Account banned, or pending email verification. - 422 Request validation error. - 429 Rate limit exceeded. - 503 Domain search under maintenance, or public API temporarily disabled. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to generate the PDF report for, e.g. `example.com`. | **Responses:** - `200` : PDF report for the requested domain. - `400` : Empty domain. - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### GET `/search/domain/{domain}/subdomains` **List subdomains for a domain** Return a paginated list of distinct subdomains for the given domain with their occurrence counts. **Query parameters** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - `search` optional wildcard filter on subdomain. **Access and visibility** - If your plan includes `domain_search`, full subdomains are returned. - Otherwise subdomains are redacted except for the first 10 items per page. If `search` is provided and you are not entitled, results are redacted. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Parent domain to list subdomains for, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional wildcard filter on subdomain. | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | **Responses:** - `200` : Results returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### POST `/search/domain/{domain}/subdomains/export` **Export all subdomains for a domain as CSV/TXT/JSON** Generate an on-demand export of all distinct subdomains for the given domain. The export contains a single `Subdomain` column. **Plan requirement** Requires a paid plan with `domain_search` enabled. **Parameters** - You can provide `search` either as a query parameter or in the JSON body. If both are provided, the JSON body value takes precedence. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Parent domain to export subdomains for, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional wildcard filter on subdomain when using query parameters. If also provided in the JSON body, the body value takes precedence. | | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Request body** (optional, `application/json`): Type: `object (DomainSubdomainsExportBody) \| null` **Responses:** - `200` : Subdomains file (CSV/TXT/JSON) streamed successfully. - `401` : Authentication required, or invalid/expired API key. - `403` : Domain search not available on your plan, domain blocked, account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### GET `/search/domain/{domain}/third_parties` **List leaks for third parties of a domain** Return a paginated list of leaks linked to third parties of the given domain. **Query parameters** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - `search` optional term to narrow results. - `is_email` optional boolean to filter emails (true), usernames (false), or both (null). **Access and visibility** - If your plan includes `domain_search`, full URLs and item IDs are returned. - Otherwise URLs are redacted except for the first 10 items per page and IDs are omitted. - Credentials are included only for items with `unlocked=true` and if your plan includes `domain_search`. **Response** `PaginatedLeaksResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. **Auto-unlock** Pass `auto_unlock=true` to automatically unlock locked items on the current page. Points are consumed per newly unlocked item. If insufficient points, partial unlock is performed. The response field `auto_unlock_points_consumed` indicates how many points were used. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain whose third-party leaks to list, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `search` | string \| null | no | Optional search term to filter results. | | `is_email` | boolean \| null | no | Filter by identifier type: true=emails only, false=usernames only, null=both. | | `auto_unlock` | boolean | no | Automatically unlock locked items on the current page using your points. _(default=`False`)_ | **Responses:** - `200` : Results returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of leak records for the current page. | | `total` | integer | yes | Total number of matching leak records. | | `total_unlocked` | integer | yes | Number of already-unlocked records in the total results. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | | `auto_unlock_points_consumed` | integer \| null | no | Number of points consumed by auto-unlock on this request. | - `400` : Pagination too deep (offset > 1,000,000 — use the matching `/export` endpoint). - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. Response includes `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### GET `/search/domain/{domain}/urls` **List URLs for a domain** Return a paginated list of distinct URLs for the given domain with their occurrence counts. **Query parameters** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). - `search` optional wildcard filter on URL. **Access and visibility** - If your plan includes `domain_search`, full URLs are returned. - Otherwise URLs are redacted except for the first 10 items per page. If `search` is provided and you are not entitled, results are redacted. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to list URLs for, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional wildcard filter on url. | | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | **Responses:** - `200` : Results returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of items for the current page. | | `total` | integer | yes | Total number of matching items. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### POST `/search/domain/{domain}/urls/export` **Export all URLs for a domain as CSV/TXT/JSON** Generate an on-demand export of all distinct URLs for the given domain. The export contains a single `URL` column and is capped at 100,000 rows. **Plan requirement** Requires a paid plan with `domain_search` enabled. **Parameters** - You can provide `search` either as a query parameter or in the JSON body. If both are provided, the JSON body value takes precedence. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to export URLs for, e.g. `example.com`. | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional search term to filter results when using query parameters. If also provided in the JSON body, the body value takes precedence. | | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Request body** (optional, `application/json`): Type: `object (DomainURLsExportBody) \| null` **Responses:** - `200` : URLs file (CSV/TXT/JSON) streamed successfully. - `401` : Authentication required, or invalid/expired API key. - `403` : Domain search not available on your plan, domain blocked, account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### POST `/search/domain/{domain}/{leak_type}/export` **Export domain leaks to CSV/TXT/JSON** Queue an export of all leaks for the given domain and leak type (valid values: `employees`, `customers`, `third_parties`, `all`). The job runs asynchronously and the file will appear in the Exports section when ready. **Parameters** - You can provide `search`, `is_email`, and `format` either as query parameters or in the JSON body. If both are provided, the JSON body values take precedence. **Query parameters** - `search` optional term to narrow results. - `is_email` optional boolean to filter emails (true), usernames (false), or both (null). - `format` optional output format: `csv` (default), `txt`, or `json`. **Limits and plan requirement** - You can have at most 5 exports with status PENDING or IN_PROGRESS (per account). - Requires a paid plan with `domain_search` enabled. **Response** `ExportResponse` with `status`, `message`, and `export_id`. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to export leaks for, e.g. `example.com`. | | `leak_type` | string ('employees', 'third_parties', 'customers', 'all') | yes | Which leaks to export: `employees`, `customers`, `third_parties`, or `all`. _(enum: 'employees', 'third_parties', 'customers', 'all')_ | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional term to narrow results when using query parameters. If also provided in the JSON body, the body value takes precedence. | | `is_email` | boolean \| null | no | Optional boolean to filter emails (true), usernames (false), or both (null). If also provided in the JSON body, the body value takes precedence. | | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Request body** (optional, `application/json`): Type: `object (DomainExportBody) \| null` **Responses:** - `200` : Export queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : Maximum concurrent exports reached (PENDING/IN_PROGRESS limit). - `401` : Authentication required, or invalid/expired API key. - `403` : Domain search not available on your plan, domain blocked, account banned, or pending email verification. - `409` : An identical export is already queued or running. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### POST `/search/domain/{domain}/{leak_type}/unlock` **Unlock leaks for a domain** Unlock leaks for the given domain and leak type (valid values: `employees`, `customers`, `third_parties`). Use `max` to cap how many new leaks to unlock. The effective limit is capped by your available points. Behavior - Only previously locked items are unlocked. Already unlocked items are left unchanged. - Only newly unlocked items consume points. - **Hard cap**: synchronous unlocks are limited to 10,000. Use `/search/domain/{domain}/{leak_type}/unlock/task` for higher volumes. Plan requirement Requires a paid plan with `domain_search` enabled. **Errors** - 400 Insufficient points, no matching data, all items already unlocked, more than 10,000 unlocks (use the /unlock/task endpoint), unlock not needed on your plan. - 401 Authentication required, or invalid/expired API key. - 403 Domain search not available on your plan, this domain is blocked, account banned, or pending email verification. - 404 The provided list_id does not exist or is not yours. - 422 Request validation error. - 429 Rate limit exceeded. - 503 Domain search under maintenance, or public API temporarily disabled. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to unlock leaks for, e.g. `example.com`. | | `leak_type` | string ('employees', 'third_parties', 'customers', 'all') | yes | Which leaks to unlock: `employees`, `customers`, or `third_parties`. _(enum: 'employees', 'third_parties', 'customers', 'all')_ | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional search term to filter results. | | `is_email` | boolean \| null | no | Filter on e-mails (`true`), usernames (`false`) or both (`null`). | | `max` | integer \| null | no | Maximum number of leaks to unlock. | | `list_id` | integer \| null | no | Assign this list id to new unlocks. | **Responses:** - `200` : Unlocked leaks returned successfully. - `400` : Insufficient points, no matching data, all items already unlocked, more than 10,000 unlocks (use the /unlock/task endpoint), unlock not needed on your plan. - `401` : Authentication required, or invalid/expired API key. - `403` : Domain search not available on your plan, this domain is blocked, account banned, or pending email verification. - `404` : The provided list_id does not exist or is not yours. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. #### POST `/search/domain/{domain}/{leak_type}/unlock/task` **Queue an async mass unlock task for a domain** Creates an asynchronous unlock **Task** for the given domain and leak type. Returns a `task_id` for polling. **Concurrency limit** - advanced / raw: max 2 concurrent tasks per user. - email / domain: max 5 concurrent tasks per user. - Returns 429 if limit exceeded. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `domain` | string | yes | Domain to unlock leaks for, e.g. `example.com`. | | `leak_type` | string ('employees', 'third_parties', 'customers', 'all') | yes | Which leaks to unlock: `employees`, `customers`, or `third_parties`. _(enum: 'employees', 'third_parties', 'customers', 'all')_ | **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | Optional search term. | | `is_email` | boolean \| null | no | True=email only, false=username only, null=both. | | `max` | integer \| null | no | Optional cap for new unlocks. | | `list_id` | integer \| null | no | Assign this list id to new unlocks. | **Responses:** - `200` : Unlock task queued; returns a task_id to poll. - `400` : Insufficient points, or unlock not needed on your plan (all data already accessible). - `401` : Authentication required, or invalid/expired API key. - `403` : Domain search not available on your plan, this domain is blocked, account banned, or pending email verification. - `404` : The provided list_id does not exist or is not yours. - `422` : Validation Error - `429` : Rate limit exceeded, or too many concurrent unlock tasks (max 5 for domain). - `503` : Domain search under maintenance, or public API temporarily disabled. #### POST `/search/domains/locked-exists` **Batch exists-check of still-locked leaks by domain and category (account-specific)** For up to 100 domains, returns per-category booleans indicating whether there are leaks that are still locked for the current account (total minus the account's unlocked items). Use include_counts=true to also get locked/total/unlocked counts. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `domains` | array | yes | List of domains, max 100. | | `categories` | array<(recursive)> \| null | no | Subset among employees, customers, third_parties. If omitted -> all. | | `include_counts` | boolean | no | If true, also include counts (locked/total/unlocked). _(default=`False`)_ | **Responses:** - `200` : Per-domain, per-category locked-exists results returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `results` | array | yes | | - `401` : Authentication required, or invalid/expired API key. - `403` : Your plan does not allow domain mass checks, account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Domain search under maintenance, or public API temporarily disabled. ### Search Email #### POST `/search/email` **Search leaks by email or username** Search for leaks associated with a specific email or username. Provide the search payload in the JSON body. Pagination - `page` starts at 1. - `page_size` is 1 to 100 (default 100). Access and visibility - If your plan includes `email_search`, full URLs and item IDs are returned. - Otherwise URLs are redacted except for the first 10 items per page and IDs are omitted. - Credentials are returned only when `unlocked=true` and your plan includes `email_search`. Auto-unlock Pass `auto_unlock=true` to automatically unlock locked items on the current page. Points are consumed per newly unlocked item. If insufficient points, partial unlock is performed. The response field `auto_unlock_points_consumed` indicates how many points were used. Response `EmailSearchResponse` with `items`, `total`, `total_unlocked`, `page`, `page_size`. Special case: if `email` is a full SHA-1 (40 hex chars, no '@'), search is done on username_hash exactly. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–100, default 100). _(minimum=1, maximum=100, default=`100`)_ | | `auto_unlock` | boolean | no | Automatically unlock locked items on the current page using your points. _(default=`False`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `email` | string | yes | Email or username to search. _(maxLength=254)_ | | `search` | string \| null | no | Optional free-text filter. | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | **Responses:** - `200` : Search results successfully returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of leak records for the current page. | | `total` | integer | yes | Total number of matching leak records. | | `total_unlocked` | integer | yes | Number of already-unlocked records in the total results. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | | `blacklisted_value` | string \| null | no | If a filter value matched a blacklist entry, this field contains the matched value. | | `auto_unlock_points_consumed` | integer \| null | no | Number of points consumed by auto-unlock on this request. | - `401` : Authentication required, or invalid/expired API key. - `403` : Account banned, or pending email verification. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Email search under maintenance, or public API temporarily disabled. #### POST `/search/email/export` **Export leaks for an email to CSV/TXT/JSON** Queue an export of all leaks that match the given email or username and optional filters. Provide the search payload in the JSON body. Request body - `email` the email or username to search. - `search` optional free-text filter applied to URL or username. - `is_email` optional boolean: `true` emails only, `false` usernames only, `null` both. Query parameters - `format` optional output format: `csv` (default), `txt`, or `json`. Limits and plan requirement - You can have at most 5 exports with status PENDING or IN_PROGRESS (per account). - Requires a paid plan with `email_search` enabled. Response `ExportResponse` with `status`, `message`, and `export_id`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `email` | string | yes | Email or username to search. _(maxLength=254)_ | | `search` | string \| null | no | Optional free-text filter. | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | **Responses:** - `200` : Export queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : Maximum concurrent exports reached (PENDING/IN_PROGRESS limit). - `401` : Authentication required, or invalid/expired API key. - `403` : Email search not available on your plan, search blocked, account banned, or pending email verification. - `409` : An identical export is already queued or running. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Email search under maintenance, or public API temporarily disabled. #### POST `/search/email/unlock` **Unlock leaks for an email** Unlock leaks for the given email or username. Provide the search payload in the JSON body. Use the `max` query parameter to cap how many new leaks to unlock. The effective limit is capped by your available points. Request body - `email` the email or username to search. - `search` optional free-text filter applied to URL or username. - `is_email` optional boolean: `true` emails only, `false` usernames only, `null` both. Behavior - Only previously locked items are unlocked. Already unlocked items are left unchanged. - Only newly unlocked items consume points. - If nothing new can be unlocked but matching unlocked items exist, those unlocked items are returned. - **Hard cap**: synchronous unlocks are limited to 10,000. Use `/search/email/unlock/task` for higher volumes. Plan requirement Requires a paid plan with `email_search` enabled. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `max` | integer \| null | no | 0 or omitted = use all your points | | `list_id` | integer \| null | no | Assign this list id to new unlocks. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `email` | string | yes | Email or username to search. _(maxLength=254)_ | | `search` | string \| null | no | Optional free-text filter. | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | **Responses:** - `200` : Unlocked leaks returned successfully. - `400` : Insufficient points, no matching data, all items already unlocked, more than 10,000 unlocks (use the /unlock/task endpoint), unlock not needed on your plan. - `401` : Authentication required, or invalid/expired API key. - `403` : Email search not available on your plan, this search is blocked, account banned, or pending email verification. - `404` : The provided list_id does not exist or is not yours. - `422` : Validation Error - `429` : Rate limit exceeded (10 req/sec). See Retry-After / X-RateLimit-* headers. - `503` : Email search under maintenance, or public API temporarily disabled. #### POST `/search/email/unlock/task` **Queue an async mass unlock task for an email** Creates an **asynchronous** unlock Task for the given email/username + optional filters. Returns a `task_id` you can poll with the task-status endpoint. max semantics - Omit `max` or set `max=0` → use all your available points. - Positive `max` → unlock up to that number (and ≤ your points). **Concurrency limit** - advanced / raw: max 2 concurrent tasks per user. - email / domain: max 5 concurrent tasks per user. - Returns 429 if limit exceeded. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `max` | integer \| null | no | 0 or omitted = use all your points | | `list_id` | integer \| null | no | Assign this list id to new unlocks. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `email` | string | yes | Email or username to search. _(maxLength=254)_ | | `search` | string \| null | no | Optional free-text filter. | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | **Responses:** - `200` : Unlock task queued; returns a task_id to poll. - `400` : Insufficient points, or unlock not needed on your plan (all data already accessible). - `401` : Authentication required, or invalid/expired API key. - `403` : Email search not available on your plan, this search is blocked, account banned, or pending email verification. - `404` : The provided list_id does not exist or is not yours. - `422` : Validation Error - `429` : Rate limit exceeded, or too many concurrent unlock tasks (max 5 for email). - `503` : Email search under maintenance, or public API temporarily disabled. #### POST `/search/emails/locked-exists` **Batch exists-check of still-locked leaks by email or username (account-specific)** For up to 100 emails or usernames, returns booleans and optional counts indicating whether there are leaks that are still locked for the current account. Use include_counts=true to also get locked/total/unlocked counts. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `emails` | array | yes | List of emails or usernames, max 100. | | `include_counts` | boolean | no | If true, also include counts (locked/total/unlocked). _(default=`False`)_ | **Responses:** - `200` : Per-email locked-exists results returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `results` | array | yes | | - `401` : Authentication required, or invalid/expired API key. - `403` : Your plan does not allow email/username mass checks, account banned, or pending email verification. - `422` : Validation Error - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. - `503` : Email search under maintenance, or public API temporarily disabled. ### Stats #### GET `/live-stats` **Get live landing stats** Lightweight live snapshot used by the public landing page (raw lines total, today, ingestion rate, active sources, dark web posts). Refreshed every ~2 min. Public endpoint (no authentication). Returns `{ "message": "Live stats not available" }` when the cache is empty. **Responses:** - `200` : Live stats returned successfully. #### GET `/stats` **Get statistics** Return the cached service statistics snapshot. If stats are not available in cache, the endpoint returns `{ "message": "Stats not available" }`. **Responses:** - `200` : Statistics returned successfully. #### GET `/transparency-stats` **Get transparency report figures** Figures published on the public transparency page: active exclusion rules by field, suspended accounts, removal requests for the last closed quarter, legal demands received from authorities, and data quality counters. Refreshed every 6 hours. Public endpoint (no authentication). Returns `{ "message": "Stats not available" }` when the cache is empty. **Responses:** - `200` : Transparency figures returned successfully. ### Tasks #### GET `/tasks/{task_id}` **Get background task status** Return the live status for a background task started by list operations Use the `task_id` returned by the create endpoint. **Usage** Poll this endpoint every 1s until `completed=true`. If the task is unknown or expired, the service returns `completed=true` with no totals, which can be treated as finished. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | Background task id returned by the create endpoint. | **Responses:** - `200` : Task status returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | | | `running` | boolean | yes | | | `completed` | boolean | yes | | | `total` | integer \| null | no | | | `updated` | integer \| null | no | | | `version_conflicts` | integer \| null | no | | - `400` : Invalid task_id format. - `401` : Authentication required. - `404` : Task not found. - `422` : Validation Error ### Team #### GET `/profile/team` **Get team overview** Returns the full team structure: owner, active members, pending invitations, plan summary, and member quota. **Responses:** - `200` : Team overview Response shape: | Field | Type | Required | Description | |---|---|---|---| | `owner` | object (TeamMemberResponse) | yes | TeamMemberResponse | | `members` | array | yes | Active team members. | | `invitations` | array | yes | Pending and past invitations. | | `plan` | object (TeamPlanSummary) | yes | TeamPlanSummary | | `quota` | object (TeamQuotaResponse) | yes | TeamQuotaResponse | - `401` : Unauthorized - `403` : Forbidden - `404` : Not Found #### POST `/profile/team/invitations` **Create a team invitation** Invite a user to join your team by email. An invitation email is sent automatically. Returns 409 if the user already belongs to a team or an invitation is already pending. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `email` | string | yes | | | `name` | string \| null | no | | | `role` | string ('member', 'admin') | no | _(default=`member`)_ | **Responses:** - `200` : Invitation created Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | Invitation ID. | | `invited_email` | string | yes | Email address of the invited user. | | `invited_name` | string \| null | no | Name of the invited user. | | `role` | string | yes | Assigned role: 'member' or 'admin'. | | `status` | string | yes | Invitation status: 'pending', 'accepted', 'revoked', or 'expired'. | | `expires_at` | string | yes | Expiration date of the invitation. | | `created_at` | string | yes | Creation date of the invitation. | | `invited_by` | object (TeamInvitationActor) \| null | no | User who sent the invitation. | | `accepted_user_id` | integer \| null | no | ID of the user who accepted the invitation. | | `token` | string \| null | no | Invitation token (only returned on creation). | - `401` : Unauthorized - `403` : Forbidden - `404` : Not Found - `409` : Conflict - `422` : Unprocessable Content #### POST `/profile/team/invitations/{invitation_id}/resend` **Resend a team invitation** Resend the invitation email for a pending invitation. Generates a new token and extends the expiration. Rate limited to 1 resend per invitation per 60 seconds. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `invitation_id` | integer | yes | ID of the team invitation. | **Responses:** - `200` : Invitation resent Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | Invitation ID. | | `invited_email` | string | yes | Email address of the invited user. | | `invited_name` | string \| null | no | Name of the invited user. | | `role` | string | yes | Assigned role: 'member' or 'admin'. | | `status` | string | yes | Invitation status: 'pending', 'accepted', 'revoked', or 'expired'. | | `expires_at` | string | yes | Expiration date of the invitation. | | `created_at` | string | yes | Creation date of the invitation. | | `invited_by` | object (TeamInvitationActor) \| null | no | User who sent the invitation. | | `accepted_user_id` | integer \| null | no | ID of the user who accepted the invitation. | | `token` | string \| null | no | Invitation token (only returned on creation). | - `400` : Bad Request - `401` : Unauthorized - `403` : Forbidden - `404` : Not Found - `422` : Validation Error - `429` : Too soon — wait 60 seconds before resending this invitation. #### POST `/profile/team/invitations/{invitation_id}/revoke` **Revoke a team invitation** Revoke a pending invitation. The invited user will no longer be able to accept it. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `invitation_id` | integer | yes | ID of the team invitation. | **Responses:** - `200` : Invitation revoked Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | Invitation ID. | | `invited_email` | string | yes | Email address of the invited user. | | `invited_name` | string \| null | no | Name of the invited user. | | `role` | string | yes | Assigned role: 'member' or 'admin'. | | `status` | string | yes | Invitation status: 'pending', 'accepted', 'revoked', or 'expired'. | | `expires_at` | string | yes | Expiration date of the invitation. | | `created_at` | string | yes | Creation date of the invitation. | | `invited_by` | object (TeamInvitationActor) \| null | no | User who sent the invitation. | | `accepted_user_id` | integer \| null | no | ID of the user who accepted the invitation. | | `token` | string \| null | no | Invitation token (only returned on creation). | - `400` : Bad Request - `401` : Unauthorized - `403` : Forbidden - `404` : Not Found - `422` : Validation Error #### POST `/profile/team/leave` **Leave the team** Voluntarily leave the team you are a member of. Team owners cannot use this endpoint. **Responses:** - `200` : Successfully left the team - `400` : Bad Request - `401` : Unauthorized - `403` : Forbidden - `404` : Not Found #### DELETE `/profile/team/members/{member_id}` **Remove a team member** Remove a member from the team. The member's account is detached but not deleted. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `member_id` | integer | yes | ID of the team member. | **Responses:** - `200` : Member removed Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | User ID of the team member. | | `email` | string | yes | Email address of the team member. | | `first_name` | string \| null | no | First name. | | `last_name` | string \| null | no | Last name. | | `organization` | string \| null | no | Organization name. | | `team_role` | string \| null | no | Role within the team: 'member' or 'admin'. | | `joined_at` | string | yes | Date when the member joined the team. | | `last_login` | string \| null | no | Last login date. | | `is_owner` | boolean | no | Whether this member is the team owner. _(default=`False`)_ | - `401` : Unauthorized - `403` : Forbidden - `404` : Not Found - `422` : Validation Error #### PATCH `/profile/team/members/{member_id}` **Update a team member's role** Change a team member's role to 'member' or 'admin'. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `member_id` | integer | yes | ID of the team member. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `team_role` | string ('member', 'admin') | yes | | **Responses:** - `200` : Member updated Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | User ID of the team member. | | `email` | string | yes | Email address of the team member. | | `first_name` | string \| null | no | First name. | | `last_name` | string \| null | no | Last name. | | `organization` | string \| null | no | Organization name. | | `team_role` | string \| null | no | Role within the team: 'member' or 'admin'. | | `joined_at` | string | yes | Date when the member joined the team. | | `last_login` | string \| null | no | Last login date. | | `is_owner` | boolean | no | Whether this member is the team owner. _(default=`False`)_ | - `401` : Unauthorized - `403` : Forbidden - `404` : Not Found - `422` : Unprocessable Content ### Unlock #### POST `/unlock` **Unlock selected leaks by ID** Unlock leaks by their IDs for the authenticated account if they have enough points. **Request body** - `leak_ids` array of leak IDs to unlock (max 10,000). Duplicates are ignored while preserving order. **Behavior** - Requires an active paid subscription. - 1 point per leak. Points are charged up front and partially refunded if some items were already unlocked during processing. - Only found leaks are processed. If none are found, the request fails. - The response reflects the final unlocked state from the account-level index. **Visibility** - `id` is included only for paid users. - Credentials are included only for items with `unlocked=true`. **Errors** - 400 No leak IDs provided, too many leak IDs, insufficient points, or all selected data already unlocked. - 401 Unauthorized. - 403 Forbidden: subscription does not allow unlocking. - 404 The provided target_list_id does not exist. - 410 The selected data is no longer available (refresh your search). - 422 Validation error. - 500 Billing or indexation error while unlocking. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `leak_ids` | array | yes | List of leak document IDs to unlock. | | `target_list_id` | integer \| null | no | Optional list id to assign to newly unlocked items. | **Responses:** - `200` : Selected leaks successfully unlocked. - `400` : No leak IDs provided, too many leak IDs, insufficient points, or all selected data already unlocked. - `401` : Unauthorized. - `403` : Forbidden. The user's subscription does not allow unlocking data. - `404` : The provided target_list_id does not exist or is not yours. - `410` : The selected data is no longer available (removed during cleanup). Refresh your search results. - `422` : Validation error. - `500` : Billing or indexation error while unlocking. ### Unlocked Combolists #### GET `/profile/unlocked/combolists` **List unlocked combolist credentials** List the Combolist credentials you have unlocked, in clear. **Separate from unlocked leaks** This space is disjoint from `GET /profile/unlocked`: a pair unlocked from a combolist search never shows up there, and a stealer-log record never shows up here. Unlocked lists, however, are shared between the two datasets. **Filters** - `search` matches on username or password. - `is_email` splits email identifiers from username-only ones. - `list_id` and `list_none` are mutually exclusive. - `status` is the remediation status you set on the record. - `unlocked_at_min` keeps only what you unlocked at or after that instant, which is how you page an incremental sync. It must carry a timezone. **Pagination** - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). **Response** `PaginatedUnlockedCombolistsResponse`. `total` is the filtered count and `total_unlocked` the unfiltered one, so a client can show "x of y" without a second call. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1-1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `search` | string \| null | no | Filter on username or password. | | `is_email` | boolean \| null | no | `true` keeps only email identifiers, `false` only username-only pairs. Omit for both. | | `list_id` | integer \| null | no | Keep only the records assigned to this list. | | `list_none` | boolean | no | Keep only the records with no list assigned. _(default=`False`)_ | | `status` | string ('new', 'in_progress', 'fixed', 'accepted_risk') \| null | no | Keep only the records in this remediation status. | | `unlocked_at_min` | string \| null | no | Keep only what was unlocked at or after this instant. Must carry a timezone, for example `2026-09-01T00:00:00Z`. | **Responses:** - `200` : Unlocked credentials returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | | | `total` | integer | yes | Records matching the filters. _(minimum=0.0)_ | | `total_unlocked` | integer | yes | Records you have unlocked in total, filters ignored. _(minimum=0.0)_ | | `page` | integer | yes | _(minimum=1.0)_ | | `page_size` | integer | yes | _(minimum=1.0)_ | - `401` : Authentication required, or invalid/expired API key. - `404` : Combolist search is not enabled on this deployment. - `422` : Validation error, including `unlocked_at_min` without a timezone. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. #### POST `/profile/unlocked/combolists/advanced` **List unlocked combolist credentials with advanced filters** Same listing as `GET /profile/unlocked/combolists`, with the advanced filter set applied on top. **Filters** - `filters` takes the same contract as `POST /search/combolist/advanced`: `username`, `password`, `email_domain` with their `_not` and `_match_type` variants, the hash prefixes, `is_email`, `password_strength`, `added_from`, `added_to` and `force_and`. There is no `url` filter, combolist records carry no URL. - The unlocked-specific filters (`search`, `is_email`, `list_id`, `list_none`, `status`, `unlocked_at_min`) still apply and combine with AND. - `filters` is optional: an empty set behaves exactly like the global listing. **Why POST** The body reuses the advanced search contract verbatim rather than a second query-string parser that could drift from it. **Export** `POST /profile/unlocked/combolists/export` takes the same `filters`, so the file holds exactly the rows this listing shows. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1-1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | | | `is_email` | boolean \| null | no | | | `list_id` | integer \| null | no | | | `list_none` | boolean | no | _(default=`False`)_ | | `status` | string ('new', 'in_progress', 'fixed', 'accepted_risk') \| null | no | | | `unlocked_at_min` | string \| null | no | | | `filters` | object (CombolistAdvancedSearchRequest) \| null | no | | **Responses:** - `200` : Unlocked credentials returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | | | `total` | integer | yes | Records matching the filters. _(minimum=0.0)_ | | `total_unlocked` | integer | yes | Records you have unlocked in total, filters ignored. _(minimum=0.0)_ | | `page` | integer | yes | _(minimum=1.0)_ | | `page_size` | integer | yes | _(minimum=1.0)_ | - `401` : Authentication required, or invalid/expired API key. - `404` : Combolist search is not enabled on this deployment, the list does not exist, or the page is out of range. - `422` : Validation error, including a filter field absent from the combolist mapping. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. #### POST `/profile/unlocked/combolists/export` **Export unlocked combolist credentials** Export the Combolist credentials you have already unlocked, as a background job. **Scope** - Only your unlocked pairs, never the locked ones. To widen the set, unlock first with `POST /search/combolist/unlock/task`. - The body takes the same filters as `GET /profile/unlocked/combolists` (`search`, `is_email`, `list_id`, `list_none`, `status`, `unlocked_at_min`), so what you see in the list is what the file contains. - `unlocked_at_min` is the incremental hook: pass the timestamp of your last successful export to get only what came after. It must carry a timezone. - `filters` takes the advanced set of `POST /profile/unlocked/combolists/advanced`. Send it to export exactly the rows that advanced listing shows. **Separate from unlocked leaks** This export never contains a stealer-log record. Its counterpart is `POST /profile/unlocked/export`. **Limits** - Formats: `csv`, `txt`, `json`. - At most 5 exports pending or in progress per account, all types combined. - An identical export already queued is refused rather than duplicated. **Retrieval** The response carries an `export_id`. Follow it and download the file from the Exports endpoints. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `format` | string ('csv', 'txt', 'json') | no | Output format: `csv`, `txt` or `json`. _(enum: 'csv', 'txt', 'json')_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | | | `is_email` | boolean \| null | no | | | `list_id` | integer \| null | no | | | `list_none` | boolean | no | _(default=`False`)_ | | `status` | string ('new', 'in_progress', 'fixed', 'accepted_risk') \| null | no | | | `unlocked_at_min` | string \| null | no | | | `filters` | object (CombolistAdvancedSearchRequest) \| null | no | Advanced filter set, same contract as the advanced listing. When set, the file holds exactly the rows that listing shows. | **Responses:** - `200` : Export queued. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : Too many exports already in progress. - `401` : Authentication required, or invalid/expired API key. - `404` : Combolist search is not enabled on this deployment. - `409` : An identical export is already queued. - `422` : Validation error, including `unlocked_at_min` without a timezone. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. #### DELETE `/profile/unlocked/combolists/{leak_id}/comment` **Delete the comment on one unlocked combolist credential** Remove the comment attached to a single unlocked combolist credential. Idempotent: deleting an absent comment succeeds. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Combolist credential ID, in the `cl1_` namespace. | **Responses:** - `200` : Comment removed. - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked combolist credential not found, or combolist search is not enabled on this deployment. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. #### PUT `/profile/unlocked/combolists/{leak_id}/comment` **Set the comment on one unlocked combolist credential** Attach or replace a free-text comment on a single unlocked combolist credential. Behavior - The comment replaces any previous one. Use the DELETE variant to clear it. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Combolist credential ID, in the `cl1_` namespace. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `comment` | string \| null | no | | **Responses:** - `200` : Comment saved. - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked combolist credential not found, or combolist search is not enabled on this deployment. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. #### PUT `/profile/unlocked/combolists/{leak_id}/list` **Assign or unassign a list for one unlocked combolist credential** Assign a specific unlocked list to a single unlocked combolist credential, or unassign it. Behavior - Provide `list_id` to assign. Use `null` to remove the assignment. - Lists are shared with unlocked leaks: the same list can hold records from both datasets. Counts per dataset are read from `GET /profile/unlocked/lists?dataset=combolist`. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Combolist credential ID, in the `cl1_` namespace. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `list_id` | integer \| null | no | | **Responses:** - `200` : List assignment updated. - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked combolist credential not found, or combolist search is not enabled on this deployment. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. #### PUT `/profile/unlocked/combolists/{leak_id}/status` **Set the remediation status of one unlocked combolist credential** Set the remediation status of a single unlocked combolist credential. Behavior - Allowed values: `new`, `in_progress`, `fixed`, `accepted_risk`. - Newly unlocked credentials start at `new`. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Combolist credential ID, in the `cl1_` namespace. | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `status` | string ('new', 'in_progress', 'fixed', 'accepted_risk') | yes | | **Responses:** - `200` : Status updated. - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked combolist credential not found, or combolist search is not enabled on this deployment. - `422` : Validation error. - `429` : Rate limit exceeded. See Retry-After / X-RateLimit-* headers. ### Unlocked Leaks #### GET `/profile/unlocked` **List your unlocked leaks** Return a paginated list of unlocked leaks for the authenticated account. Pagination - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). Filtering - `search` optional free-text filter applied to URL or username. - `is_email` optional boolean: `true` emails only, `false` usernames only, `null` both. - `list_id` optional integer: filter leaks assigned to this list. - `list_none` optional boolean: filter leaks with no list assignment. Response semantics - When no filter is provided at all (no `search`, no `is_email`, no list filter), `total` equals your global count of unlocked items. - When any filter is provided (including only `is_email`), `total` equals the filtered count, while `total_unlocked` remains your global total. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 Page out of range. - 422 Request validation error. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `search` | string \| null | no | Optional free-text filter on URL or username. | | `is_email` | boolean \| null | no | true=email, false=username | | `list_id` | integer \| null | no | Filter by list id. | | `list_none` | boolean \| null | no | Only leaks without a list when true | | `status` | string \| null | no | Filter by remediation status: new, in_progress, fixed, accepted_risk | **Responses:** - `200` : Unlocked leaks returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of unlocked leak records for the current page. | | `total` | integer | yes | Total number of matching records. | | `total_unlocked` | integer | yes | Total number of unlocked records. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | - `401` : Authentication required. - `404` : Page out of range. - `422` : Validation Error #### GET `/profile/unlocked/advanced` **List your unlocked leaks with advanced filters** Return a paginated list of your unlocked leaks filtered by the same fields supported by `LeakSearchFilters`. Filters are passed as query parameters. Optional `search` can further narrow results. Pagination - `page` starts at 1. - `page_size` is 1 to 1000 (default 100). Filtering - Any combination of `LeakSearchFilters` fields. - `search` optional free-text filter applied to URL or username. - `list_id` optional integer: filter leaks assigned to this list. - `list_none` optional boolean: filter leaks with no list assignment. Response semantics - `total` and `total_unlocked` both equal the number of unlocked items matching the filters. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `page` | integer | no | Page number (starts at 1). _(minimum=1, default=`1`)_ | | `page_size` | integer | no | Items per page (1–1000, default 100). _(minimum=1, maximum=1000, default=`100`)_ | | `search` | string \| null | no | Optional free-text filter on URL or username. | | `list_id` | integer \| null | no | Filter by list id. | | `list_none` | boolean \| null | no | Only leaks without a list when true. | | `status` | string \| null | no | Filter by remediation status: new, in_progress, fixed, accepted_risk | | `username` | array \| null | no | Username values to match. | | `username_not` | array \| null | no | Username values to exclude. | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Username matching mode; default contains. | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded username matching mode; default contains. | | `password` | array \| null | no | Password values to match. | | `password_not` | array \| null | no | Password values to exclude. | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Password matching mode; default contains. | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded password matching mode; default contains. | | `url` | array \| null | no | URL values to match. | | `url_not` | array \| null | no | URL values to exclude. | | `url_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL matching mode; default contains. | | `url_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL matching mode; default contains. | | `url_domain` | array \| null | no | URL domain values to match. | | `url_domain_not` | array \| null | no | URL domain values to exclude. | | `url_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL domain matching mode; default contains. | | `url_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL domain matching mode; default contains. | | `url_host` | array \| null | no | URL host values to match. | | `url_host_not` | array \| null | no | URL host values to exclude. | | `url_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL host matching mode; default contains. | | `url_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL host matching mode; default contains. | | `username_hash` | array \| null | no | Username SHA-1 hash prefix (hex). | | `password_hash` | array \| null | no | Password SHA-1 hash prefix (hex). | | `url_scheme` | array \| null | no | URL scheme(s) to include (multi). | | `url_scheme_not` | array \| null | no | URL scheme(s) to exclude (multi). | | `url_port` | array \| null | no | URL port(s) to include (multi). | | `url_port_not` | array \| null | no | URL port(s) to exclude (multi). | | `url_tld` | array \| null | no | URL TLD(s) to include (multi). | | `url_tld_not` | array \| null | no | URL TLD(s) to exclude (multi). | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | | `email_domain` | array \| null | no | Email domain values to match. | | `email_domain_not` | array \| null | no | Email domain values to exclude. | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email domain matching mode; default contains. | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email domain matching mode; default contains. | | `email_host` | array \| null | no | Email host values to match. | | `email_host_not` | array \| null | no | Email host values to exclude. | | `email_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email host matching mode; default contains. | | `email_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email host matching mode; default contains. | | `email_tld` | array \| null | no | Email TLD(s) to include (multi). | | `email_tld_not` | array \| null | no | Email TLD(s) to exclude (multi). | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | Filter by password strength category. too_weak = score 0-2, weak = 3-4, medium = 5-7, strong = 8+. | | `added_from` | string \| null | no | Only include leaks indexed on/after this UTC datetime. | | `added_to` | string \| null | no | Only include leaks indexed on/before this UTC datetime. | | `force_and` | boolean \| null | no | When true, values within each field are combined with AND instead of OR. | **Responses:** - `200` : Unlocked leaks returned successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `items` | array | yes | List of unlocked leak records for the current page. | | `total` | integer | yes | Total number of matching records. | | `total_unlocked` | integer | yes | Total number of unlocked records. | | `page` | integer | yes | Current page number. | | `page_size` | integer | yes | Number of items per page. | - `401` : Authentication required. - `404` : Page out of range. - `422` : Validation Error #### POST `/profile/unlocked/advanced/export` **Export your unlocked leaks to CSV/TXT/JSON (advanced filters)** Queue an export of your unlocked leaks filtered by the same fields supported by `LeakSearchFilters`. Provide advanced filters in the JSON request body. The job runs asynchronously and the file will appear in the Exports section when ready. Request body - JSON payload following the `LeakSearchFilters` schema. - The four scope fields below may also be sent as keys of this same flat body. A value given in the query string wins over the body one. Scope, as query parameters or body keys - `unlocked_at_min` optional ISO 8601 timestamp with timezone to include items unlocked at or after this instant. - `list_id` optional list id filter. - `list_none` optional boolean to only include leaks without list. - `status` optional remediation status: `new`, `in_progress`, `fixed` or `accepted_risk`. Omit it to export every status. - `format` optional output format: `csv` (default), `txt`, or `json`. Limits - At least one advanced filter is required. - You can have at most 5 exports with status PENDING or IN_PROGRESS. Response `ExportResponse` with `status`, `message`, and `export_id`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `list_id` | integer \| null | no | Filter by list id | | `list_none` | boolean \| null | no | Only leaks without list | | `status` | string ('new', 'in_progress', 'fixed', 'accepted_risk') \| null | no | Remediation status filter. Omit to export every status. | | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | | `unlocked_at_min` | string \| null | no | Only include items unlocked at or after this ISO 8601 timestamp (with timezone). | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `username` | array \| null | no | Username values to match. | | `username_not` | array \| null | no | Username values to exclude. | | `username_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Username matching mode. Omitted means contains. | | `username_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded username matching mode. Omitted means contains. | | `password` | array \| null | no | Password values to match. | | `password_not` | array \| null | no | Password values to exclude. | | `password_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Password matching mode. Omitted means contains. | | `password_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded password matching mode. Omitted means contains. | | `url` | array \| null | no | URL values to match. | | `url_not` | array \| null | no | URL values to exclude. | | `url_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL matching mode. Omitted means contains. | | `url_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL matching mode. Omitted means contains. | | `url_domain` | array \| null | no | URL domain values to match. | | `url_domain_not` | array \| null | no | URL domain values to exclude. | | `url_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL domain matching mode. Omitted means contains. | | `url_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL domain matching mode. Omitted means contains. | | `url_host` | array \| null | no | URL host values to match. | | `url_host_not` | array \| null | no | URL host values to exclude. | | `url_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | URL host matching mode. Omitted means contains. | | `url_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded URL host matching mode. Omitted means contains. | | `username_hash` | array \| null | no | Username SHA-1 hash prefix (hex). | | `password_hash` | array \| null | no | Password SHA-1 hash prefix (hex). | | `url_scheme` | array \| null | no | URL scheme(s) to include (multi). | | `url_scheme_not` | array \| null | no | URL scheme(s) to exclude (multi). | | `url_port` | array \| null | no | URL port(s) to include (multi). | | `url_port_not` | array \| null | no | URL port(s) to exclude (multi). | | `url_tld` | array \| null | no | URL TLD(s) to include (multi). | | `url_tld_not` | array \| null | no | URL TLD(s) to exclude (multi). | | `is_email` | boolean \| null | no | Identifier type filter: true=email only, false=username only, null=both. | | `email_domain` | array \| null | no | Email domain values to match. | | `email_domain_not` | array \| null | no | Email domain values to exclude. | | `email_domain_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email domain matching mode. Omitted means contains. | | `email_domain_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email domain matching mode. Omitted means contains. | | `email_host` | array \| null | no | Email host values to match. | | `email_host_not` | array \| null | no | Email host values to exclude. | | `email_host_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Email host matching mode. Omitted means contains. | | `email_host_not_match_type` | string ('contains', 'starts_with', 'ends_with') \| null | no | Excluded email host matching mode. Omitted means contains. | | `email_tld` | array \| null | no | Email TLD(s) to include (multi). | | `email_tld_not` | array \| null | no | Email TLD(s) to exclude (multi). | | `password_strength` | string ('too_weak', 'weak', 'medium', 'strong') \| null | no | Filter by password strength category. too_weak = score 0-2, weak = 3-4, medium = 5-7, strong = 8+. | | `added_from` | string \| null | no | Only include leaks indexed on/after this UTC datetime. | | `added_to` | string \| null | no | Only include leaks indexed on/before this UTC datetime. | | `force_and` | boolean \| null | no | When true, require all values within each field (AND within field). | | `unlocked_at_min` | string \| null | no | Only items unlocked at or after this instant. Needs a timezone. | | `list_id` | integer \| null | no | Filter by list id. | | `list_none` | boolean \| null | no | Only items that belong to no list. | | `status` | string ('new', 'in_progress', 'fixed', 'accepted_risk') \| null | no | Remediation status filter. Omit to export every status. | **Responses:** - `200` : Export queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : No filters provided, maximum concurrent exports reached. - `401` : Authentication required, or invalid/expired API key. - `422` : Validation Error #### POST `/profile/unlocked/export` **Export your unlocked leaks to CSV/TXT/JSON** Queue an export of your unlocked leaks with optional filters. The job runs asynchronously and the file will appear in the Exports section when ready. Body (JSON) - `search` optional free-text filter applied to URL or username. - `is_email` optional boolean: `true` emails only, `false` usernames only, `null` both. - `list_id` optional list id filter. - `list_none` optional boolean to only include leaks without list. - `unlocked_at_min` optional ISO 8601 timestamp (with timezone) to include items unlocked at or after this instant. - `status` optional remediation status: `new`, `in_progress`, `fixed` or `accepted_risk`. Omit it to export every status. `new` also matches records that were never triaged. Query parameters - `format` optional output format: `csv` (default), `txt`, or `json`. Limits - You can have at most 5 exports with status PENDING or IN_PROGRESS. Columns URL, USERNAME, PASSWORD, ADDED_AT, UNLOCKED_AT, STATUS. The TXT format stays `url:username:password`, one record per line. Response `ExportResponse` with `status`, `message`, and `export_id`. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `format` | string ('csv', 'txt', 'json') | no | Output format: csv (default), txt, or json. _(enum: 'csv', 'txt', 'json')_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `search` | string \| null | no | | | `is_email` | boolean \| null | no | | | `unlocked_at_min` | string \| null | no | | | `list_id` | integer \| null | no | | | `list_none` | boolean \| null | no | _(default=`False`)_ | | `status` | string ('new', 'in_progress', 'fixed', 'accepted_risk') \| null | no | Remediation status filter, same values as the unlocked listing. Omit to export every status. | **Responses:** - `200` : Export queued successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Export job status: 'queued', 'processing', 'completed', or 'failed'. | | `message` | string | yes | Human-readable status message. | | `export_id` | integer | yes | ID of the export job. Use this to check status or download the result. | - `400` : Maximum concurrent exports reached. - `401` : Authentication required, or invalid/expired API key. - `422` : Validation Error ### Unlocked Leaks Lists #### POST `/profile/unlocked/bulk-status` **Bulk update the remediation status across many unlocked leaks** Start a background task that updates the remediation status on all unlocked leaks matching the provided filters. Valid statuses: `new`, `in_progress`, `fixed`, `accepted_risk`. Filtering - Supports the same filtering fields used by the advanced unlocked search API. - Optional helpers, applied on top of `filters` when both are sent: `search`, `is_email`, `list_id_filter`, `list_none`, `status_filter`. Response - Returns a `task_id` that you can poll with `/profile/unlocked/list-tasks/{task_id}`. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `target_status` | string | yes | Target status: new, in_progress, fixed, accepted_risk | | `dataset` | string ('stealer_logs', 'combolist') | no | _(default=`stealer_logs`)_ | | `search` | string \| null | no | | | `is_email` | boolean \| null | no | | | `list_id_filter` | integer \| null | no | | | `list_none` | boolean \| null | no | | | `status_filter` | string \| null | no | | | `filters` | object (LeakSearchFilters) \| null | no | | **Responses:** - `200` : Bulk status task started. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | | | `running` | boolean | yes | | | `completed` | boolean | yes | | | `total` | integer \| null | no | | | `updated` | integer \| null | no | | | `version_conflicts` | integer \| null | no | | - `401` : Authentication required, or invalid/expired API key. - `422` : Validation error. #### GET `/profile/unlocked/list-tasks/{task_id}` **Get unlocked lists background task status** Return the status of a background task started by unlocked lists operations. This endpoint is only for tasks created by: - `POST /profile/unlocked/lists/bulk-assign` (bulk assign/unassign list) Usage - Use the exact `task_id` returned by one of these endpoints. Important - Do **not** use this endpoint for unlock tasks created by `/search/.../unlock/task`. For unlock tasks, poll `/tasks/{task_id}` instead. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | Task id returned by an unlocked-lists operation (delete / clear / bulk). _(minLength=1)_ | **Responses:** - `200` : Task status returned. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | | | `running` | boolean | yes | | | `completed` | boolean | yes | | | `total` | integer \| null | no | | | `updated` | integer \| null | no | | | `version_conflicts` | integer \| null | no | | - `400` : Invalid task_id for this endpoint (use /tasks/{task_id} for unlock tasks). - `401` : Authentication required, or invalid/expired API key. - `422` : Validation Error #### GET `/profile/unlocked/lists` **List your Unlocked Lists** Return all your Unlocked Lists in ascending creation order. Parameters - `with_counts` when true, each list includes `items_count`, the number of unlocked leaks currently assigned to that list. When false, `items_count` is returned as 0 to avoid extra computation. **Errors** - 401 Authentication required, or invalid/expired API key. - 422 Request validation error. **Query parameters:** | Name | Type | Required | Description | |---|---|---|---| | `with_counts` | boolean | no | When true, include `items_count` per list. _(default=`True`)_ | | `dataset` | string ('stealer_logs', 'combolist') | no | Dataset used to compute each list's item count. _(default=`stealer_logs`, enum: 'stealer_logs', 'combolist')_ | **Responses:** - `200` : Lists returned successfully. - `401` : Authentication required, or invalid/expired API key. - `422` : Validation Error #### POST `/profile/unlocked/lists` **Create an Unlocked List** Create a new list to organize your unlocked leaks. Rules - Maximum number of lists is determined by your current plan. - List names must be unique per account, case-insensitive. Response - Returns the created list. `items_count` is 0 at creation time. **Errors** - 400 No plan, or maximum number of lists reached. - 401 Authentication required, or invalid/expired API key. - 409 A list with the same name already exists. - 422 Request validation error. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `name` | string | yes | _(maxLength=80, minLength=1)_ | | `color` | string \| null | no | | **Responses:** - `200` : List created successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | | | `name` | string | yes | | | `color` | string \| null | yes | | | `created_at` | string \| null | yes | | | `updated_at` | string \| null | yes | | | `items_count` | integer \| null | no | | - `400` : No plan, or maximum number of lists reached. - `401` : Authentication required, or invalid/expired API key. - `409` : A list with the same name already exists. - `422` : Validation error. #### POST `/profile/unlocked/lists/bulk-assign` **Bulk assign or unassign a list across many unlocked leaks** Start a background task that assigns a target list to all unlocked leaks matching the provided filters. Use a null `target_list_id` to remove the assignment for matches. Filtering - Supports the same filtering fields used by the advanced unlocked search API. - Optional helpers, applied on top of `filters` when both are sent: `search`, `is_email`, `list_id_filter`, `list_none`, `status_filter`. Validation - `target_list_id` must be a positive integer when provided. Use null to unassign. Response - Returns a `task_id` that you can poll with `/profile/unlocked/list-tasks/{task_id}`. **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `target_list_id` | integer \| null | yes | | | `dataset` | string ('stealer_logs', 'combolist') | no | _(default=`stealer_logs`)_ | | `search` | string \| null | no | | | `is_email` | boolean \| null | no | | | `list_id_filter` | integer \| null | no | | | `list_none` | boolean \| null | no | | | `status_filter` | string \| null | no | | | `filters` | object (LeakSearchFilters) \| null | no | | **Responses:** - `200` : Bulk assign task started. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | | | `running` | boolean | yes | | | `completed` | boolean | yes | | | `total` | integer \| null | no | | | `updated` | integer \| null | no | | | `version_conflicts` | integer \| null | no | | - `400` : Invalid target_list_id. - `401` : Authentication required, or invalid/expired API key. - `404` : Target list not found. - `422` : Validation error. #### DELETE `/profile/unlocked/lists/{list_id}` **Delete an Unlocked List** Delete the list and start a background cleanup of related leak assignments. Behavior - The list is removed immediately. - Any unlocked leak that referenced this list will be cleaned up by a background task. - `task_id` remains the primary Stealer Logs task for backward compatibility. - `task_ids` contains every Stealer Logs and Combolist cleanup task; poll each at `/profile/unlocked/list-tasks/{task_id}`. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 List not found. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `list_id` | integer | yes | ID of the list to delete _(minimum=1)_ | **Responses:** - `200` : List deleted; cleanup task started. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | | | `running` | boolean | yes | | | `completed` | boolean | yes | | | `total` | integer \| null | no | | | `updated` | integer \| null | no | | | `version_conflicts` | integer \| null | no | | | `task_ids` | array | yes | | - `401` : Authentication required, or invalid/expired API key. - `404` : List not found. - `422` : Validation Error #### PATCH `/profile/unlocked/lists/{list_id}` **Update an Unlocked List** Update list metadata. Only provided fields are updated. Updatable fields - `name` must be unique per account, case-insensitive. - `color` optional UI attribute. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 List not found. - 409 A list with the same name already exists. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `list_id` | integer | yes | ID of the list to update _(minimum=1)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `name` | string \| null | no | | | `color` | string \| null | no | | **Responses:** - `200` : List updated successfully. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `id` | integer | yes | | | `name` | string | yes | | | `color` | string \| null | yes | | | `created_at` | string \| null | yes | | | `updated_at` | string \| null | yes | | | `items_count` | integer \| null | no | | - `401` : Authentication required, or invalid/expired API key. - `404` : List not found. - `409` : A list with the same name already exists. - `422` : Validation error. #### POST `/profile/unlocked/lists/{list_id}/clear` **Clear list assignment from leaks while keeping the list** Remove the list assignment from all unlocked leaks that currently reference this list. The list itself is kept. The operation runs in the background. `task_id` is the backward-compatible primary task and `task_ids` contains every dataset cleanup task. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `list_id` | integer | yes | ID of the list whose assignments will be cleared _(minimum=1)_ | **Responses:** - `200` : Clear task started. Response shape: | Field | Type | Required | Description | |---|---|---|---| | `task_id` | string | yes | | | `running` | boolean | yes | | | `completed` | boolean | yes | | | `total` | integer \| null | no | | | `updated` | integer \| null | no | | | `version_conflicts` | integer \| null | no | | | `task_ids` | array | yes | | - `401` : Authentication required, or invalid/expired API key. - `404` : List not found. - `422` : Validation Error #### DELETE `/profile/unlocked/{leak_id}/comment` **Delete the comment of one unlocked leak** Delete the user comment attached to a specific unlocked leak. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 Unlocked leak not found. - 422 Request validation error. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Unlocked leak identifier _(maxLength=64, minLength=1)_ | **Responses:** - `200` : Comment deleted. - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked leak not found. - `422` : Validation Error #### PUT `/profile/unlocked/{leak_id}/comment` **Create or update a comment for one unlocked leak** Create or update a user comment on a single unlocked leak. Empty or whitespace-only comments are treated as delete. Limits - Max 500 characters. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 Unlocked leak not found. - 422 Comment exceeds 500 characters. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Unlocked leak identifier _(maxLength=64, minLength=1)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `comment` | string | yes | _(maxLength=500, minLength=0)_ | **Responses:** - `200` : Comment saved (or removed when empty). - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked leak not found. - `422` : Comment exceeds 500 characters. #### PUT `/profile/unlocked/{leak_id}/list` **Assign or unassign a list for one unlocked leak** Assign a specific list to a single unlocked leak, or unassign it. Behavior - Provide `list_id` to assign. Use `null` to remove the assignment. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 Unlocked leak not found, or list not found. - 422 Request validation error. - 429 Rate limit exceeded. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Unlocked leak identifier _(maxLength=64, minLength=1)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `list_id` | integer \| null | no | null to remove list | **Responses:** - `200` : List assignment updated. - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked leak not found, or list not found. - `422` : Validation error. - `429` : Rate limit exceeded. #### PUT `/profile/unlocked/{leak_id}/status` **Update the remediation status of one unlocked leak** Set the remediation status of a single unlocked leak. Valid statuses: `new`, `in_progress`, `fixed`, `accepted_risk`. **Errors** - 401 Authentication required, or invalid/expired API key. - 404 Unlocked leak not found. - 422 Invalid status value. - 429 Rate limit exceeded. **Path parameters:** | Name | Type | Required | Description | |---|---|---|---| | `leak_id` | string | yes | Unlocked leak identifier _(maxLength=64, minLength=1)_ | **Request body** (required, `application/json`): | Field | Type | Required | Description | |---|---|---|---| | `status` | string | yes | Target status: new, in_progress, fixed, accepted_risk | **Responses:** - `200` : Status updated. - `401` : Authentication required, or invalid/expired API key. - `404` : Unlocked leak not found. - `422` : Invalid status value. - `429` : Rate limit exceeded.