# Browse a collection `GET /discovery/v1/collections` Category / collection browsing with merchandising support. Rate limited: a ``429`` carries ``Retry-After``. ## Request example ```bash curl --request GET \ --url 'https://api.eu1.luigisbox.ai/discovery/v1/collections?channel_id=&surface_id=&user_id=&personalize=true&filters=&size=10&debug=false&type=&collection_filters=&sort=&cursor=' \ --header 'user-agent: ' \ --header 'referer: ' \ --header 'x-lbx-visitor-id: ' \ --header 'Authorization: Bearer ' ``` ## Query Parameters | Name | Type | Required | Description | Constraints | |---|---|---|---|---| | `channel_id` | `string` | Yes | Channel identifier (serving destination). | >= 1 characters | | `surface_id` | `string` | Yes | Surface identifier, lbs__. The service segment must match the endpoint: lbs_search_* on /search, lbs_collection_* on /collection, lbs_recommender_* on /recommender. | >= 1 characters | | `user_id` | `string` | No | Signed-in shopper's stable identifier, your own opaque key. Omit it or send it blank for anonymous traffic; the strings undefined, null, None, NaN and [object Object] (any case) also count as anonymous. | — | | `personalize` | `boolean` | No | Personalize this request, and assert that the shopper has consented to behavioural personalization. Defaults to true: personalized, and recorded as consented. Send false for a shopper who has not consented; read it from your consent manager on every request. When true and no sort is given, the result set (and, where returned, its request_key and cursors) is bound to the shopper: user_id and the X-Lbx-Visitor-Id sent. Under an explicit sort personalization does not change the order, so the result set is shared. | — | | `filters` | `string` | No | Filter expression, for example 'brand = nike AND lbx:price < 100'. At most 100 conditions, in groups nested at most 10 deep — conditions are counted and the groups holding them are not. An expression past either is a `422` with `exception_details.error_code` `filter_too_complex`. | <= 4096 characters | | `return_fields` | `Array` | No | Field projection list. Each entry is either '*' (alone, all fields) or a field path, optionally ending in '*' to take a whole subtree (e.g. 'attributes.title' or 'attributes.*'). '*' cannot be combined with other names. | <= 100 items | | `size` | `integer` | No | Maximum number of items to return. Must be between 1 and 100. | >= 1; <= 100 | | `debug` | `boolean` | No | Request ranking diagnostics. Honoured only on the first page for callers holding ``discovery:debug``; silently ignored otherwise. | — | | `type` | `string` | Yes | Object type to retrieve (e.g. 'product', 'category', 'article'). | >= 1 characters | | `collection_filters` | `string` | Yes | Filter expression defining the collection scope. Required and non-blank: the scope is what bounds a query-less collection page. | >= 1 characters; <= 4096 characters | | `sort` | `string` | No | Sort expression, e.g. 'price:asc'. | <= 256 characters | | `cursor` | `string` | No | Opaque pagination cursor from a prior response; omit for the first page. Valid only with the same request parameters other than size and, when personalize is true and no sort is given, the same user_id and X-Lbx-Visitor-Id as the request that returned it; otherwise the request fails with a 422. | <= 128 characters | | `touchpoint` | `anyOf(CollectionTouchpoint, null)` | No | Where in the shopper's journey these results appear. The value is recorded with the request so reporting can tell apart results served at different points of the journey; a request without one is recorded with no touchpoint. Send it on every request. It does not influence ranking. | — | ## Header Parameters | Name | Type | Required | Description | Constraints | |---|---|---|---|---| | `user-agent` | `string` | No | Originating client's User-Agent. Optional; used to derive the platform (ios, android, web) the result set is attributed to. A backend caller should forward the end user's value rather than its own. | — | | `referer` | `string` | No | Originating page URL. Optional; lets the result set be attributed to the page it was served on. | — | | `x-lbx-visitor-id` | `string` | Yes | Stable per-browser visitor identifier, generated client-side and persisted (e.g. a UUID in localStorage or a first-party cookie). Send it whether or not personalization is on: it is what links a request to a visitor's history and to the analytics events reporting it. Keep it stable across page loads, sessions and token refreshes; never generate it server-side, or every visitor collapses into one. | >= 1 characters | ## Responses | Status | Description | Content | |---|---|---| | `200` | Successful Response | application/json: CollectionResponse | | `400` | Malformed request | application/json: ErrorResponse | | `401` | Missing or invalid credentials | application/json: ErrorResponse | | `403` | Authenticated but not permitted | application/json: ErrorResponse | | `404` | Resource not found | application/json: ErrorResponse | | `422` | Request validation failed | application/json: DiscoveryValidationError | | `429` | Rate limit exceeded | application/json: ErrorResponse | | `503` | Service temporarily unavailable | application/json: ErrorResponse | ### 200 Successful Response #### application/json Schema: `CollectionResponse` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `debug` | `anyOf(object, null)` | No | Ranking diagnostics — an open object whose contents carry no compatibility guarantee. Populated only when the caller passed ``debug=true`` AND holds the ``discovery:debug`` permission. Omitted from the response otherwise. | — | | `debug.*` | `any` | No | Additional property. | — | | `guid` | `string` | Yes | Deprecated alias of ``request_key`` carrying the same value. Read ``request_key`` instead: ``guid`` will be repurposed as the per-request attribution identifier once clients have migrated. | deprecated | | `hits` | `Array` | Yes | Ranked list of result hits — only objects this channel can serve. | — | | `hits[].@disclosure` | `Disclosure` | No | Present only on a hit whose placement is disclosed — one a disclosing campaign's rules pinned or boosted, or one an ad platform sold. Absent otherwise; never null. | — | | `hits[].@disclosure.advertiser` | `string` | No | The person or company on whose behalf the placement is shown, e.g. `Northwear Ltd`, as the campaign wrote it. Present when the campaign names one. | <= 128 characters | | `hits[].@disclosure.payer` | `string` | No | Who paid for the placement. Present when the campaign names one. | <= 128 characters | | `hits[].@disclosure.provider` | `string` | No | The ad platform that sold the placement. Absent for a campaign-authored disclosure. | — | | `hits[].@disclosure.recipient_parameters` | `Array` | No | What decided that this shopper is shown the placement on this request: `search_query` for what they searched, `collection` for the collection they browse, `anchor` for the products a recommendation is shown for, or `none`, alone, when nothing about the shopper did. Codes, for you to word in the shopper's language; more may be added, so handle one you do not know generically. Present on a campaign's placement. | >= 1 items | | `hits[].@disclosure.tracking` | `object` | No | Impression and click URLs to call, under the ad platform's own key names. Absent for a campaign-authored disclosure, and when the platform returned none. | — | | `hits[].@disclosure.tracking.*` | `string` | No | Additional property. | — | | `hits[].@disclosure.type` | `string` | Yes | How the placement is disclosed. `sponsored` marks a paid placement. | allowed value: sponsored | | `hits[].@id` | `string` | Yes | Unique identity of the hit. | — | | `hits[].@type` | `string` | Yes | Type of the hit (e.g. 'product'). | — | | `hits[].*` | `any` | No | Additional property. | — | | `next_page_cursor` | `anyOf(string, null)` | Yes | Opaque cursor for the next page, or ``null`` when there is none — read ``pagination_status`` to tell why. Pass it back verbatim as ``cursor`` to fetch the next page. A page may return fewer than ``size`` hits *and* still carry a cursor: objects that are no longer servable are dropped from the page, so keep paging until the cursor is ``null``. | — | | `pagination_status` | `string` | Yes | Why this response does or does not carry a ``next_page_cursor``: ``more`` — more pages exist and ``next_page_cursor`` is set; ``exhausted`` — this was the last page; ``unsupported`` — this result set cannot be paged and retrying will not produce a cursor; ``unavailable`` — pagination state could not be saved for this request (a transient failure) and re-issuing the search may succeed. | allowed values: more, exhausted, unsupported, unavailable | | `request_key` | `string` | Yes | Key derived from the request parameters that decide which objects are returned. Two identical requests produce the same value. A request with ``personalize`` set to true and no explicit ``sort`` is also scoped to its shopper (``user_id`` and ``X-Lbx-Visitor-Id``), so two visitors sending it get different values; every other request keys the same for every shopper. Pass it to ``/facets`` and ``/variants`` to address the same result set; a cursor is only valid together with the request key it was issued for. | — | | `total` | `anyOf(integer, null)` | Yes | Authoritative total across the whole result set, counting only the objects this channel can serve. ``null`` when the result set is larger than is counted exactly — fall back to ``total_approx`` in that case. Calling ``/facets`` for the same result set may fill it in. When the surface collapses variants, this counts variant groups, not individual objects. | — | | `total_approx` | `integer` | Yes | Approximate total for the whole result set, counting only the objects this channel can serve. Always present, so use it as the fallback when ``total`` is ``null``. It may be an estimate, so prefer ``total`` wherever that is set. | — | ##### Response example ```json { "debug": {}, "guid": "example", "hits": [ { "@disclosure": { "advertiser": "example", "payer": "example", "provider": "example", "recipient_parameters": [ "none" ], "tracking": { "additionalProperty": "example" }, "type": "sponsored" }, "@id": "example", "@type": "example" } ], "next_page_cursor": "example", "pagination_status": "more", "request_key": "example", "total": 1, "total_approx": 1 } ``` ### 400 Malformed request #### application/json Schema: `ErrorResponse` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `ExceptionDetails` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 401 Missing or invalid credentials #### application/json Schema: `ErrorResponse` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `ExceptionDetails` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 403 Authenticated but not permitted #### application/json Schema: `ErrorResponse` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `ExceptionDetails` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 404 Resource not found #### application/json Schema: `ErrorResponse` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `ExceptionDetails` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 422 Request validation failed #### application/json Schema: `DiscoveryValidationError` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `DiscoveryExceptionDetails` | Yes | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `anyOf(string, CursorErrorCode, SurfaceErrorCode, TouchpointErrorCode)` | No | Why a well-formed request was refused: a filter the catalog cannot serve, a cursor from another result set or one that cannot be read, a surface called on the wrong endpoint, or a touchpoint that does not serve the endpoint's intent. Safe to branch on; `reason` is not. Absent when the request failed field validation instead (see `validation_errors`). | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | Yes | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "filter_syntax", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 429 Rate limit exceeded #### application/json Schema: `ErrorResponse` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `ExceptionDetails` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 503 Service temporarily unavailable #### application/json Schema: `ErrorResponse` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `ExceptionDetails` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ```