Search collections

The search collections a tenant has, the query endpoint POST /v1/search/query, the X-Revenexx-Locale and X-Revenexx-Channel headers, filters, sort, views, the response shape, and the suggest and events endpoints.

A search collection is an index the platform builds from an app's search.json. Every tenant has its own copy, so a query only ever sees the tenant it is sent for. A storefront reaches it from its server, never from the browser. See The BFF pattern.

The collections

CollectionDeclared byOne document per
productsthe products appproduct and write key (de, or de-DE in a regional tenant)

Under the default search_untranslated: hide, a product without its own text in a language other than the tenant's main language has no document in that language. See Languages and locale codes.

Category, manufacturer and page collections are being rolled out (as of 2026-10). Until then, category names reach a product search through the categories and category_labels fields of products.

GET /v1/search/collections lists what a tenant actually has, including collections declared by apps you installed. GET /v1/search/collections/{collection} returns one collection's definition: its fields, its defaults and its document count. An unknown collection answers 404.

What a products document carries

FieldFacetSortNotes
id, skuskuIdentity
name, descriptionnameTranslated, in the document's locale
kind, enabledyesenabled is always filtered to true by the query endpoint
manufactureryes
ean
categoriesyesCategory codes
category_labelsCategory names in the document's locale
attrs, attrs_localeAttribute maps, returned with a hit. Not searchable and not facets.
localeWhich locale the document is for
updated_atyesDefault order

Querying a collection

Request
curl -X POST https://api.revenexx.com/v1/search/query \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "X-Revenexx-Locale: de-DE" \
  -H "X-Revenexx-Channel: b2b" \
  -H "Content-Type: application/json" \
  -d '{
    "collection": "products",
    "view": "search",
    "q": "e27 led",
    "filters": {
      "categories": ["led", "e27"],
      "manufacturer": ["LEDVANCE"],
      "attrs.lichtstrom": { "min": 400, "max": 1100 }
    },
    "sort": "relevance",
    "page": 1,
    "per_page": 24
  }'

POST /v1/search/multi_search stays available for clients that build the search parameters themselves.

Headers

HeaderRequiredMeaning
X-Revenexx-TenantYesThe tenant. Checked against the API key.
X-Revenexx-LocaleNoThe locale to search in, as a full code (de-DE) or a bare language (de). Send the market locale the page renders in. The query matches documents under the full code and its bare language and returns one hit per product. A bare de is read as de-DE and en as en-US; other bare codes stay as they are. A malformed value is ignored.
X-Revenexx-ChannelNoA channel code. When the collection has a channels field, only documents visible in that channel come back.
X-Revenexx-MarketNoA market code. Accepted and passed on, not used for search yet (as of 2026-10).

Without X-Revenexx-Locale the query searches across every locale.

One collection per language

Collections that keep one index per write key are being rolled out (as of 2026-10). For those, the header picks the index: the exact code, else its language (de-AT → de), else an index of the same language (de → de-DE), else the tenant's default locale. Without the header, or with a malformed one, the default locale answers instead of every locale. The response header X-Revenexx-Locale-Served and the body's locale name the write key that answered, so a page can tell that de-AT was served from de. Words are stemmed for the index's language. Existing collections switch over tenant by tenant; until then the header works as described above and X-Revenexx-Locale-Served is absent.

Body

KeyDefaultMeaning
collection—The collection name, for example products.
viewsearchWhat the query is for: search, category, suggest, brand or listing. Free text matching [a-z0-9_]{1,32} is allowed for your own views. The view selects the relevance settings, so the same q ranks differently in an autocomplete than on a results page.
q*The search text. * matches everything, which is what a category listing sends.
filtersnoneField filters, combined with AND. See below.
sortrelevancerelevance, or <field>_asc / <field>_desc for a sortable field.
page11-based.
per_pagefrom the view, else 201 to 100.

Relevance settings such as the fields searched, their weights, typo tolerance or facet fields are not accepted in the body. They belong to the view and are maintained per tenant. Unknown keys are ignored.

Filters

ValueMatches
["led", "e27"]Any of the values
"LEDVANCE", 42Exactly this value
{ "min": 400, "max": 1100 }A numeric range. Either bound may be left out.
true / falseA boolean field

