How to build product search for an AI shopping assistant | simlir blog
How to build product search for an AI shopping assistant
A seven-stage architecture for shopping assistant search: intent, retrieval, structured records, provenance, comparison, MCP or REST access, and failure testing.
Useful product search connects intent, retrieval, structured records and evidence. Credit: Generated for Simlir
The short answer
Reliable shopping search needs five things working before the writing matters: intent handling, product retrieval, structured records, comparison logic and source context. An assistant that generates a confident paragraph on top of a weak retrieval layer will be wrong fluently. The practical shape is a seven-stage pipeline: scope the request to a market, retrieve candidates from text or an image, return records with identifiers and specifications, keep provenance and checked time attached, compare on criteria you can name, expose the whole thing through MCP or REST, then deliberately test what happens when fields are missing.
Most shopping assistants fail in the same order. The demo works because someone typed a product name. Then a real user says “something like this but under sixty quid”, the retrieval layer returns three unrelated items, and the model writes a persuasive recommendation for a product nobody wants. The fix is almost never a better prompt. It is a better pipeline underneath.
1. Define the request and the market
Before retrieval, resolve two things: what the shopper is actually asking for, and which market the answer applies to.
Intent decomposition is worth doing explicitly. A request like “a quiet dishwasher for an open-plan flat, under £500” contains a product type, a soft constraint that maps onto a specification, a context that explains the soft constraint, and a hard numeric bound. Hard bounds should become filters. Soft constraints should stay in the semantic query, where they can influence ranking without excluding good candidates on a technicality.
Fragment
Type
Where it belongs
“dishwasher”
Product type
Query, optionally category
“quiet”
Soft constraint
Semantic query — then verify against spec
“open-plan flat”
Context
Semantic query; also useful for explaining the choice
“under £500”
Hard bound
max_price
Shopper is in the UK
Scope
gl=gb — required
Market is not optional and should not be guessed. simlir requires the shopper country on every search — gl on the REST search and lookup endpoints, market on image search and the MCP tools — and never infers it server-side. The response echoes the market back. Compare the two in your client and treat a mismatch as a bug rather than a curiosity.
2. Retrieve candidates from text or an image
Give your assistant all three entry points. Shoppers do not arrive with uniform certainty.
For an image request, send a hosted HTTPS URL rather than raw bytes. That keeps the tool call text-only, which matters in an agent workflow: the model does not need to inspect the image itself before deciding to call the tool.
When your workflow already holds an identifier — from a merchant feed, an ERP row, a checkout export or a retailer page — skip semantic ranking entirely and use exact lookup. It costs 1 credit rather than 2, and it does not introduce ranking ambiguity where none is needed. Use type=auto to try GTIN, then MPN, then SKU. GTIN is the strongest cross-retailer key; a retailer SKU is contextual and should be paired with a retailer filter.
3. Return a record your application can actually use
This is the stage that decides how good the rest of the assistant can be. If retrieval hands you a title and a link, every downstream feature — sorting, filtering, comparison tables, explanations, follow-up questions — has to be reconstructed with a model, expensively and unreliably.
product object (abridged)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"brand": "Optimum Nutrition",
"category": "protein powder",
"gtin": "5060245603478",
"title": "Optimum Nutrition Gold Standard Whey Protein Powder",
"product_description": "Premium whey protein powder with 24g protein per serving...",
"key_selling_points": ["24g protein per serving", "5.5g BCAAs", "Informed Sport certified"],
"spec": {
"protein_per_serving": "24g",
"servings": "29",
"calories_per_serving": 120,
"flavour": "Double Rich Chocolate"
},
"image_url": "https://images.optimumnutrition.co.uk/whey-front.jpg",
"model_number": "GS100W-2270G-DRC",
"retailer_sku": "ON-2270G-GB",
"review_score": 4.8,
"review_count": 20,
"price": {
"amount": 29.99,
"currency": "GBP",
"retailer": "Holland & Barrett",
"as_of": "2026-04-07"
},
"links": {
"retailer": "https://www.hollandandbarrett.com/shop/product/..."
},
"market": "gb",
"relevance_score": 0.923
}
Four things in that record do disproportionate work:
id is a stable UUID that never changes. Cache it. Later you can re-fetch that exact product through /v1/product for 1 credit instead of repeating a 2-credit search — which is what you want when a user clicks a result or you refresh a saved comparison.
spec is what makes two candidates comparable. Marketing prose is not.
review_score paired with review_count is the honest form. A 5.0 from three people is not better than a 4.6 from nine hundred, and your ranking should know that.
links.retailer is the destination. It may be null when there is no verified retailer product URL for that row, so handle the null rather than rendering a dead button. links.buy is a compatibility alias that resolves to the same URL.
4. Preserve provenance and checked time
A price is not a timeless fact. In the simlir contract it arrives as an object with an amount, a currency, the retailer where it was observed, and as_of — the date it was last seen.
Carry all four through your system. The moment your assistant flattens price to a single number, it loses the ability to say “£29.99 at Holland & Barrett, seen 7 April” and is left saying “£29.99” as though it were a quote. The first sentence survives contact with a shopper who clicks through to a different number. The second does not.
An assistant earns trust by being explicit about why one product beat another. That requires comparison logic that lives in your code, not implicitly inside a model’s output.
A workable pattern:
Retrieve a candidate set larger than you intend to show.
Apply hard constraints as filters — price bounds, brand, category.
Score the remainder on named criteria: how well the specification meets the soft constraint, review score weighted by review count, price position within the set, and completeness of the record.
Drop candidates whose critical fields are missing rather than presenting them with silent gaps.
Pass the shortlist and the reasons to the model, and ask it to explain the ranking — not to invent it.
The division of labour matters. Retrieval and scoring are deterministic and testable. Language generation is where the model should be, because that is what it is good at. Teams that let the model do the ranking end up unable to answer the simplest support question: why did we recommend this?
6. Expose the capability through MCP or REST
Choose based on who decides when to search.
REST when your code decides
A backend service, a comparison page, a scheduled refresh, a catalogue join. You control timing, caching, retries and cost. Endpoints: /v1/search, /v1/search/image, /v1/lookup, /v1/product.
MCP when the model decides
A compatible tool client discovers simlir_search_products, simlir_search_products_by_image and simlir_lookup_products_by_identifier through tools/list and calls them when the conversation calls for it.
Both routes share authentication, credits, market scope and response shape, so this is an integration decision rather than a data decision. Many production assistants use both: MCP inside the conversational loop, REST for the pages and jobs around it. The full comparison is here.
7. Test the failure cases before your users find them
Shopping data is uneven. Some rows have no verified retailer URL. Some have no visual enrichment. Some categories have thin review coverage. An assistant that only works on complete records is an assistant that works in the demo.
Condition
What you will see
Handle it by
Sanity filter removes the page
Short or empty results, low meta.count
A real empty state that suggests a broader query — never a fabricated recommendation
No verified retailer page
links.retailer and links.buy are null
Suppress the button, keep the product visible
Row not visually enriched
visual is null
Fall back to image_url and the text description
Sparse specification
Few keys in spec
Down-rank for comparison; do not infer missing values
Stale snapshot
An old price.as_of
Show the date, or hide the price and link out
Out of credits
402
Degrade to cached results with a visible caveat
Rate limited
429, with X-RateLimit-Reset
Back off using the header, queue the request
Embedding failure
502
Retry once, then fall back to exact lookup if you hold an identifier
Log meta on every call. Count, market, response time and credits used are four numbers that will tell you what is wrong with your assistant long before a user complains.
Architecture checklist
Market is explicit on every requestSent as gl or market, echoed in the response, compared client-side.
Hard constraints are filters, soft constraints are query textNumeric bounds go to min_price and max_price; qualitative needs stay semantic.
All three retrieval modes are wiredSemantic search, image search, and exact lookup for known identifiers.
Product UUIDs are cachedRe-fetch with /v1/product at 1 credit instead of repeating a search.
Price stays an objectAmount, currency, retailer and as_of travel together through your whole stack.
Ranking is deterministic and inspectableWritten in your code, testable, explainable to a support agent.
Every null has a renderingNull retailer link, null visual, thin spec, no reviews.
Empty results are a designed stateNot an exception, and never filled in by the model.
Response metadata is loggedCount, market, latency and credits per call.
Error codes are handled individually402, 429, 502 and 503 need different behaviour.
Frequently asked questions
Should I call the API from the model or from my backend?+−
Both patterns are valid and they answer different questions. If the model should decide when a product search is warranted mid-conversation, use MCP. If your code knows when to search — a results page, a refresh job, a feed join — use REST, where you control caching, retries and spend. Assistants with a UI around them usually end up using both.
How do I stop the assistant recommending products that do not match?+−
Verify after retrieval instead of trusting ranking alone. Apply hard constraints as filters, then check the returned spec against the shopper's stated requirement in your own code, and drop candidates that fail. Also respect the sanity filter: if meta.count is zero, that is a genuine answer, and the correct response is to say so rather than to present the closest thing available.
What does a product search cost?+−
Semantic search costs 2 credits, image search 3, and exact identifier lookup 1. Fetching a known product by its UUID through /v1/product also costs 1. There are no monthly minimums. Caching UUIDs from an initial search and re-fetching individual products is the simplest way to reduce spend on a browsing interface. Current rates are on the pricing page.
What are the rate limits?+−
A free account is limited to 10 requests per minute with a burst of 20 and a daily cap of 10. The standard tier allows 1,000 requests per minute with a burst of 5,000 and no daily cap. Enterprise limits are set individually. These are request ceilings rather than a data allowance — paid operations still consume credits. Every response carries X-RateLimit-Remaining and X-RateLimit-Reset.
Can I use the results for a price comparison feature?+−
Yes, that is a core use case, provided you present prices as dated snapshots rather than live quotes and link through to the retailer page for the current offer. Use of API responses is governed by the API and data licence, and bulk exports require separate written permission.
MCP suits tool use inside an agent workflow. REST suits direct service integration. A decision table, auth notes and an implementation checklist for choosing between them.
Image product search is only useful when the result is more than a visual match. The stages from photo to structured candidates, and how to handle confidence and ambiguity.