Pre-launch API. The concepts described here are settled, but the API shape is not: request and response fields, parameters and defaults can still change. Build against it, and talk to your Luigi's Box contact before you put an integration into production.
A variant group is a set of products a shopper thinks of as one thing — the same
t-shirt in five sizes, the same phone in three colors. Each variant stays a complete,
independently indexed product with its own @id; the group is a shared key, not a
separate catalog object.
This page covers what variants do at query time. For how to send them, see Variant groups in the catalog object model and the product feed reference.
What Luigi’s Box knows about a group
Section titled “What Luigi’s Box knows about a group”You send grouping on a product as @group_id and @group_primary. Discovery does
not read those fields directly — Luigi’s Box derives a search attribute from each of
them, and every behavior below is driven by the derived pair:
| Derived attribute | Value |
|---|---|
lbx:group_id | The product’s @group_id, or the untyped part of its own @id when you send none. |
lbx:group_primary | The product’s @group_primary, or false when you send none. |
The lbx: namespace belongs to Luigi’s Box: these attributes are derived from what
you send and cannot be set directly — a feed or API request carrying an lbx: field
is rejected. They are public, so they come back on search hits like any other field
and you can read a hit’s group straight from a result.
Because lbx:group_id falls back to the product’s own identity, a product that
belongs to no group is a group of one. Nothing in discovery treats an ungrouped
product as a special case.
Collapsing search results
Section titled “Collapsing search results”By default, every matching variant is its own search result. A shirt stocked in five sizes takes five positions on the results page.
With collapsing enabled, a surface returns one result per variant group. Which member represents the group depends on the surface’s strategy:
| Strategy | Which products are retrieved | Which one is served |
|---|---|---|
variant_or_primary | All members. | The best-ranked member of the group. |
primary | Only members marked lbx:group_primary. | The group’s primary product. |
variant | Only members not marked primary. | The best-ranked non-primary member. |
variant_or_primary is the default and needs no primaries marked: it folds each group
down to whichever member ranked best for the query. Use it unless the storefront needs
to land shoppers on a specific record.
Collapsing is configured per surface. Ask your Luigi’s Box contact to enable it and to pick the strategy for a given search, collections, or recommendation surface.
What changes when collapsing is on
Section titled “What changes when collapsing is on”totalcounts groups, not products. A query matching 40 shirts across 8 groups reports 8.- Pagination pages through groups. A group served on page 1 is not served again on page 2, whichever of its members ranked best.
Filters and sorting still apply to individual products. A filter on size: XL
matches the XL variants and the groups they belong to survive collapsing; groups with
no matching member drop out entirely.
Resolving the rest of a group
Section titled “Resolving the rest of a group”A collapsed result gives you one product per group. To render swatches, a size
picker, or a “3 more colors” badge, ask for the group’s other members with
POST /discovery/v1/variants.
It is a follow-up to a search or collections response, like /facets — a token
authorized on either surface can call it.
curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/variants?channel_id=lbn_4hj9tv' \ -H 'Authorization: Bearer <token>' \ -H 'X-Lbx-Visitor-Id: 8f14e45f-ea0f-4b5c-9a1d-2b3c4d5e6f70' \ -H 'Content-Type: application/json' \ -d '{ "ids": ["product/sku-1001", "product/sku-1042"], "guid": "3ab549eb64a20a0c", "return_fields": ["@title", "url", "image_url", "color", "size"] }'channel_id is a query parameter, as on every discovery endpoint. There is no
surface_id: the request is authorized by the search or collections surface it follows
up on. Everything else goes in the JSON body.
| Query parameter | Required | Notes |
|---|---|---|
channel_id | Yes | Serving destination |
| Body field | Required | Default | Notes |
|---|---|---|---|
ids | Yes | — | Catalog @id values, used verbatim — the same ids search returned. 1 to 200 per request. |
guid | No | none | The guid from the originating /search or /collections response. Groups are then read from that result set. Omit it and every id resolves from the catalog. An empty string is rejected. |
return_fields | No | every attribute | Field projection applied to returned members. @id and @type are always included. |
Unknown body fields are rejected with a 422 rather than ignored.
The response maps each requested id to its group:
{ "variants": { "product/sku-1001": { "siblings": [ { "@id": "product/sku-1002", "@type": "product", "@title": "Blue Cotton T-Shirt, S", "size": "S" }, { "@id": "product/sku-1003", "@type": "product", "@title": "Blue Cotton T-Shirt, L", "size": "L" } ], "total": 3 }, "product/sku-1042": { "siblings": [], "total": 1 } }}Reading the response:
siblingsnever contains the requested id itself.totalis the group’s full size, the requested id included."total": 3with two siblings means a group of three.siblingsis capped at 100 members, returned in@idorder. When a group is larger,totalstill reports its full size, so truncation is detectable.- A product that stands alone returns empty
siblingsand"total": 1. - An id that is not in the catalog returns empty
siblingsand"total": 0.
See get_variants in the API reference for the full
schema and error responses.
Variants and ranking
Section titled “Variants and ranking”Interactions recorded on any member of a group also credit the group. A shopper who clicks the blue shirt in size M contributes to that product’s own popularity and to the group’s, so a group’s signal is the sum of what all its members earned.
Without group aggregation, a shirt selling well in six sizes would look six times less popular than an equally successful single-record product. An ungrouped product forms a group of one and is ranked on its own signal.
See also
Section titled “See also”- Catalog object model — the
@group_idand@group_primaryslots and their partial-update semantics. - Product feeds — mapping variant fields from a source feed.
- Content API — sending grouping over the API.
- Sorting and pagination — why a collapsed total counts groups.
- Discovery overview — where the
guidcomes from.
Was this page helpful?
Thanks.