Rank the right variant, not the parent.
When a shopper uploads a photo of a red T-shirt, they should see the red variant — not a generic parent card that maps to five colours. Trooply indexes each variant as its own CLIP embedding + Qdrant point, so visual similarity decides which colour or size ranks first. Collapse at render time when you want one card per product concept.
Three endpoints, no schema migration.
Variants ship as plain Qdrant payload — nothing to migrate on existing catalogues. Opt in per product.
Index the parent + N variants
POST /v1/products/with-variants takes a parent id and 1-50 variants. Each variant gets its own image, embedding, and Qdrant point, with parent_product_id stamped into its payload. Parent-level metadata is merged into every variant; variant-level fields win on conflict.
Search ranks variant-accurately
Visual search uses each variant's own embedding, so a red-shirt query surfaces the red variant above the blue one. The parent is never itself a point — only the variants are searchable. Metadata on every result includes parent_product_id so your frontend can group when it wants to.
Collapse at render (optional)
Pass collapse_variants: true on /v1/search/text or /v1/search/url. The highest-scoring variant per parent wins and carries sibling_variant_ids in its metadata — enough for an "other colours" strip without a second API call.
Shape the requests.
Index — parent with three colour variants
POST /v1/products/with-variants
Authorization: Bearer <token>
{
"product_id": "pegasus-42",
"metadata": {
"name": "Pegasus 42",
"brand": "Nike",
"category": "Footwear > Running",
"price": 129
},
"variants": [
{ "variant_id": "pegasus-42-red", "image_url": "https://cdn…/red.jpg", "metadata": { "color": "red" } },
{ "variant_id": "pegasus-42-blue", "image_url": "https://cdn…/blue.jpg", "metadata": { "color": "blue" } },
{ "variant_id": "pegasus-42-black", "image_url": "https://cdn…/black.jpg", "metadata": { "color": "black" } }
]
}
Response confirms the count + echoes every variant id. Each variant counts one-for-one against the plan's product quota — a 5-colour × 3-size catalogue is 15 products per SKU, same way merchants budget real inventory.
List siblings for a card's "other colours" strip
GET /v1/products/pegasus-42-red/variants
→ { "parent_product_id": "pegasus-42",
"count": 3,
"variants": [
{ "product_id": "pegasus-42-red", "metadata": { "color": "red", "parent_product_id": "pegasus-42" } },
{ "product_id": "pegasus-42-blue", "metadata": { "color": "blue", "parent_product_id": "pegasus-42" } },
{ "product_id": "pegasus-42-black", "metadata": { "color": "black", "parent_product_id": "pegasus-42" } }
] }
The endpoint accepts either the parent id or any variant's own id — it resolves the parent from the variant's payload. Qdrant runs a scroll against a payload index on parent_product_id, so response time is O(variant count), not O(catalog).
Collapse at search-time for tidy result grids
POST /v1/search/text
{ "query": "classic tee", "limit": 10, "collapse_variants": true }
→ { "count": 8,
"results": [
{ "product_id": "classic-tee-blue",
"similarity_score": 0.28,
"metadata": {
"parent_product_id": "classic-tee",
"sibling_variant_ids": ["classic-tee-red", "classic-tee-black"]
} },
… ] }
Without the flag, the same query returns the blue and red variants as separate rows. The flag is opt-in per request — other callers on the same tenant continue to get the full grid.
When to collapse, when not to.
Leave collapse off when…
- Shopper is searching by image — they usually want the exact colour they uploaded.
- You're surfacing an "available in these colours" grid on the PDP — the duplicates ARE the signal.
- You want the ranker to train on shopper clicks against specific variants (the feedback loop records per-variant clicks regardless).
Turn collapse on when…
- Text-search result grids where a 10-row page is visually clogged by 3 colours of the same SKU.
- Category landing pages where you want one card per concept.
- Any surface where
sibling_variant_idscan drive an inline "other colours" chip strip.
Index a catalogue in 15 minutes.
Sign up, grab an API key, POST your parents + variants. Trooply handles CLIP embedding, colour extraction, quality scoring, moderation — per variant, automatically.