# Search & Filters

The filters body used by search endpoints, its operators, which operators each field accepts, the errors a filter returns, and query-parameter filters on list endpoints.

Source: https://docs.deepinfo.com/getting-started/search-and-filters/

Last updated: 2026-09-27

---
Search endpoints (`POST …/search`) and bulk actions (`POST …/search:<action>`) take the same JSON body,
except for a few searches with a format of their own (see
[Searches With Another Filter Format](#searches-with-another-filter-format) below):

```json
{
  "filters": {
    "must":     [ { "name": "asset_type", "type": "eq", "value": "domain" } ],
    "should":   [ ],
    "must_not": [ { "name": "tags", "type": "in", "value": ["staging"] } ]
  },
  "sort": [ { "field": "added_date", "order": "desc" } ]
}
```

| Part | Meaning |
|---|---|
| `must` | Every filter must match (AND) |
| `should` | At least one filter must match (OR) |
| `must_not` | No filter may match (NOT) |
| `sort` | List of `{field, order}`; `order` is `asc` or `desc` |

A filter is `{ "name": <field>, "type": <operator>, "value": <value> }`. Each search endpoint lists the
fields it accepts on its reference page, under **Filtering**: the searchable fields, grouped by the
operators they accept, and the fields `sort` accepts where the endpoint has them. Unknown fields return
**400**.

## Operators

| Operator (type) | Value | Matches when the field… |
|---|---|---|
| `eq` | single value | equals the value |
| `in` | array | equals any of the values |
| `startswith` / `endswith` | string | starts / ends with the value |
| `wildcard` | string with `*` / `?` | matches the pattern |
| `fuzzy` | string | approximately matches the value |
| `contains_any` / `contains_all` | array | (array field) contains any / all of the values |
| `gte` / `lte` | number or date | is ≥ / ≤ the value |
| `gt` / `lt` | number, date or CVE ID | is > / < the value; only in Vulnerability Search of the CVE database (see below) |
| `exists` | `true` / `false` | has / has no value |

Not every field accepts every operator: see the next section.

## Which Operators a Field Accepts

Operator support is set per field. Neither the field's type nor its name tells you which operators it
accepts:

- Text fields of the same endpoint: in [Vulnerability Search](/reference/vulnerability/search/),
  the search of the CVE database, `id` accepts `eq`, `startswith`, `wildcard`, `gt`, `lt` and `exists`,
  while `descriptions.value` accepts `wildcard`, `contains_any`, `contains_all` and `exists`.
- The same field on different endpoints: `asset` accepts `wildcard` and `fuzzy` in
  [Asset Search](/reference/easm/asset-search/), and neither of them in
  [Deleted Asset Search](/reference/easm/deleted-asset-search/).
- True/false fields: in Asset Search they accept `eq` and `exists`; in
  [Domain Search](/reference/discovery/domain-search/) they accept `in` as well.
- Identifiers: `id` accepts only `eq` and `in` in [Issue Search](/reference/easm/issue-search/).
- `gt` and `lt`: among the searches with `must`, `should` and `must_not` lists, only Vulnerability Search
  of the CVE database accepts them, on each of its date and number fields and on `id`. Every other one
  returns **400** for them (see [An Operator the Search Does Not Know](#an-operator-the-search-does-not-know)),
  so use `gte` and `lte` there. `gt` and `lt` do work elsewhere in the API: the
  [searches with another filter format](#searches-with-another-filter-format) take them as keys of a range
  filter, and [Email Breach List](/reference/cti/email-breach-list/), a
  [list endpoint](#list-endpoints), as the `__gt` and `__lt` suffixes.

So check the field on the endpoint you call. Each search's **Filtering** section groups its searchable
fields by the operators they accept. The groups come from measurement: each operator was sent to each
field against the API, and the API's answer decided the field's group. They are not derived from the
field's type. A few fields could not be measured: they take a value from a fixed list, the API's error
message does not name the values they accept, and the demo account used for the measurement had no
records that use them. They are listed under **Operators: not measured**: `website.parent_asset.type` in Asset Search,
and `source_format` and `network` in Compromised Payment Credential Search. Any operator outside a
field's group returns **400** (see [Filter Errors](#filter-errors)).

| Search | Request |
|---|---|
| [Domain Search](/reference/discovery/domain-search/#ref-filtering) | `POST /discovery/domain-search` |
| [Vulnerability Search (CVE database)](/reference/vulnerability/search/#ref-filtering) | `POST /discovery/vulnerability-search` |
| [Asset Search](/reference/easm/asset-search/#ref-filtering) | `POST /easm/assets/search` |
| [Deleted Asset Search](/reference/easm/deleted-asset-search/#ref-filtering) | `POST /easm/deleted-assets/search` |
| [Discovered Asset Search](/reference/easm/discovered-asset-search/#ref-filtering) | `POST /easm/discovery/assets/search` |
| [Issue Search](/reference/easm/issue-search/#ref-filtering) | `POST /easm/issues/search` |
| [Vulnerability Search](/reference/easm/vulnerability-search/#ref-filtering) | `POST /easm/vulnerabilities/search` |
| [Vulnerability Asset Search](/reference/easm/vulnerability-asset-search/#ref-filtering) | `POST /easm/vulnerabilities/asset-search` |
| [Compromised Employee Account Search](/reference/cti/compromised-employee-account-search/#ref-filtering) | `POST /cti/compromised-employee-accounts/search` |
| [Compromised Employee Credential Search](/reference/cti/compromised-employee-credential-search/#ref-filtering) | `POST /cti/compromised-employee-credentials/search` |
| [Compromised Client Credential Search](/reference/cti/compromised-client-credential-search/#ref-filtering) | `POST /cti/compromised-client-credentials/search` |
| [Compromised Payment Credential Search](/reference/cti/compromised-payment-credential-search/#ref-filtering) | `POST /cti/compromised-payment-credentials/search` |
| [Compromised Device Search](/reference/cti/compromised-device-search/#ref-filtering) | `POST /cti/compromised-devices` |
| [Threat Actor Search](/reference/cti/threat-actor-search/#ref-filtering) | `POST /cti/threat-actors/search` |
| [Security News Search](/reference/cti/security-news-search/#ref-filtering) | `POST /cti/news/search` |
| [Fraudulent Domain Search](/reference/brp/fraudulent-domain-search/#ref-filtering) | `POST /brp/fraudulent-domains/search` |
| [Suspicious Domain Search](/reference/brp/suspicious-domain-search/#ref-filtering) | `POST /brp/suspicious-domains/search` |

Exports (`…/search:export`) and bulk actions (`…/search:<action>`) have a **Filtering** section of their
own on their reference pages.

Each of these searches, and each export and bulk action beside it, also shows a **Request Template**
among its examples: a request body that lists the endpoint's filters, each with an operator the field
accepts and a placeholder value, ready to copy and fill in. A template is a request only; it has no
response.

## Filter Errors

A filter with an operator the field does not accept returns **400 Bad Request**. Today the message comes
back in one of three forms, depending on the endpoint. An operator the search does not know at all, such
as `gt` outside Vulnerability Search of the CVE database, returns a fourth:

| Form | `code` | Message in | Message |
|---|---|---|---|
| Invalid query | `10400` | `parameters[].details` | `Invalid query: <field> does not support <operator> query` |
| Supported queries listed | `10400` | `parameters[].details` | `Field '<field>' does not support '<operator>' query. Supported queries are [...]` |
| Not supported yet | `30004` | `details` | `<operator> query type is not supported yet.` |
| Unknown operator | `10400` | `parameters[].details`, with `param` `type` | `Select a valid choice.` |

To catch them all in code, read the messages in both `parameters[].details` and `details`: the first
three forms say `does not support` or `is not supported`, and the unknown operator comes with `param`
set to `type`.

### Invalid Query

[Asset Search](/reference/easm/asset-search/) with `startswith` on `open_port_count`, a number field:

**400 Bad Request**

```json
{
  "code": 10400,
  "parameters": [
    {
      "param": "must",
      "path": "filters",
      "details": [
        "Invalid query: open_port_count does not support startswith query"
      ]
    }
  ]
}
```

### Supported Queries Listed

[Vulnerability Search](/reference/vulnerability/search/) of the CVE database with `gte` on
`id`, a text field that accepts `gt` and `lt` but not `gte`:

**400 Bad Request**

```json
{
  "code": 10400,
  "parameters": [
    {
      "param": "must",
      "path": "filters",
      "details": [
        "Field 'id' does not support 'gte' query. Supported queries are ['eq', 'exists', 'wildcard', 'startswith', 'lt', 'gt']."
      ]
    }
  ]
}
```

This form lists the operators the field does accept, which is the quickest way to correct the filter.

### Not Supported Yet

[Deleted Asset Search](/reference/easm/deleted-asset-search/) with `wildcard` on `asset`:

**400 Bad Request**

```json
{
  "code": 30004,
  "details": [
    "wildcard query type is not supported yet."
  ]
}
```

This form names the operator but not the field. Code `30004` also stands for a state change that is not
allowed (see [Errors](/getting-started/errors/)), so read the message as well as the code.

### An Operator the Search Does Not Know

[Asset Search](/reference/easm/asset-search/) with `gt` on `open_port_count`. Among the searches with
`must`, `should` and `must_not` lists, only Vulnerability Search of the CVE database knows `gt` and `lt`,
so here the filter's `type` itself is rejected:

**400 Bad Request**

```json
{
  "code": 10400,
  "parameters": [
    {
      "param": "type",
      "path": "filters.must",
      "details": [
        "Select a valid choice."
      ]
    }
  ],
  "solution": "https://docs.deepinfo.com/reference/"
}
```

This form names neither the field nor the operator: `param` is `type`, and `path` names the list the
filter is in. Use `gte` or `lte` instead.

### A Value of the Wrong Type

A value of the wrong type for the field also returns **400** with code `10400` and the message in
`parameters[].details`, but it reads differently. Here the operator is accepted and the value is not:

**400 Bad Request**

```json
{
  "code": 10400,
  "parameters": [
    {
      "param": "must",
      "path": "filters",
      "details": [
        "Invalid value true: $eq must be boolean."
      ]
    }
  ]
}
```

The message names the value you sent, the operator and the type the field needs: `boolean` here,
`numeric` for a number field. Keep the operator and send the value as that type.

## Always Send a Filter

An empty body `{}` matches everything. For **bulk actions** (delete, tag, state changes) that means **every
record**.

> [!WARNING]
> Always send a filter with a bulk action. An empty body `{}` applies the action to every record.

## A Complete Request

```bash
curl -X POST "https://api.deepinfo.com/v1/easm/assets/search" \
  -H "apikey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "must": [ { "name": "asset_type", "type": "eq", "value": "domain" } ]
    },
    "sort": [ { "field": "added_date", "order": "desc" } ]
  }'
```

See [Asset Search](/reference/easm/asset-search/) for the fields this endpoint accepts.

## Results and Exports

Search results are paginated: see [Pagination](/getting-started/pagination/). The matching
`…/search:export` endpoints return the matches as CSV or a JSON array (`format` query parameter), for
example [Asset Export](/reference/easm/asset-export/). Large exports can time out, so narrow them
with filters too.

## Searches With Another Filter Format

A few searches take `filters` as an object with one key per field instead of `must`, `should` and
`must_not` lists. Each field takes its operators as keys, the fields combine with AND, and `sort` is one
`{field, order}` object instead of a list. For example, [Technology Search](/reference/easm/technology-search/):

```json
{
  "filters": {
    "technology": { "equals": "nginx" }
  },
  "sort": { "field": "technology", "order": "desc" }
}
```

A number or date field takes a range filter, with `gt`, `gte`, `lt` and `lte` as its keys. Technologies
found on more than five of your assets:

```json
{
  "filters": {
    "affected_asset_count": { "gt": 5 }
  }
}
```

These searches use this format. The **Filtering** section of each one lists its fields and the operators
each field takes:

| Search | Request |
|---|---|
| [Technology Search](/reference/easm/technology-search/#ref-filtering) | `POST /easm/technologies/search` |
| [Technology Asset Search](/reference/easm/technology-asset-search/#ref-filtering) | `POST /easm/technologies/asset-search` |
| [Asset Discovery Custom Discovery Rule Search](/reference/easm/asset-discovery-custom-discovery-rule-search/#ref-filtering) | `POST /easm/discovery/custom-rules/search` |
| [Fraudulent Rule Search](/reference/brp/fraudulent-rule-search/#ref-filtering) | `POST /brp/fraudulent-rules/search` |
| [Report Search](/reference/platform/report-search/#ref-filtering) | `POST /platform/reports/search` |

The exports of the technology searches take the same format. [Report Search](/reference/platform/report-search/)
also requires `filters.type`, for example:

```json
{
  "filters": {
    "type": "easm_executive_summary"
  }
}
```

## List Endpoints

**List** endpoints (`GET`) filter with query parameters instead, for example:

```text
?email__contains=john&vip=true&ordering=-last_breach_date
```

The suffix sets the operator (`__contains`, `__not_contains`, `__icontains`, `__startswith`, `__in`,
`__nin`, `__gte`, `__lte`, `__gt`, `__lt`), and `ordering` sorts (prefix `-` for descending). Each list
endpoint's reference page shows the query parameters it accepts. Not every list endpoint takes every
suffix: [Email Breach List](/reference/cti/email-breach-list/) takes `__gt` and `__lt` on its dates and
counts, while [Breached Account List](/reference/cti/breached-account-list/),
[Notification Email List](/reference/platform/notification-email-list/),
[Notification Rule List](/reference/platform/notification-rule-list/) and
[Scheduled Report Rule List](/reference/platform/scheduled-report-rule-list/) take `__gte` and `__lte`. The
other list endpoints have no range suffix.