A filter on a field that does not exist or is not filterable, or a value containing a backtick, is dropped and named in ignored_filters. A sort on a field that is not sortable is dropped and named in ignored_sort. Neither fails the query.

Some filters are always applied and cannot be lifted by a caller: the tenant, enabled is true where the collection has that field, the channel when X-Revenexx-Channel is sent, and the locale when X-Revenexx-Locale is sent.

Views

Without tenant-specific settings, a view behaves like this:

ViewSearchesFacetsper_page
search, listing, categoryname, sku, description, manufacturermanufacturer, kind, categories, plus any attrs.<code> facet field the collection has20
suggestname, sku, as prefixesnone6
brandnamemanufacturer0

Fields the collection does not have are skipped, and a view name without built-in settings behaves like search. A tenant can tune each view in Cockpit, and the storefront call does not change.

The response

Response
{
  "query_id": "q_8f2c1a",
  "found": 154,
  "page": 1,
  "per_page": 24,
  "search_time_ms": 6,
  "locale": "de-DE",
  "hits": [
    { "document": { "id": "…", "sku": "LV-E27-806", "name": "LED-Lampe E27 806 lm" }, "highlight": { }, "text_match": 578 }
  ],
  "facets": [
    { "field": "manufacturer", "values": [ { "value": "LEDVANCE", "count": 412 } ] },
    { "field": "attrs.lichtstrom", "values": [ { "value": "806", "count": 37 } ], "stats": { "min": 250, "max": 1521 } }
  ],
  "actions": [ { "type": "redirect", "url": "/c/buegeln" } ],
  "ignored_filters": [],
  "ignored_sort": null
}
KeyMeaning
query_idIdentifies this query. Keep it if you report clicks later.
foundTotal matches across all pages.
localeThe locale that was applied, as a full code, "" when none was sent.
hitsOne per product, in rank order. document is the stored document, highlight the matched snippets.
facetsPer facet field, the values in the current result and how many hits each has. Build the filter panel from this list rather than from a hard-coded one. Numeric facets carry stats.
actionsWhat the merchant attached to this query: redirect (send the buyer to url instead of a results page), banner, or metadata with free data. Handle redirect at least.
ignored_filters, ignored_sortWhat was dropped from the request, so you can see a typo instead of an unfiltered result.

Errors

StatusWhen
400The body is malformed, or X-Revenexx-Tenant is missing. The message says what.
401No valid API key or token.
403The key is not valid for this tenant or lacks the search scope.
404The collection does not exist for this tenant, or search is not enabled for it.
429Too many requests. Retry with backoff.
502, 503The search service did not answer. Retry with backoff.

See Errors for the error envelope.

Suggestions as you type

POST /v1/search/suggest answers a search box with a few hits from several collections in one call. It takes the same headers as a query.

Request body
{ "q": "e27", "collections": ["products", "categories"], "per_page": 5 }
KeyDefaultMeaning
q—The text typed so far, 1 to 200 characters. Required.
collectionsproducts, categories, manufacturers, pagesUp to six. Collections the tenant does not have are skipped.
per_pagefrom the suggest view1 to 20 hits per collection.

The response carries query_id, locale, actions and one entry per collection in groups, each with collection, found and hits shaped like a query's hits. Each collection uses its suggest view. A collection that fails drops out of groups and the rest still answer.

Reporting what buyers do

Send POST /v1/search/events from your server when a buyer acts on a result, so the tenant's search analytics can show which queries lead somewhere. It takes the same headers as a query, and the locale comes from X-Revenexx-Locale.

KeyRequiredMeaning
typeYesclick, add_to_cart or purchase.
collectionYesThe collection the hit came from.
doc_idExcept for purchaseThe hit's document id, up to 128 characters.
query_idFor clickThe query_id of the query or suggestion the hit came from.
positionNoThe hit's position in the result, 1 to 1000.
user_tokenNoAn anonymous id for the buyer, up to 128 characters. It is stored hashed. Never send an email address.
qNoThe query text, up to 200 characters.

It answers 202 with { "accepted": true }. Both endpoints take POST only.

Where to go next

Was this page helpful?