> ## Documentation Index
> Fetch the complete documentation index at: https://docs.depict.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Search

> How to call the Platform API search endpoints: the /v3/search/results request and response, pagination, filter and sort semantics, and /v3/search/suggestions.

<Warning>
  The Platform API currently has two active versions. Build new integrations
  against the `/v3` endpoints — `/v3` is the successor to `/v2`. `/v2` is
  expected to be deprecated within roughly 6 months and `/v3` within roughly 9
  months, with a v4 API expected in Q4. These are expectations, not committed
  dates — confirm timelines with your Depict contact before planning a
  migration deadline.
</Warning>

Search on the Platform API consists of these endpoints:

| Endpoint | Method | Purpose |
| - | - | - |
| `/v3/search/results` | POST | Search results for a query, with filters, sorting and pagination |
| `/v3/search/suggestions` | GET | Query and listing suggestions for autocomplete |
| `/v3/search/related` | POST | Related-products recommendations for a search — returns a recommendations-shaped response, see the note below |

<Note>
  `/v3/search/related` returns the same response shape as
  [`/v3/recommend/products`](/api-guide/recommendations/api), not a search
  response. Its displays carry `recommendation_id` (not `search_result_id`) for
  [tracking](/api-guide/tracking/product-cards).
</Note>

## Limits and behavior

