More Like This — now from an image
The same endpoint that finds similar products for a URL or UUID now accepts an image. Send a photo — a product shot, a look you want to match — and get similar products from your catalogs. Every example below is a real request with live results from production.
The third source identifier
source takes exactly one of url, uuid, or — new — image. The image itself is exactly one of b64 (base64 bytes) or url (fetched server-side from any public HTTPS host — retailer product-image URLs work directly). Everything else about the request — catalog, limit, include_facets, exclude_facets, embedding-column overrides, debug — works as it does for product sources.
| Guard | Behavior |
|---|---|
8 MB decoded cap | Larger payloads return 413. The pipeline downscales to 768px internally, so send a client-side-downscaled image (~1024px JPEG) — it's faster for you and identical in quality. |
price_preference | Must be "any" for image sources (400 otherwise) — there is no source product to be price-relative to. |
cursor | Supported. The cursor embeds the relaxation round that produced the page, so continuations re-run the exact converged query — deterministic within the 24h generation-cache window. Measured: page 2 in 0.52s. |
image.url fetch | Any public HTTPS image host — send retailer product-image URLs directly. Hardened fetch: HTTPS-only, no redirects, DNS + connected-peer public-address checks, size-capped while streaming. |
How an image becomes results — the resolution ladder
If the submitted bytes hash-match an indexed product image, the request becomes a product-source MLT — near-instant. Phase 1 ships the feasibility counter; the shortcut lands if real traffic shows exact matches.
A repeat image hits the cached generated query and runs search-only — measured 0.25s in production (vs 7.15s cold for the same image). Client integrations re-send identical images constantly, so this is where most real traffic lands. The response reports resolution: "cached_image_query".
A multimodal parse reads the image, the shared query processors build facets, and the search runs with zero-result facet relaxation. 5–8s typical, ~11s when relaxation rounds run. The response's resolution field always says which rung served you.
Catalog-scoped, no relaxation needed
"catalogs": ["jcrew"]The lean query a jewelry image generates matches directly — no relaxation. Scoping with the catalogs allowlist works exactly as it does for product sources.
{
"source": {
"image": {
"b64": "<base64 image bytes>" // or "url" on allowlisted hosts
}
},
"catalogs": ["jcrew"],
"limit": 6,
"debug": true
}
Generated retrieval text: “Gold-tone minimalist hoop earrings, medium-thick round tube design, polished metal finish.”
Round-1 relaxation: the logo brand softens
relaxationRounds: 1The model reads the visible logo and generates an exact brand_name: Emporio Armani filter. Your granted catalogs may not carry the brand, so the exact filter would pin the result set to zero — round 1 drops the inferred brand facets and recovers the search. A brand_name you pass yourself in include_facets is never dropped. Honest caveat: these results match on category and silhouette but not the minimalist look — logo styling is exactly the kind of visual signal text-derived retrieval carries weakly. Image-embedding retrieval/ranking is on the roadmap for cases like this.
{
"source": {
"image": {
"b64": "<base64 image bytes>" // or "url" on allowlisted hosts
}
},
"limit": 6,
"debug": true
}
Generated retrieval text: “Emporio Armani white leather low-top sneakers with lace-up closure, rounded toe, and signature eagle logo detail on the side.”
Round-2 relaxation: attributes soften too
relaxationRounds: 2Dress images generate rich attribute facets (neckline, pattern, dress style, length…). When the full conjunction over-constrains, round 2 additionally releases the attribute tier — the retrieval text still carries every detail, so results stay on-look while the hard filters relax.
{
"source": {
"image": {
"b64": "<base64 image bytes>" // or "url" on allowlisted hosts
}
},
"limit": 6,
"debug": true
}
Generated retrieval text: “Blue floral print midi wrap dress with long sheer sleeves, V-neckline, and ruffled trim detailing.”
Round-3 relaxation: last-resort visual tier
relaxationRounds: 3The deepest cascade: inferred brand, then attributes, then color and deep category levels. Top-level category, gender, and age-group facets are never dropped — recovery broadens the query without changing what kind of product comes back.
{
"source": {
"image": {
"b64": "<base64 image bytes>" // or "url" on allowlisted hosts
}
},
"limit": 6,
"debug": true
}
Generated retrieval text: “Brunello Cucinelli grey pebbled leather hobo bag with a single adjustable shoulder strap, minimalist structured silhouette, and subtle logo branding.”
Notes for integrators
- Facet relaxation is automatic and observable. Image-derived facets are all inferences, so zero-result searches retry with the least-trusted facet tiers dropped (inferred brand → attributes → color/deep category). With
debug: truethe response reportsrelaxationRoundsand exactly which facets were released. Facets you pass ininclude_facetsare never dropped. - Latency envelope: first sight of an image p50 ≤ 8s (~11s worst observed with relaxation); repeat images ~0.25s from the generation cache. The
debugpayload carries per-stage generation timings. - Downscale before sending. A ~1024px JPEG generates identical results to a 5000px original and uploads 50× faster. Full-resolution product photography commonly exceeds the 8 MB cap.
- Response shape: image sources return
sourceImage.hash+resolutioninstead of the resolved-product stanza; existing url/uuid responses are byte-identical to before.