# Query API

The public, read-only API storefronts, browsers and agents ask: searches, product pages, URLs and robots.txt.

Base URL in the compose stack: `http://127.0.0.1:7800`. It takes no key and allows every origin. [Conventions](/docs/reference/conventions) covers what both APIs share: JSON, ids, times and [problems](/docs/reference/problems).

| Endpoint | What it does |
| --- | --- |
| [`GET /health`](#health) | Whether the Query API is up |
| [`POST /v1/search`](#search) | Search, browse a category or complete a query |
| [`GET /v1/search`](#search-by-url) | Search with the request in the URL |
| [`GET /v1/product`](#product) | One product as its page shows it |
| [`GET /v1/resolve`](#resolve) | What page one of the merchant's URLs is |
| [`GET /v1/robots`](#robots) | The robots.txt lines that keep crawlers off filter states |

## Whether the Query API is up

`GET /health`

Answers while the server runs; the container's health check asks it.

### Answers

- `200` (object): The server runs.
  
  - `status` (string · required): `ok` while the server runs.
  
  - `version` (string · required): The version of the binary.

```sh title="Terminal"
curl -s http://127.0.0.1:7800/health
```

```json title="Answer: 200 · 1.4 ms"
{"status":"ok","version":"0.1.0"}
```

## Search, browse a category or complete a query

`POST /v1/search`

Answers one request: sections of results or a redirect, named by the release and the catalog snapshot that answered it. The time is set when the request arrives; a request that brings its own `time` is refused. With `debug`, the response carries the trace: one entry per step.

### Body

One search, category page or autocomplete request.

- `query` (string): What the shopper typed. Empty on a category page they only browse.

- `on` (string): Where the request comes from. Default: `search`.
  
  - `search`: The search page's results.
  
  - `category_page`: A category's listing.
  
  - `autocomplete`: A search box's suggestions while the shopper types.

- `channel` (string): The channel. Default: the release's first channel.

- `locale` (string): The locale. Default: the channel's first locale.

- `category` (string): The category page being browsed.

- `filters` ([Selector](#Selector)): The shopper's choices in the page's facets, by field: value or group codes `{ "color": ["grey", "green"] }`, bucket codes `{ "price": ["100-300"] }`, a range `{ "width": { "gte": 160, "lte": 220 } }`, a toggle `{ "on_sale": true }`, and the category facet's choice `{ "category": { "within": "wohnen/sofas" } }`. Values of one field are alternatives; fields narrow each other.

- `dismissed` (array of objects): What the shopper took back of what the query named: those words stay text, however often the query is sent again. A value, such as `{ "field": "color", "value": "black" }` or `{ "field": "category", "value": "wohnen/sofas" }`, or every detection of a field, such as `{ "field": "width" }` for a width a pattern read.
  
  A detection the shopper removed.
  
  - `field` (string · required): The field the detection filtered, or `category`.
  
  - `value` (boolean, number or string): The value, a value or group code or a category id; without one, every detection of the field.

- `redirect` (boolean): Lets the response send the shopper elsewhere: to the category page a query names completely, or where a rule redirects. `false` answers with the results instead, as the way back from such a page does. Default: `true`.

- `sort` (string): A sort option. Default: the page's default sort.

- `page` (integer · at least 1): The page, counted from 1.

- `per_page` (integer · 1 to 100): Hits per page, up to the limit [`per_page`](/docs/reference/limits#per_page). Default: 24, which fills rows of two, three, four and six tiles.

- `facets` (array of strings): Facets to count in full, besides those the page counts first (the limit [`counted_facets`](/docs/reference/limits#counted_facets) of its layout, and every one with a choice): a facet the response lists as `deferred` or with `more` comes with every value when it is named here, as a shopper opening it needs. Each must be a facet of some page of the release; one the page does not show is left out.

- `count_only` (boolean): Answers with each section's total alone: no hits, and no facets but those `facets` names, as a filter sheet's "Show 128 results" needs.

- `context` (map of strings): Declared context keys and their values, such as `{ "customer_group": "b2b" }`.

- `time` (string): The time the request is answered at. The Query API stamps it on arrival; only search tests and the preview (`previewSearch`) may fix it, which makes scheduled rules testable.

- `debug` (boolean): Adds the trace to the response.

- `page_view` (string): The page the shopper loaded, which the storefront names anew on every page load. The search log counts a query in the reports once two page views typed it on a day.

- `traffic` (string): Whose search this is: tests and benchmarks say so, and the reports leave them out. Default: `shopper`.
  
  - `shopper`: Everyone a storefront serves that none of the below is.
  
  - `test`: End-to-end tests, which mark their requests so.
  
  - `bench`: A latency benchmark, which marks its requests so.
  
  - `bot`: A crawler or another bot, as uap-core's maintained user agent rules name it; the Query API decides this from the `User-Agent` header of a request that says it is a shopper's.

### Answers

- `200` (object): The answer.
  
  - `query_id` (string · required): Names this answer for clicks and purchases.
  
  - `release` (string · required): The configuration that answered.
  
  - `snapshot` (string · required): The catalog that was searched.
  
  - `query` (object · required): The query as typed, and the text that was searched after rules removed words and resolution took what it named.
    
    - `typed` (string · required)
    
    - `searched` (string · required)
  
  - `redirect` (object): Where to send the shopper when they submit the query: the category or brand page it names completely, or where a rule redirects. A client that does not follow it, such as a search box suggesting as the shopper types, shows the sections, which hold what that page shows; a rule's redirect to a fixed URL has none.
    
    - `url` (string · required)
    
    - `entity` (string): The entity whose URL it is, when the redirect named one: the category or brand a query landed on, or a rule's target.
    
    - `rule` (string): The rule that redirects; none when the query lands on the page it names.
  
  - `resolution` (object): What the query named.
    
    The categories, brands and values a query named, and the text that is left.
    
    - `category` (string): The id of a category node, such as `wohnen/sofas`: the merchant's id of a `category` entity.
    
    - `filters` ([Selector](#Selector)): The detected filters, such as `{ "color": "grey", "width": { "lte": 220 } }`.
    
    - `text` (string · required)
    
    - `complete` (boolean · required): Nothing is left as text.
    
    - `left_out` ([array of AppliedFilter](#AppliedFilter)): What a relaxed search left out of what the query named, each as the filter it would have been, with its labels, so a storefront can say what the results leave out. Empty unless the response is `relaxed`.
  
  - `relaxed` (boolean): Nothing matched the resolved search, so it was repeated without what the query named; the resolution says what that was, and `filters` no longer holds it.
  
  - `filters` ([array of AppliedFilter](#AppliedFilter)): Every filter that narrowed the results the page lists, each removable by the shopper.
  
  - `listed` (string · required): The type the page lists: its section pages through the results and carries the facets, and `filters` narrow it. Products, unless the category page, or the category the query named, holds another type with offers, such as the services of a workshop's category.
  
  - `sorts` (array of objects): The orders the page offers, in display order, one of them marked as the one the page starts in. A storefront draws its sort control from them and puts `sort` in a URL only where the shopper chose another order than the start. None where the release lays out no options: the page offers no choice.
    
    An order a page offers.
    
    - `code` (string · required): What a request's `sort` names to choose it; `relevance` is built in.
    
    - `label` (string · required): In the request's locale.
    
    - `default` (boolean): The page starts in this order while the shopper chooses none: the sort its category or the layout's default sets, or relevance where the request has words, which rank by it.
  
  - `sections` (array of objects): The results, one section per entity type, in order.
    
    The results of one entity type.
    
    - `type` (string · required): The code of an entity type, such as `product`, `category` or a merchant's own `store`.
    
    - `total` (integer · required): How many entities match in all, counted exactly however many they are.
    
    - `reachable` (integer · required): How many of them pages reach: for the listed section the total, or the limit [`reachable_hits`](/docs/reference/limits#reachable_hits) when more match; for a section beside it, which does not page, the hits it shows. A storefront links and loads no page past them and says why when the total is larger.
    
    - `upper_bound` (boolean): The total is an upper bound: a free range over a value that differs between a tile's variants narrows it beside another variant-level choice, and one variant may meet the range while another meets the choice. The trace says which range.
    
    - `hits` (array of objects · required): This page's tiles; none when the request asked for the totals alone.
      
      One result tile.
      
      - `entity` (string · required): An entity named with its type, `type:id`, such as `product:SOFA-LUND-3`.
      
      - `variant` (string): The variant the tile shows, when the listing grain is finer than the product or the shopper's choices on variant-level values pick one, such as the grey variant of a sofa under a grey filter.
      
      - `fields` (object · required): The type's result fields, in the request's locale and channel.
      
      - `sponsored` (object): A rule's paid pin placed it here: a storefront labels the tile as an ad and names the campaign in the tile's clicks and views.
        
        The campaign a paid placement belongs to: `{ "campaign": "Michelin spring" }`.
        
        - `campaign` (string · required): The merchant's name for it, which the campaign report counts by.
    
    - `facets` (array of objects): The page's facets in display order, each counted under every filter but its own, so choosing a value never makes its siblings vanish. A facet without values to show is left out.
      
      A facet with its values and counts.
      
      - `field` (string · required): A field a selector, filter or facet reads: an attribute code, or one of the built-in fields `category`, `brand`, `price`, `on_sale`, `availability`, `delivery_days` and `rating`.
      
      - `kind` (string · required): - `list`
        
        - `swatch`
        
        - `hierarchical`
        
        - `range`
        
        - `buckets`
        
        - `toggle`
        
        - `relative`: Dates relative to the request's time, such as "new in the last 30 days". Bucket bounds are days.
      
      - `label` (string · required)
      
      - `group` (object): The group the page draws it in, such as a tyre's width under "Tyre size". The facets of a group follow one another in the group's order, so a storefront without a part for the group draws them one under the other.
        
        The group a facet stands in, as every facet of it says.
        
        - `group` (string · required): The code of a group of facets on a page, such as `tyre_size`.
        
        - `label` (string · required): The heading, in the request's locale.
        
        - `display` (string): How a group of facets is drawn.
          
          - `finder`: One select beside the next, each with "Any" first and every value counted under the choices of the others, such as a tyre's width, ratio and rim. Each of its facets is shown as `select`.
      
      - `values` (array of objects · required): The values that have results, and every chosen value even without, so it can always be unchosen. A list or swatch carries the most frequent ones up to its cap, and `more` says how many it leaves out. A range has none; its `min` and `max` bound it. A `deferred` facet has none yet.
        
        - `value` (boolean, number or string · required): What a request's `filters` name to choose it: a value or group code, a number, `true`, a category id, or a bucket's code written as its bounds, such as `100-300`, `-4` or `4-`.
        
        - `label` (string · required)
        
        - `count` (integer · required)
        
        - `bounds` ([Comparison](#Comparison)): A bucket's bounds, such as `{ "gte": 100, "lt": 300 }`; selecting the bucket filters the field by them.
        
        - `parent` (boolean, number or string): In a hierarchical facet, the value one level up.
        
        - `selected` (boolean)
        
        - `swatch` (string)
        
        - `image` (string): A swatch image or pattern, or a brand's logo where the facet shows images; its URL.
        
        - `description` (string): What the value means, read with it.
      
      - `more` (integer): How many more values with results the facet has than `values` holds: the cap left them out, which is the limit [`facet_values`](/docs/reference/limits#facet_values) or the layout's `limit`. Naming the facet in a request's `facets` returns them all.
      
      - `deferred` (boolean): The page did not count this facet (the limit [`counted_facets`](/docs/reference/limits#counted_facets)), so it has no `values` yet and may have none to show. Naming it in a request's `facets` counts it.
      
      - `upper_bound` (boolean): The counts are upper bounds. That happens only under multi-select across two variant axes inside one tile, and under a free range over a variant-level value that differs inside a tile; the trace says which.
      
      - `single` (boolean): One value at most: a radio group with "Any" first.
      
      - `description` (string): Text under the facet's name, read with its group.
      
      - `headings` (array of objects): Parts of the list under headings of their own, in order; values no heading names follow the last one.
        
        A heading inside a facet and the values it stands above.
        
        - `label` (string · required)
        
        - `values` (array of (boolean, number or string) · required)
      
      - `search` (boolean): A search box belongs above the values.
      
      - `display` (string): How a facet's values look beyond what its kind says.
        
        - `slider`: A range: a two-thumb slider above the two inputs, which stay.
        
        - `histogram`: A range over the price: a slider with the distribution of the prices drawn behind it, in the bins of a fixed ladder of preferred numbers (`histogram` of the response). Prices that fall in a single bin get the plain slider.
        
        - `stars`: A rating: stars beside each bucket; its words carry the meaning.
        
        - `grades`: An option's values on badges in their own swatch colors, such as the energy label's A to G, in the order the values are listed. Every listed value needs a swatch.
        
        - `buttons`: Values or buckets as a wrap of toggle buttons, such as sizes.
        
        - `select`: One value at most, chosen from one native select with "Any" first, for a long ordered list such as a tyre width.
        
        - `images`: Each value's image beside its name: a brand's logo, or the image an option's value carries, such as an icon.
      
      - `custom_display` (string): The shop's own storefront display, as the release names it; a storefront without it draws `display`.
      
      - `min` (number): A range: the lowest value among the results without the range's own choice, such as the cheapest price.
      
      - `max` (number): A range: the highest value among the results without the range's own choice.
      
      - `histogram` (array of objects): A histogram: the bins in ascending order from the one the cheapest tile falls in to the one the dearest does, empty ones included so the bars keep their places, each with the tiles counted in it under every filter but the facet's own. The bins are a fixed ladder of preferred numbers, 24 to the decade and each 8 to 15 percent above the one before, the same under every filter, so a bar says where products sit, not exactly how many: a tile with several prices (one per variant) counts in every bin one of them falls in, and the chosen range may cut a bin in two. The range's `min` and `max` still bound the slider. None where the prices fall in a single bin, and while the facet is `deferred`; naming it in a request's `facets` counts it, with the same bins.
        
        One bar of a histogram: the tiles with a price from `gte` up to below `lt`.
        
        - `gte` (number · required)
        
        - `lt` (number · required)
        
        - `count` (integer · required)
      
      - `unit` (string): The unit of a quantity's values and bounds, such as `cm`.
        
        - `mm`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `cm`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `m`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `in`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `ft`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `g`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `kg`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `lb`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `oz`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `ml`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `l`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `w`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `kw`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `wh`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `kwh`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `mah`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `v`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `lm`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `kelvin`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `celsius`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `hz`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `mb`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `gb`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `tb`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `day`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `month`: The unit of a quantity's values and bounds, such as `cm`.
        
        - `year`: The unit of a quantity's values and bounds, such as `cm`.
      
      - `currency` (string): The currency of a price's values and bounds, the channel's.
  
  - `placements` (array of objects): The banners and teasers rules place on this page, in the order it draws them: the top ones, those among the tiles by position, the bottom ones. None on the autocomplete surface and for a request for totals alone.
    
    A banner a rule placed on this page, in the request's locale. It is not a hit: the sections' totals, facets and hits are the same without it.
    
    - `slot` (string · required): Where on a page a banner sits.
      
      - `top`: Above the results, on the first page. Banners of several rules stand in rule order, the highest first.
      
      - `grid`: Among the tiles, before the one at its position, on the page that holds that tile.
      
      - `bottom`: After the results, on the last page. Banners of several rules stand in rule order, the highest first.
    
    - `position` (integer): With `grid`: the tile of the listed section it sits before, counted from 1 across the pages, so on page `p` it stands before hit `position - (p - 1) * per_page`. A higher rule's banner at the position asked for moves it to the next free one.
    
    - `kind` (string · required): - `banner`: A campaign's banner, as wide as the results.
      
      - `teaser`: A card to a page, such as a buying guide, the size of a tile.
    
    - `title` (string · required)
    
    - `text` (string)
    
    - `image` (object): - `url` (string · required)
      
      - `alt` (string · required): What the image shows, in the request's locale.
    
    - `link` (object): Where it leads; left out for a banner that only informs, and for one whose page the catalog no longer holds, which the trace names.
      
      A URL a response leads to, written by the merchant's routes where it is an entity's page.
      
      - `url` (string · required)
      
      - `entity` (string): The entity whose page it is; none for a fixed URL.
    
    - `rule` (string · required): The rule that placed it.
  
  - `suggestions` (array of objects): On the autocomplete surface, the queries the shopper may mean, in the order a search box lists them: the text suggestions, then the scoped ones.
    
    A query the shopper may mean while typing. A text suggestion completes what was typed with the catalog's own words, such as "Ecksofa" for "ecks"; a scoped one searches such a completion within a category, "Ecksofa in Sofas".
    
    - `text` (string · required): The query it suggests, as the catalog writes it.
    
    - `scope` (object): The category it searches in; none for a text suggestion.
      
      The category a scoped suggestion searches in, and its title in the request's locale.
      
      - `category` (string · required): The id of a category node, such as `wohnen/sofas`: the merchant's id of a `category` entity.
      
      - `title` (string · required)
  
  - `trace` ([object](/docs/reference/trace#search)): Why the response is what it is; only when the request asked for it.

- `400` ([problem](/docs/reference/problems#400)): The request cannot be answered as sent.

- `503` ([problem](/docs/reference/problems#503)): Nothing is searchable yet, or the engine is unreachable.

```sh title="Terminal"
curl -s http://127.0.0.1:7800/v1/search -H 'Content-Type: application/json' \
  -d '{ "query": "sofa", "filters": { "color": ["grey"] }, "per_page": 2 }' \
  | jq '{resolution, filters, sections: [.sections[] | {type, total, hits: [.hits[].entity]}]}'
```

```json title="Answer: 200 · 5.6 ms · release b25a6aaa"
{
  "resolution": {
    "category": "wohnen/sofas",
    "text": "",
    "complete": true
  },
  "filters": [
    {
      "where": {
        "color": [
          "grey"
        ]
      },
      "source": "shopper",
      "label": "Farbe",
      "labels": {
        "grey": "Grau"
      }
    },
    {
      "where": {
        "category": {
          "within": "wohnen/sofas"
        }
      },
      "source": "query",
      "label": "Kategorie",
      "labels": {
        "wohnen/sofas": "Sofas"
      }
    }
  ],
  "sections": [
    {
      "type": "category",
      "total": 1,
      "hits": [
        "category:wohnen/sofas"
      ]
    },
    {
      "type": "product",
      "total": 2,
      "hits": [
        "product:SOFA-ASKA-2",
        "product:SOFA-NIKO-SCHLAF"
      ]
    },
    {
      "type": "content",
      "total": 2,
      "hits": [
        "content:guide-sofa-finden",
        "content:guide-teppichgroesse"
      ]
    },
    {
      "type": "brand",
      "total": 0,
      "hits": []
    }
  ]
}
```

## Search with the request in the URL

`GET /v1/search`

The same search with the request's plain fields as URL parameters, so a link or `curl` is enough: `/v1/search?query=sofa&debug=1`. Filters and context need the request body of `POST /v1/search`.

### Parameters

- `query` (string · default: `""`): What the shopper typed.

- `on` (string): Where the request comes from. Default: `search`.
  
  - `search`: The search page's results.
  
  - `category_page`: A category's listing.
  
  - `autocomplete`: A search box's suggestions while the shopper types.

- `channel` (string): A channel's id, such as `de` or `ch`: a market or storefront.

- `locale` (string): A BCP 47 language tag such as `de`, `de-CH` or `en`. A locale falls back along its tag: `de-CH` to `de`.

- `category` (string): The category page being browsed.

- `sort` (string): The code of a sort option, such as `price_asc`. `relevance` is built in.

- `page` (integer · at least 1): The page, counted from 1.

- `per_page` (integer · 1 to 100): Hits per page, up to the limit [`per_page`](/docs/reference/limits#per_page). Default: 24.

- `count_only` (boolean · default: `false`): `1` or `true` answers with the totals alone.

- `redirect` (boolean · default: `true`): `0` or `false` answers with results where the response would send the shopper elsewhere.

- `debug` (boolean · default: `false`): `1` or `true` adds the trace to the response.

- `page_view` (string): The page the shopper loaded, as in the request body.

- `traffic` (string): Whose search this is. Default: `shopper`.
  
  - `shopper`: Everyone a storefront serves that none of the below is.
  
  - `test`: End-to-end tests, which mark their requests so.
  
  - `bench`: A latency benchmark, which marks its requests so.
  
  - `bot`: A crawler or another bot, as uap-core's maintained user agent rules name it; the Query API decides this from the `User-Agent` header of a request that says it is a shopper's.

### Answers

- `200` ([object](/docs/reference/query-api#search.answer)): The answer.

- `400` ([problem](/docs/reference/problems#400)): The request cannot be answered as sent.

- `503` ([problem](/docs/reference/problems#503)): Nothing is searchable yet, or the engine is unreachable.

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/search?query=graues%20Sofa&redirect=0&per_page=2' \
  | jq '{resolution, sections: [.sections[] | select(.total > 0) | {type, total, hits: [.hits[].entity]}]}'
```

```json title="Answer: 200 · 9.6 ms · release b25a6aaa"
{
  "resolution": {
    "category": "wohnen/sofas",
    "filters": {
      "color": "grey"
    },
    "text": "",
    "complete": true
  },
  "sections": [
    {
      "type": "product",
      "total": 2,
      "hits": [
        "product:SOFA-ASKA-2",
        "product:SOFA-NIKO-SCHLAF"
      ]
    }
  ]
}
```

## One product as its page shows it

`GET /v1/product`

Answers one product by its id or by its URL in the locale: its text, photos and categories, the details the type lists with their labels and units, its variants, and its offer as its tile shows it, all in the request's channel and locale and named by the release and the catalog snapshot that answered. With `debug`, the response carries the trace.

### Parameters

- `id` (string): The merchant's id of the product.

- `url` (string): The product's URL in the locale as the catalog writes it, such as `/p/aska-2-sitzer-sofa`, instead of its id.

- `channel` (string): The channel. Default: the release's first channel.

- `locale` (string): The locale. Default: the channel's first locale.

- `debug` (boolean · default: `false`): `1` or `true` adds the trace to the response.

### Answers

- `200` (object): The product.
  
  - `release` (string · required): The configuration that answered.
  
  - `snapshot` (string · required): The catalog the product was read from.
  
  - `product` ([Product](#Product) · required): A product as its page shows it, in the request's channel and locale.
  
  - `trace` ([object](/docs/reference/trace#product)): Why the response is what it is; only when the request asked for it.

- `400` ([problem](/docs/reference/problems#400)): The request names no product, or both an id and a URL, or a channel or locale the release lacks.

- `404` ([problem](/docs/reference/problems#404)): The channel sells no product by this id or URL in the locale.

- `503` ([problem](/docs/reference/problems#503)): Nothing is searchable yet, or the engine is unreachable.

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/product?id=SOFA-ASKA-2' | jq '.product | {id, title, url, brand, offer}'
```

```json title="Answer: 200 · 2.4 ms · release b25a6aaa"
{
  "id": "SOFA-ASKA-2",
  "title": "Aska 2-Sitzer-Sofa aus Samt",
  "url": "/p/aska-2-sitzer-sofa",
  "brand": "Halvard",
  "offer": {
    "price": 749.0,
    "sale_price": 649.0,
    "currency": "EUR",
    "availability": "in_stock",
    "stock": 16,
    "delivery_days": {
      "min": 3,
      "max": 5
    }
  }
}
```

## What page one of the merchant's URLs is

`GET /v1/resolve`

Answers what one of the merchant's URLs is, in the order normalize, legacy table, search page, category and brand paths, product URL: a listing with its search and results in one call, a product, a redirect to the final URL, a URL gone on purpose, or not found. The page carries the status, canonical and robots a storefront answers with; the call itself answers 200 for every page. With `debug`, the response carries the trace: each step of the order and what it found.

### Parameters

- `url` (string): The URL the shopper or crawler asked for: a path with its query string, such as `/wohnen/sofas?farbe=grau`, or a whole URL, whose scheme and host are ignored.

- `channel` (string): The channel. Default: the release's first channel.

- `locale` (string): The locale. Default: the channel's first locale.

- `results` (boolean · default: `true`): `0` or `false` answers with the decision alone, without the listing's results or the product, as URL tests and agents need. Default: `1`.

- `per_page` (integer · 1 to 100): Hits per page of a listing, up to the limit [`per_page`](/docs/reference/limits#per_page). Default: 24.

- `debug` (boolean · default: `false`): `1` or `true` adds the trace to the response.

- `page_view` (string): The page the shopper loaded, which the storefront names anew on every page load, as in a search request.

- `traffic` (string): Whose request this is. Default: `shopper`.
  
  - `shopper`: Everyone a storefront serves that none of the below is.
  
  - `test`: End-to-end tests, which mark their requests so.
  
  - `bench`: A latency benchmark, which marks its requests so.
  
  - `bot`: A crawler or another bot, as uap-core's maintained user agent rules name it; the Query API decides this from the `User-Agent` header of a request that says it is a shopper's.

### Answers

- `200` (object): The page, whatever its own status.
  
  - `release` (string · required): The configuration that decided.
  
  - `snapshot` (string · required): The catalog the paths were read from.
  
  - `type` (string · required): - `category`: A category's listing.
    
    - `brand`: A brand's listing.
    
    - `search`: The search page.
    
    - `product`: A product's page.
    
    - `redirect`: The URL lives elsewhere: `location` says where.
    
    - `gone`: The URL was removed on purpose.
    
    - `not_found`: Not a page OrbSearch knows; a shop with other pages answers it with its own routes first.
  
  - `status` (integer · required): The HTTP status the storefront answers with: 200, a redirect's 301, 302, 307 or 308, 404 or 410.
  
  - `search` (string · required): The search page's path in this locale, as `routes.json` names it, where the page's search box submits to.
  
  - `location` (string): Where a redirect leads: always the final URL, never a chain.
  
  - `canonical` (string): The URL a search engine should keep for this page; none where the page is `noindex` or not found.
  
  - `robots` (string): What search engines may do with the page; none for a redirect, a page that is gone or not found.
    
    - `index,follow`: One of the pages the merchant declared indexable.
    
    - `noindex,follow`: A page the merchant did not choose, such as a filter state or the search page; its links are still followed.
  
  - `entity` (string): The category, brand or product the page is the page of.
  
  - `breadcrumbs` (array of objects): A category page's ancestors and itself, from the root, for the trail above the page and its `BreadcrumbList`.
    
    One step of a page's trail.
    
    - `title` (string · required)
    
    - `url` (string · required)
  
  - `from` (string): The text a search typed that led to this category page, so the page can say what it shows and lead back.
  
  - `request` ([object](/docs/reference/query-api#search.body)): The search a listing runs, with the filters, sort and page its URL holds.
  
  - `results` ([object](/docs/reference/query-api#search.answer)): The listing's results, as `POST /v1/search` answers its `request`; left out with `results=0`. With `debug`, a redirect the listing's search decided keeps them too, so its trace says which rule or resolution sent it on.
  
  - `product` ([Product](#Product)): A product page's product, as `GET /v1/product` shows it; left out with `results=0`.
  
  - `trace` ([object](/docs/reference/trace#page)): Why the page is what it is; only when the request asked for it.

- `400` ([problem](/docs/reference/problems#400)): The request names a channel or locale the release lacks.

- `503` ([problem](/docs/reference/problems#503)): Nothing is searchable yet, or the engine is unreachable.

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/resolve?url=/wohnen/sofas%3Fcolor%3Dgrey' \
  | jq '{type, status, robots, entity, request}'
```

```json title="Answer: 200 · 12 ms · release b25a6aaa"
{
  "type": "category",
  "status": 200,
  "robots": "noindex,follow",
  "entity": "category:wohnen/sofas",
  "request": {
    "on": "category_page",
    "channel": "de",
    "locale": "de",
    "category": "wohnen/sofas",
    "filters": {
      "color": "grey"
    }
  }
}
```

## The robots.txt lines that keep crawlers off filter states

`GET /v1/robots`

The `Disallow` lines the merchant serves in their own `robots.txt`, written from `routes.json`: one for every parameter no indexable page uses, one for any URL with a second parameter, and one for the search page.

### Answers

- `200` (object): The lines.
  
  - `release` (string · required): The id of a release. A release is named by its content and never changes.
  
  - `lines` (array of strings · required): `Disallow` lines in the pattern form Google documents, to place under the shop's `User-agent: *`.

- `503` ([problem](/docs/reference/problems#503)): Nothing is searchable yet.

```sh title="Terminal"
curl -s http://127.0.0.1:7800/v1/robots | jq '.lines[:5]'
```

```json title="Answer: 200 · 1.4 ms · release b25a6aaa"
[
  "Disallow: /*?*&",
  "Disallow: /*?availability=",
  "Disallow: /*?brand=",
  "Disallow: /*?category=",
  "Disallow: /*?color="
]
```

**Guide:** [Search and browse](/docs/search) · [Product pages](/docs/product-pages) · [URLs and redirects](/docs/urls)

## `AppliedFilter`

A filter as the storefront shows it: a chip the shopper can remove. Removing it means taking `where` out of the request's filters, or for a filter the query named, sending its field and value in `dismissed`, which keeps its words as text.

- `where` ([Selector](#Selector) · required): What the filter keeps, with exactly one field or `category`, such as `{ "category": { "within": "wohnen" } }`.

- `source` (string · required): - `shopper`: The shopper chose it.
  
  - `query`: The query named it.
  
  - `rule`: A rule added it.

- `rule` (string): The rule that added it, when `source` is `rule`.

- `label` (string): The field's name in the request's locale, as its facet is labeled, such as "Farbe" or "Kategorie"; none for a rule's filter of several fields.

- `labels` (map of strings): What the values and categories it names read as in the request's locale, by code or id: an option's or group's label, a brand's or a category's title, such as `{ "grey": "Grau" }` or `{ "textilien/teppiche": "Teppiche" }`. A number, a range and a value nothing labels have none.

## `Comparison`

Compares a field's value. Several operators in one object combine with AND: `{ "gte": 10, "lt": 20 }`.

- `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.

- `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.

- `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.

- `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.

- `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.

- `exists` (boolean): True: the field has a value. False: it has none.

## `Product`

A product as its page shows it, in the request's channel and locale.

- `id` (string · required): The merchant's id of an entity, unique per type, such as `SOFA-LUND-3`. It travels unchanged; OrbSearch never rewrites it.

- `title` (string · required)

- `url` (string): The merchant's URL of the product in the locale.

- `alternates` (map of strings): The product's URL in each other locale of the channel, for a language switch and `hreflang`.

- `description` (string)

- `brand` (string)

- `badges` (array of strings): The badges the release's overlays give the product, in the locale, such as "Bestseller".

- `signals` (map of (boolean, number or string)): The signals the type returns as result fields, such as its rating and review count: those its tile shows.

- `images` (array of strings): Every photo of the product, its own first, then those of its variants.

- `categories` (array of strings): The categories the product is assigned to, the primary one first.

- `offer` ([ProductOffer](#ProductOffer)): What a shopper can buy best, as the product's tile shows it when one tile stands for the whole product: the variants with the best availability, and among them the lowest price paid and the soonest delivery.

- `details` ([array of ProductDetail](#ProductDetail)): What holds for the whole product, in the order the type's `details` list them: its own values and those every variant shares.

- `variants` ([array of ProductVariant](#ProductVariant)): The variants sold in the channel, in the catalog's order; none for a product the catalog sends without.

## `ProductDetail`

One detail of a product or variant, as the shopper's locale reads it.

- `attribute` (string · required): The code of an attribute definition, such as `color` or `seat_height`. The reserved words `any`, `all`, `not`, `category`, `brand`, `price`, `on_sale`, `availability`, `delivery_days` and `rating` cannot be attribute codes.

- `label` (string · required): The attribute's label in the locale.

- `values` (array of (boolean, number, string or object) · required): Its values in the attribute's unit: an option by its label, a number rounded to the attribute's precision, `true` or `false`, a date or text, or a range's bounds. Several for a list, such as two materials.
  
  A detail's value: one scalar, or a range's bounds.
  
  - `min` (number)
  
  - `max` (number)

- `unit` (string): The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `mm`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `cm`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `m`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `in`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `ft`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `g`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `kg`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `lb`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `oz`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `ml`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `l`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `w`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `kw`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `wh`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `kwh`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `mah`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `v`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `lm`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `kelvin`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `celsius`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `hz`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `mb`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `gb`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `tb`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `day`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `month`: The unit of a quantity's or a range's numbers, such as `cm`.
  
  - `year`: The unit of a quantity's or a range's numbers, such as `cm`.

- `swatch` (string): The swatch color of an option's one value, or of the group it shows under, as its facet draws it; none for a detail of several values.

- `image` (string): A swatch image of an option's one value, such as a pattern no single color shows; its URL.

## `ProductOffer`

An offer in the request's channel, with the sale price that holds at the time the live index run was built; the indexer builds again when a sale window opens or closes.

- `price` (number)

- `sale_price` (number): The reduced price, while a sale is on.

- `currency` (string · required): An ISO 4217 currency code, such as `EUR`.

- `availability` (string): One of `in_stock`, `out_of_stock`, `preorder` or `backorder`.

- `stock` (integer): How many are in stock, where the catalog says.

- `delivery_days` (object): How many days delivery takes, from the earliest to the latest.
  
  - `min` (integer · required)
  
  - `max` (integer · required)

## `ProductVariant`

A buyable variation of a product.

- `id` (string · required): The merchant's id of an entity, unique per type, such as `SOFA-LUND-3`. It travels unchanged; OrbSearch never rewrites it.

- `title` (string): The variant's own title, where the catalog gives it one.

- `url` (string): The variant's own URL in the locale, where the catalog gives it one.

- `images` (array of strings): The variant's own photos.

- `details` ([array of ProductDetail](#ProductDetail)): What sets the variant apart: its values that differ between the product's variants, such as its color and size, in the order of the product's details.

- `offer` ([ProductOffer](#ProductOffer)): An offer in the request's channel, with the sale price that holds at the time the live index run was built; the indexer builds again when a sale window opens or closes.

## `Selector`

Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.

`{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`

- `category` (object): The entity is in this category, or below it with `within`.
  
  A category, exactly or including everything below it.
  
  - `is` (string or array of strings): One value, or a list meaning any of them.
  
  - `within` (string or array of strings): One value, or a list meaning any of them.

- `any` ([array of Selector](#Selector) · at least 1 item): At least one of these holds.

- `all` ([array of Selector](#Selector) · at least 1 item): All of these hold; useful inside `any`.

- `not` ([Selector](#Selector)): This does not hold.

- `<key>` (boolean, number, string, array of (boolean, number or string), object or array of Selector): - `is` (string or array of strings): One value, or a list meaning any of them.
  
  - `within` (string or array of strings): One value, or a list meaning any of them.
