Documentation Index
Fetch the complete documentation index at: https://docs.oathnet.org/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Structured filters are the canonical search grammar behind:GET /service/v2/breach/searchGET /service/v2/stealer/searchGET /service/v2/victims/searchPOST /service/v2/ai/filterresponsesquery_configon exports, bulk search, and scanners
filter, filter_id, operator behavior, and filter-related 400 errors.
Start Here
Use the simplest option that fits your case:- If you only need simple exact filters like email, domain, country, or db name, use normal query params.
- If you need
or,in,not_in,contains,starts_with,ends_with,wildcard, range filters, orexists, use a structuredfilter. - If you want to type plain English like “US gmail users older than 18”, use the AI filter endpoint and then search with
filter_id. - If you are saving a search for scanners, bulk search, or exports, use
query_config.
- keep simple exact rules as normal flat params
- switch to a structured
filterwhen flat params are no longer enough - keep
filter_idonly when there is no real filter to send yet
1. Simple Search: Use Normal Query Params
This is the easiest option. Use it when every condition is a simple exact match.- breach:
email[],email_domain[],country[],city[],dbname[],phone[] - stealer:
domain[],subdomain[],username[],password[],email[],log_id - victims:
username[],email[],ip[],hwid[],discord_id[],total_docs_min
2. Complex Search: Use filter
Use a structured filter when normal query params are not enough.
Good examples:
country is US OR CAemail ends with @gmail.comage is at least 18instagram exists(country is US AND dbname is linkedin.com) OR password contains hunter
filter in two ways.
Option A: GET with filter in the query string
Pass filter as a JSON-encoded string:
Option B: POST to the same search route with a JSON body
This is the pattern the frontend uses for complex filters. The response shape is the same asGET /search.
filterfilter_id
page_size, cursor, sort, from, to, and date_field can stay in the query string.
3. Natural Language: Use AI Filter Then Search
Step 1: Ask the AI filter endpoint to build the filter
Step 2: Search with the returned filter_id
Step 3: Optional: edit the filter and send your own filter
If you want to change the AI result before searching, send a real filter instead of relying only on the stored context.
4. Saved Jobs: Use query_config
query_config is the JSON version of a search. It is used by:
- scanners
- bulk search
- exports
Simple query_config
For JSON
query_config objects, prefer canonical keys such as domain, email_domain, and dbname. Bracketed variants such as domain[] are still accepted for parity with query-string filters.Complex query_config
When the logic is too complex for simple flat keys, put a real structured filter inside query_config.filter.
Filter Grammar
Every filter is built from just three shapes:- a leaf rule with
field - an
andgroup - an
orgroup
Leaf node
AND group
OR group
Nested example
Merge Order
Requests support this merge order:filter_idloads a stored transient filter context when it still exists.- An explicit
filterreplaces that stored filter. - Flat filters such as
email[],domain[],dbname[], orlog_idare merged on top. - Pagination and sort settings such as
cursor,page_size,fields[],from,to, andsortstill apply normally.
- start from a saved or AI-generated
filter_id - send your own
filterif you want to take control - still add normal query params if needed
Operator Guide In Plain Language
| Operator | Value type | Meaning | Notes |
|---|---|---|---|
eq | string | Exact or field-aware equality | If the value contains * or ?, it behaves like a wildcard pattern instead of a literal exact match |
neq | string | Not equal | Inverts the normal equality translation |
in | string array | Match any listed value | Array required |
not_in | string array | Exclude any listed value | Array required |
contains | string | Substring match | Rejected on date and IP fields |
starts_with | string | Prefix match | Rejected on date and IP fields |
ends_with | string | Suffix match | Rejected on date and IP fields |
wildcard | string | Wildcard pattern using * and ? | Rejected on date and IP fields |
gt | string | Greater than | Use stringified numeric/date values |
gte | string | Greater than or equal | Use stringified numeric/date values |
lt | string | Less than | Use stringified numeric/date values |
lte | string | Less than or equal | Use stringified numeric/date values |
exists | boolean | Field exists or is missing | true means present, false means missing, and omitted value defaults to true |
Quick examples
Common Cases
Pattern searches
contains,ends_with, andwildcardrequire at least2non-wildcard characters- patterns like
"*","*a*", or"?"are rejected with400 starts_withuses prefix semantics
Exists / missing checks
Numeric and date ranges
Use string values that match the public field format:- numeric-style fields such as
ageshould use numeric strings like"18" - date-style fields such as
date_birthshould use ISO-style dates like"1990-01-01" - datetime fields such as
indexed_atshould use RFC3339 timestamps like"2026-04-18T00:00:00Z"
in and not_in
Use arrays when you already know multiple values:
Domain-only email filters
When you already know the mail domain, prefer the dedicated public field:Step-By-Step Examples
Example: exact filters only
Example: one structured rule
Example: AND two rules together
Example: OR two rules together
Example: nested logic
Example: send the filter with POST
Example: filter_id plus your own exact params
This is valid:
Example: filter_id plus a replacement filter
This is also valid:
Choosing Fields
Field availability is endpoint-specific. A field accepted by breach search is not automatically valid for stealer or victims.- breach search commonly uses fields such as
email,email_domain,username,country,city,dbname,date_birth, andage - stealer search commonly uses fields such as
domain,subdomain,path,username,password,email,ip,hwid,discord_id, andlog_id - victims search commonly uses fields such as
username,email,ip,hwid,discord_id,log_id, andtotal_docs
Scanners, Exports, And Bulk Search
query_config reuses the same filter system, with a few extra rules:
filtercan be a JSON object or a JSON string- flat filters can be sent with canonical JSON keys such as
domain,email_domain, ordbname - bracketed keys such as
domain[]are also accepted - scanners reject runtime-only keys such as
from,to,cursor,page_size,format,debug, andsearch_id - scanners always monitor on
indexed_at - exports and bulk search allow
from,to,date_field, andsortbecause those jobs serialize full searches
- start flat when the saved search is simple
- switch to
query_config.filterwhen the saved search becomes complex
Limits
filter_idmust be a 24-character hex string- maximum nesting depth is
2 - maximum leaf conditions is
50 valueis required for every operator exceptexistsinandnot_inrequire arrays
Common 400 Errors
Typical structured-filter validation failures include:- malformed JSON in
filter - a node that mixes
fieldwithandoror - invalid operator names
- disallowed fields for the selected endpoint
contains,ends_with, orwildcardpatterns that are too short- using pattern operators on date or IP fields
- nesting deeper than two levels
- more than fifty leaf rules
What The Frontend Does
The frontend uses three practical rules that are helpful for API clients too:- If a filter can be represented as normal exact-match params, keep it simple and send flat params.
- If the filter needs nested logic or advanced operators, send a real structured
filter. - If both
filter_idand a real filter are available for a saved config, prefer the real filter as the canonical version.
Related Endpoints
V2 Breach Search
Search breach data with structured filters
V2 Stealer Search
Search stealer records with flat or structured filters
Search Victims
Search victim summaries using the same filter grammar
Create AI Filter
Generate a reusable filter from natural language