Skip to content

    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.

    Product variants

    View source

    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.

    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 attributeValue
    lbx:group_idThe product’s @group_id, or the untyped part of its own @id when you send none.
    lbx:group_primaryThe 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.

    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:

    StrategyWhich products are retrievedWhich one is served
    variant_or_primaryAll members.The best-ranked member of the group.
    primaryOnly members marked lbx:group_primary.The group’s primary product.
    variantOnly 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.

    • total counts 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.

    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.

    Terminal window
    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 parameterRequiredNotes
    channel_idYesServing destination
    Body fieldRequiredDefaultNotes
    idsYesCatalog @id values, used verbatim — the same ids search returned. 1 to 200 per request.
    guidNononeThe 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_fieldsNoevery attributeField 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:

    • siblings never contains the requested id itself.
    • total is the group’s full size, the requested id included. "total": 3 with two siblings means a group of three.
    • siblings is capped at 100 members, returned in @id order. When a group is larger, total still reports its full size, so truncation is detectable.
    • A product that stands alone returns empty siblings and "total": 1.
    • An id that is not in the catalog returns empty siblings and "total": 0.

    See get_variants in the API reference for the full schema and error responses.

    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.