* Every response is a single JSON body. No search endpoint streams; there is no SSE or chunked delivery.
* Two pagination styles exist — cursor-based and page-based. They are mutually exclusive: mixing fields from both in one request returns 400.
* Cursor-based `limit`: 1–250. Page-based `hits_per_page`: 1–200 and required whenever `page` is sent.
* `merchant`, `market` and `locale` are required on every request. Enumerate valid values via [`/v3/markets` and `/v3/locales`](/api-guide/introduction#markets-locales-and-currency).
* Unknown request fields are silently ignored — a misspelled field name is dropped, not rejected.
* Available filters and sorts are per-merchant configuration. Do not hardcode them: read them off the `filters` and `sorts` arrays in each response.
* `GET /v2/search/content` (dedicated content search) has no v3 equivalent. Content results are available only as `content_search_links` inside `/v3/search/results` responses.
* Suggestions responses carry a one-hour cache header; `limit` for suggestions is 1–10, default 3.
* Response fields with null values are omitted from the JSON (`exclude_none` serialization).

## Search results

`POST /v3/search/results` with a JSON body:

| Field | Type | Required | Description |
| - | - | - | - |
| `merchant` | string | Yes | Your merchant identifier, provided by Depict |
| `market` | string | Yes | Market identifier |
| `locale` | string | Yes | Locale identifier |
| `query` | string | No | The search query |
| `filters` | array | No | Filters to apply — see [Filters](#filters) |
| `sort` | object | No | Sorting method — see [Sorting](#sorting) |
| `session_id` | string | No | Session identifier; required for the request to be tracked and attributable |
| `metadata` | object | No | String-to-string map that can modify search behavior; only use values agreed with Depict |
| `cursor` | string | No | Cursor-based pagination — see [Pagination](#pagination) |
| `limit` | integer | No | Cursor-based pagination: max results per response, 1–250 |
| `page` | integer | No | Page-based pagination: 1-based page index |
| `hits_per_page` | integer | No | Page-based pagination: results per page, 1–200; required when `page` is sent |

Example request:

```json theme={null}
{
  "merchant": "MERCHANT_ID",
  "market": "MARKET",
  "locale": "en",
  "query": "jeans",
  "limit": 40,
  "session_id": "SESSION_ID"
}
```

### Response

| Field | Type | Description |
| - | - | - |
| `displays` | array of objects | The results, one object per product card. The contents are merchant-specific product data; each display also carries a `search_result_id` used for [tracking](/api-guide/tracking/product-cards) |
| `n_hits` | integer | Total number of results for the query. Not necessarily exact |
| `sorts` | array | Available sorting methods. Any element can be sent as `sort` in a subsequent request |
| `filters` | array | Available filters that can be sent back in a subsequent request |
| `search_request_id` | string | Identifier for this response, used in tracking |
| `cursor` | string | Cursor for the next page. Absent when there are no more results |
| `page` | integer | Echoed page index when page-based pagination was used; absent otherwise |
| `content_search_links` | array | Content pages matching the query — see [Content results](#content-results) |

## Pagination

| Style | Request fields | How to page |
| - | - | - |
| Cursor-based | `cursor`, `limit` (1–250) | Omit `cursor` for the first request. Send the response's `cursor` in the next request until the response has no `cursor` |
| Page-based | `page` (≥1), `hits_per_page` (1–200) | Request each page by index. `hits_per_page` is required whenever `page` is sent |

Rules for how the two styles interact:

* The styles are mutually exclusive. A request that mixes fields from both returns 400 with `{"error": "Cannot mix cursor-based and page-based pagination."}`.
* `page` without `hits_per_page` returns 400.
* An undecodable `cursor` returns 400 with `{"error": "Invalid pagination cursor."}`.
* The response mirrors the style used: page-based requests always echo `page` (defaulting to 1) and omit `cursor`; cursor-based requests omit `page`.

## Filters

Each response's `filters` array describes the available filters. To apply a
filter, send its `field`, `op` and selected `data` in the next request.
The schema requires `field` and `op`. It permits omitted or null `data`, but
that does not select a value. An empty selection does not constrain results.

A filter object:

| Field | Type | Description |
| - | - | - |
| `field` | string | The product attribute the filter applies to |
| `op` | string | One of `eq`, `neq`, `in`, `nin`, `leq`, `geq`, `inrange` |
| `data` | varies | The selected state — format depends on the filter type, see below |
| `meta` | object | UI metadata (type, title, selectable values, counts, display names). Returned by the API; not required when sending a filter back |
| `id` | string | Groups filters that belong together. Optional |

The filter type is in `meta.type`:

| `meta.type` | Selection behavior | `data` format |
| - | - | - |
| `range` | Numeric range, e.g. price | `[min, max]` |
| `radio` | Exactly one value selectable | List with the selected value |
| `checkbox` | Multiple values selectable | List of selected values |
| `checkbox-grid` | Same as `checkbox`; render compactly as a grid (typically sizes) | List of selected values |
| `checkbox-color` | Same as `checkbox`; `meta.swatches` provides color swatches per value | List of selected values |
| `checkbox-hierarchical` | Tree of values, typically categories | List of paths — see below |

### Range filters

`data` is `[min, max]`. `meta` carries the selectable bounds `min` and `max`, plus optional `unit` and `currency` for rendering.

### Value filters

`data` is the list of selected values. `meta.values` lists the selectable values, `meta.counts` the result count per value, and `meta.names` optional display names.

### Hierarchical filters

`data` is a list of paths, where each path is the list of node titles from the root to a selected node. If a parent is selected, all of its children must also be included as selected.

Example: a category tree where the user selected **Shirts** and **Jeans**:

1. Clothes
   1. **Shirts**
      1. **Cotton Shirts**
      2. **Linen Shirts**
   2. Pants
      1. **Jeans**
      2. Shorts

Selecting Shirts (a parent) implies both of its children, so the corresponding `data` is:

```json theme={null}
[
  ["Clothes", "Shirts"],
  ["Clothes", "Shirts", "Cotton Shirts"],
  ["Clothes", "Shirts", "Linen Shirts"],
  ["Clothes", "Pants", "Jeans"]
]
```

Invalid filter, market or locale combinations return 422 with `{"error": "Invalid request parameters. Please check your filters, market, and locale."}`.

## Sorting

Each response's `sorts` array lists the available sorting methods for your catalog; the default is relevance. To sort, send one of them back as `sort`: `{"field": "...", "order": "asc" | "desc"}`. The `meta` object on each sort (title, selectable orders, display names) is for rendering and does not need to be sent back.

## Query suggestions

`GET /v3/search/suggestions` with query-string parameters:

| Parameter | Type | Required | Description |
| - | - | - | - |
| `merchant` | string | Yes | Your merchant identifier |
| `market` | string | Yes | Market identifier |
| `locale` | string | Yes | Locale identifier |
| `query` | string | No | The partial query the user has typed |
| `session_id` | string | No | Session identifier |
| `limit` | integer | No | Maximum number of suggestions, 1–10. Default 3 |

The response is `{"suggestions_request_id": "...", "suggestions": [...]}` where each suggestion is one of two shapes:

| `type` | Fields | Meaning |
| - | - | - |
| `query` | `query`, `suggestions_result_id` | A suggested search query |
| `listing` | `listing_id`, `external_id`, `listing_type`, `title`, `slug`, `image_urls`, `show_in_breadcrumbs`, `show_in_quicklinks`, `ancestors`, `suggestions_result_id` | A suggested [listing](/api-guide/listings). `ancestors` is the ordered breadcrumb path, root first |

<Note>
  On v2 the suggested-query field was named `title` and listing suggestions
  were category suggestions. On v3 the field is `query` and suggestions
  reference listings.
</Note>

Suggestions responses carry a one-hour cache header. If the suggestions index for the requested locale has not been built yet, the endpoint returns 404 with an `{"error": ...}` body.

## Content results

There is no dedicated content-search endpoint on v3 — `GET /v2/search/content` has no successor. Content results arrive as the `content_search_links` array on `/v3/search/results` responses. Each element:

| Field | Type | Description |
| - | - | - |
| `type` | string | Always `content_link` |
| `title` | string | Title of the content page |
| `description` | string | Short description. May be absent |
| `image_url` | string | Image for the content page. May be absent |
| `page_url` | string | URL of the content page |
| `highlights` | array | Snippets showing why the query matched |

## Errors

| Status | Body | Cause |
| - | - | - |
| 400 | `{"error": "..."}` | Invalid cursor, `page` without `hits_per_page`, mixed pagination styles, unknown merchant/market configuration |
| 422 | `{"error": "..."}` | Invalid filters, market or locale |
| 422 | `{"detail": [...]}` | Request body fails schema validation (missing required field, wrong type) |
| 404 | `{"error": "..."}` | Suggestions index or search backend resource not found |
| 500 | `{"error": "..."}` | A handled search backend failure; the message includes an `Error ID` |
| 503 | `{"detail": "..."}` | Search is temporarily unavailable; the response can include `Retry-After` |

Handled unexpected search errors include an `Error ID`. Give that ID to Depict
support. Other failures can have a different body. Check the HTTP status before
you read response fields. Apply a request timeout in your storefront. Keep the
page usable if the request fails. Depict does not specify a storefront timeout
or a rate-limit policy on this page.

## Product card data

`displays` contains merchant-specific product objects. Confirm your display
mapping with Depict before you assign types to these objects.

If your mapping uses `variant_displays` and `variant_index`, select the card
variant with `display.variant_displays[display.variant_index]`. The
[display transformer guide](/reference/listings-search/display-transformers)
shows this structure and the usual card fields:

* `product_id` identifies the selected variant for product page and add-to-cart tracking.
* `title` supplies the card title.
* `image_urls` is an array of image URLs. Some mappings use one `image_url` instead.
* `sale_price` and `original_price` supply the resolved prices.
* `page_url` can come from your display transformer. It is not guaranteed in the raw response.
* If your mapping supplies `in_stock`, use it for the card's availability state.

Preserve `search_result_id` on the outer display for card tracking. Use the
returned prices for the requested market and locale. Keep the API's product
order. If a returned variant is unavailable, disable its add-to-cart control.
Do not infer availability from the image or price. Provide an image placeholder
when no usable image is present.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.