acme.com.
In the dashboard, this is Filtered Credentials, both as a Search by Source tile and as a Unified Search tab.
Availability: every plan with a license in force. No plan feature is checked.
Cost: no metered allowance. With an API key, each call counts once toward your monthly API Calls allowance, polls and result pages included, and only 2xx responses count. See Rate limits.
When your organization’s license is not active, every endpoint on this page returns 403 with {"detail": "Your license is not active."}. These endpoints never return the plan-feature 403, an allowance 429 or a capacity 409. See Plans and quotas.
Want the machine, not the account? Filtered credentials returns a deduplicated account list. Stealer logs searches the same corpus by infected device, and returns the whole capture: session cookies, screenshots and everything else that was on the machine.
Which domains you can search
You typically point a search at your organization’s own domain, but ownership is not checked. Any domain can be searched, subject to the two checks below. Pass it bare, asacme.com or mail.acme.com, without a scheme or path. The two checks refuse very large domains:
-
The blocklist. About 1,000 free-mail and consumer domains cannot be searched. The check runs when you create the search. The task comes back already
failed, with thiserror_message: -
The 5,000,000-record ceiling. The first search for a domain counts its leaked records in the background. The task starts as
pending. If the domain has more than 5,000,000 leaked records, the task moves tofailedwith thiserror_message:
Run a search
A filtered search is asynchronous: pick a filter, create the search, poll it until it ends, then read the accounts.1
Pick a filter
leak_filter_filters returns the filter catalog. Each filter has an id, name, description and parameters_schema. The filter_id you pick is the arrangement: it decides which slice of the domain’s accounts comes back, and whether you send filter_parameters.filter_id is required on every call that takes it. Unique Credentials (5) returns the full deduplicated set, and it is the arrangement the dashboard opens with. The IDs below are the ones the platform defines. Confirm them against leak_filter_filters.The Include and Exclude filters take comma-separated terms and match a case-insensitive substring, so
alice also matches alice.smith.2
Create the search
leak_filter_search_create takes a JSON body with domain_url, filter_id and mode, plus filter_parameters when your filter needs them.filter_id 1). The key is plural, usernames:mode decides what happens when only part of the data could be processed:The call returns
201 with the task: its id, status, error_message, domain_url, mode, filter_name and timestamps. Keep the id. A blocklisted domain still returns 201, with status already failed. Read status before you start polling.Invalid input returns 400 instead, and no task is created: a malformed domain_url, a filter_id that does not exist, or filter_parameters that do not match the filter’s parameters_schema.3
Poll until the task ends
Pass the task
id as filter_task_id to leak_filter_search_retrieve. Poll about every 30 seconds.Stop on
ready, incomplete_ready or failed. A failed task stays failed. Fix the cause, then create a new search.The first search for a domain does the heavy work. It walks every leak exposing that domain since 23 September 2020, deduplicates the accounts and scores each one. Depending on how much is exposed, it can take several minutes. The accounts are then stored per domain. Every later search for the same domain processes only the leaks found since the last run, so it finishes much faster.
4
Read the accounts
Once the task is The response is a page of accounts:
ready or incomplete_ready, read its accounts from leak_filter_search_results with the same filter_task_id:count, next, previous and results. Read it too early and you get 404 with {"error_message": "Filter task is not ready. Current status: in_progress"}. Read a failed task and you get 400 with {"error_message": "Cannot process filter task. Processing has failed."}.Read a domain without a task
A domain’s accounts stay stored after its first search.leak_filter_domain_search_results reads them straight from the domain, with no task to track. It takes domain_url and filter_id, and returns the current accounts. The dashboard’s Filtered Credentials results use this call.
filter_parameters is a JSON string in the query, so URL-encode it:
This
403 carries error_message, not detail. It is about the domain, not your license or plan.
Page through results
Both result calls page the same way:page × page_size cannot exceed 50,000. Past that, the call returns 400 with Pagination depth limit exceeded. page * page_size must be <= 50000. Use a narrower filter or the export endpoint. Narrow the filter or export the results. A page past the last one returns an empty results list.
Step through pages by increasing page until you have read count accounts. Do not follow the next and previous links. They do not point at client-api.leak.center.
Filter and sort results
Both result calls and both export calls take the same optional filters, as query parameters:
On a result call, a date in any other format returns
400 with Invalid date format, expected YYYY-MM-DD.
Sort with order and direction (asc or desc). The default is created_at, descending. The dashboard sorts by novelty_score, descending. On a result call, any other order or direction returns 400.
On an export call, these values are not checked up front. The call returns
202, and a bad date or an unknown password policy ID makes the job end failed, with the reason in error_message. An unknown order or direction falls back to created_at, desc.
What comes back
Each account row carries the credential, where it was used, when it surfaced, and how it scores:- Identifiers.
idis the account row.record_idis the leaked record it came from.created_atis when that record was indexed. - The credential.
server,usernameandpassword.serverreads-when the leak named no server, andhas_urlis thenfalse. - Classification.
is_infrastructure,is_business,is_privateandhas_url. - Scoring.
novelty_score,risk_score,reuse_count_till_creation_timeandfirst_discovery. - Provenance.
leaks(each withleak_idandleak_name),leak_publish_date,leak_discover_dateandtags. - Policy match.
matches_password_policyisnullunless you sendpwd_policy_ids.
server + username + password. The same login on a different server stays a separate row, because it is a different exposure.
How novelty and risk are scored
novelty_score and risk_score are computed, not copied from the leak. Both run from 0 to 10.
Novelty: how fresh a credential is. It is the average of two sub-scores.
Recency, from when the credential was first seen:
Reuse, from how many earlier records already held the same username and password:
When
first_discovery is unknown, both sub-scores count as 5. A new, never-seen credential scores 10. An old, heavily reused one scores 2.
Risk: how urgently it deserves attention. A weighted average of four inputs, each scaled to 0–1, then multiplied by 10:
is_private is recorded on the row, but it does not change the score.
Both scores round halves to the nearest even number. With the default weights, both are set when the record is analyzed. Once you change any weight, both are computed each time you read, from the age of the credential at that moment.
Tune the weights
Six weights shape the scores for your organization, each from0 to 5:
first_discovery_weight and reuse_weight cannot both be 0, and at least one of the four risk weights must be above 0. Otherwise the call returns 400.
Read them with leak_filter_risk_settings_retrieve. Change them with leak_filter_risk_settings_update, sending all six:
Match against your password policy
A leaked password that still satisfies your current password policy is more likely to still be in use. Filtered credentials can flag those rows. Define a policy once, then apply it to any read. Define it. A password policy belongs to your organization. Create one withleak_filter_password_policies_create:
min_length (default 8), require_numbers, require_special_chars and require_uppercase (all default false). A password matches when it reaches min_length and meets each rule you turned on: at least one digit, at least one A–Z, and at least one of !@#$%^&*(),.?":{}|<>.
Your organization can hold up to 5 policies, each with a unique name. A sixth returns 400 with Maximum number of password policies (5) reached for this organization. List, read, update and delete policies with leak_filter_password_policies_list, leak_filter_password_policy_retrieve, leak_filter_password_policy_update and leak_filter_password_policy_delete.
Apply it. Pass pwd_policy_ids on a results or export call:
matches_password_policy as true or false. It tags rows, it does not filter them. Non-matching accounts still come back, so you keep the full picture and can sort or triage by the flag. Pass several IDs, and a row is flagged when it matches any of them. On a result call, an ID that is not one of your policies returns 400 with Password policy not found. On an export call, the job ends failed with that message in error_message.
Export the results
To pull a full result set as one file instead of paging, create an export. Filtered credentials has its own export endpoints. For exports from other sources, see Downloads and exports.1
Create the export
Every parameter goes in the query string, including on these Or export a searched domain with Both return The same rules as reading apply. A task export needs a
POST calls. file_type is required: json, csv or xlsx. The filters and sort from Filter and sort results apply too.Export a finished task with leak_filter_export_create_from_filter_task. The export uses the task’s filter and filter_parameters:leak_filter_export_create_from_domain, passing domain_url and filter_id, plus filter_parameters as a JSON string when the filter needs them:202 with the export job:ready or incomplete_ready task, so an unfinished task returns 404 and a failed one returns 400. A domain export needs an existing, active domain, so you get the 404 and 403 described in Read a domain without a task. A missing or unknown file_type returns 400. Dates, order, direction and pwd_policy_ids are not checked here. See Filter and sort results.2
Poll until it ends
Poll
leak_filter_export_retrieve with the export id:status moves from pending to running, then ends ready or failed. A ready job fills in result_count (rows) and size (bytes). A failed job carries error_message.3
Download the file
Fetch the file from
leak_filter_export_download. It arrives as an attachment named export-<id>.<file_type>.This
409 means the file is not ready yet. It is not a capacity refusal.leak_filter_export_list lists your organization’s exports from the last 24 hours, newest first. Filter it by file_type, status or filter_ids (comma-separated), and page it with page (from 1) and page_size (default 50, up to 100).
Manage search tasks
leak_filter_search_list lists your organization’s search tasks, newest first. Filter it by status, or by domain_url (a case-insensitive substring). Page it with page (from 1) and page_size (default 10, up to 100).
leak_filter_search_delete deletes tasks by ID. Pass them comma-separated in filter_task_ids:
leak_filter_domain_search_results still reads them.
Get notified about new accounts
A filtered search runs when you call it. To be notified when new accounts appear on a domain, create a monitor on the Leak Filter source (service 6). It takes the same domain_url and filter_id. A blocklisted domain, or one already counted over the ceiling, is refused there too.
Errors
A blocklisted or over-ceiling domain is not an HTTP error on create. It shows up as a
failed task. Errors lists every error the API returns.