Instant Search
Instant search powers search-as-you-type. One request returns two things: the typeahead suggestions that fill the dropdown under the search box, and a small product panel to show beside them. Before running the product search, the endpoint can resolve the partial text the shopper has typed to one of those suggestions, so a shopper who has typed runn sees products for running shoes rather than for the fragment runn.
Without this endpoint you would call search suggestions and then /search on every keystroke, and decide in your own code whether the top suggestion should replace what the shopper typed. Instant search makes that decision on the server, saves a round trip, and applies guards that stop a suggestion from hijacking a SKU or a very short prefix.
Endpoint
POST /api/v1/indexes/{index_name}/instant-search
Authenticate with the x-marqo-index-id: {index_id} header, which is safe to use from a storefront. An Authorization: Bearer {api_key} header is also accepted.
Example
- cURL
- JavaScript
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/instant-search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "runn",
"limit": 6,
"mode": "top_suggestion",
"attributesToRetrieve": ["productTitle", "price", "variantImageUrl"]
}'
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/instant-search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "runn",
limit: 6,
mode: "top_suggestion",
attributesToRetrieve: ["productTitle", "price", "variantImageUrl"],
}),
});
Response
{
"hits": [
{
"productTitle": "Trail Running Shoes",
"price": 129.99,
"variantImageUrl": "https://cdn.example.com/trail-runner.jpg",
"_id": "trail-runner-001",
"_score": 0.87
},
{
"productTitle": "Lightweight Road Running Shoes",
"price": 149.99,
"variantImageUrl": "https://cdn.example.com/road-runner.jpg",
"_id": "road-runner-002",
"_score": 0.81
}
],
"suggestions": [
{ "suggestion": "running shoes" },
{ "suggestion": "running shorts" },
{ "suggestion": "running jacket" }
],
"resolvedQuery": "running shoes",
"query": "running shoes",
"facets": {},
"limit": 6,
"offset": 0,
"totalHits": 0,
"processingTimeMs": 41
}
The shopper typed runn. The top suggestion was running shoes, and because runn starts a word of it, the product panel was searched for running shoes. resolvedQuery tells you which query the panel reflects.
Request parameters
The request body is validated strictly. Any field not listed here is rejected with 400 and the message Unrecognized param(s): 'field', so an unsupported option never silently does nothing.
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | Required | The partial text the shopper has typed so far. Suggestions are fetched for this text verbatim; the product panel searches the resolved query. |
limit | integer | Index search default | Number of products in the panel. Must be between 1 and 100. Keep this small for a typeahead panel. |
suggestionLimit | integer | Index instant-search default (6) | Number of suggestions to return. Must be between 1 and 25. |
mode | string | Index instant-search default (typed_only) | How the typed text is resolved before the panel search. One of top_suggestion, shortest_suggestion or typed_only. See Resolution modes. |
namespace | string | None | Restrict suggestions to one namespace, for example a language. Same semantics as namespace on search suggestions. |
profileId | string | Index default | Search profile to use for the product panel. Use a light profile built for typeahead. |
filter | string | None | Filter applied to the product panel, as on /search. |
attributesToRetrieve | array of strings | Index default | Fields to return for each product in the panel. Request only what the panel displays. |
userId | string | None | Shopper identifier, forwarded to the product panel search for personalization. |
sessionId | string | None | Session identifier, forwarded to the product panel search. |
The index defaults for suggestionLimit and mode, along with the resolution thresholds described below, are configured per index. Contact your Marqo representative to change them.
Parameters that are not accepted
Instant search paints a typeahead panel, not a full results page. The following /search parameters are rejected as unrecognized: offset, sortBy, facets, useDynamicFacets, collapseFields, disableMerchandising, language, geoLocation, sizeAffinity, relevanceScore and collectionName. If you need pagination, sorting or facets, call /search when the shopper submits the query.
Resolution modes
Every mode fetches suggestions and returns them in suggestions. The modes differ only in whether one of those suggestions may replace the typed text as the query for the product panel.
| Mode | Behaviour |
|---|---|
typed_only | Always search the typed text. Suggestions are returned for the dropdown but never override the query. This is the default and works on every index. |
top_suggestion | Search the most popular suggestion when the typed text starts a word of it. Otherwise search the typed text. |
shortest_suggestion | Among all returned suggestions that the typed text starts a word of, search the shortest one. Ties go to the more popular suggestion. Otherwise search the typed text. |
How resolution works
In top_suggestion and shortest_suggestion mode the endpoint applies these rules in order. Comparison ignores case, collapses repeated or trailing whitespace, and applies Unicode NFC normalisation, so decomposed accented characters match their composed equivalents in the suggestion list.
- Minimum length. If the typed text is shorter than the index's minimum, which is 3 characters by default, the typed text is searched. Suggestions for two-character prefixes are too noisy to search on.
- No suggestions. If the suggestion list is empty, the typed text is searched.
- Numeric guard. If the typed text is made only of digits and separators (for example
36805,123-456or1234 5678), the typed text is searched. A shopper typing a part number wants that part number, not a suggestion that happens to share a prefix. This guard is on by default and can be disabled per index. - Word-start match. A suggestion can only override the typed text if the typed text starts a word of the suggestion: it matches at the very start, or immediately after a space.
talvimatchesnaisten talvitakki;mendoes not matchwomen's shoesandartdoes not matchsmart tv, because those are mid-word matches. Intop_suggestionmode only the first suggestion is tested; inshortest_suggestionmode every returned suggestion is tested and the shortest match wins. - Typed text is more specific. If no suggestion passes the word-start test, the typed text is searched. This is what happens once the shopper has typed past the suggestion, for example
running shoes menwhen the top suggestion isrunning shoes.
Worked examples
Assume the suggestions returned for the prefix are, in popularity order, pokemon cards, camera, camping chair.
| Typed text | top_suggestion | shortest_suggestion | Why |
|---|---|---|---|
cam | cam | camera | cam does not start a word of pokemon cards, so the top suggestion is rejected. In shortest mode, camera and camping chair both match and camera is shorter. |
ca | ca | ca | Below the 3 character minimum. |
36805 | 36805 | 36805 | Numeric guard: never overridden. |
camera bag | camera bag | camera bag | Nothing in the list has a word starting with camera bag, so the typed text is the more specific query. |
And with suggestions running shoes, running shorts, trail running shoes:
| Typed text | top_suggestion | shortest_suggestion | Why |
|---|---|---|---|
runn | running shoes | running shoes | runn starts the first word of the top suggestion. The two running ... suggestions tie on being matches; running shoes is shortest. |
trail | trail | trail running shoes | The top suggestion has no word starting with trail, so top mode keeps the typed text. Shortest mode finds the match in the third suggestion. |
Response
The response has the same shape as a /search response, with these differences:
| Field | Type | Description |
|---|---|---|
suggestions | array | The typeahead suggestions for the typed text, most popular first. Each item has a suggestion string. Items carry a namespace when the suggestion was indexed under one, and a metadata object when metadata is enabled for your index. |
resolvedQuery | string | The query the product panel was actually searched for. Equal to q unless a suggestion overrode it. |
query | string | The query as searched, after synonym substitution. Usually the same as resolvedQuery. |
facets | object | Always {}. Facets are never computed on this endpoint. |
hits, limit, offset, totalHits, processingTimeMs | As on /search. totalHits is not tracked exactly by default on this endpoint, so treat it as an approximation. |
Response headers
| Header | Description |
|---|---|
x-marqo-cache-hit | true or false. Whether the product panel search was served from cache. |
x-marqo-cache-hit-count | Number of the two upstream calls (suggestions and product panel) that were cache hits. |
x-marqo-cache-miss-count | Number of the two upstream calls that were cache misses. A suggestions call that failed counts as neither. |
x-marqo-results-personalized | Present when the request carried a userId. true when the product panel was personalized, otherwise false. |
Behaviour notes
- No redirects. Unlike
/search, this endpoint never returns a redirect. The shopper has not submitted a query yet. Redirects fire when the query is submitted to/search. - Same product ranking as the results page. The product panel runs through the same pipeline as
/search: synonyms, the selected search profile, query overrides, merchandising rules and personalization all apply. A panel and a results page for the same query show the same products. - Suggestions never block the panel. If the suggestions call fails for a transient reason, the response still carries the product panel,
suggestionsis an empty list, and the typed text is searched. The one exception is an index that does not support typeahead, described under Errors. - The panel is not degraded in the same way. Only the suggestions call degrades. If the product panel search itself is rejected, the endpoint returns an error rather than an empty panel. See Errors.
- Synonyms apply to the resolved query. Synonym substitution runs on the query the panel searches, so
querymay differ fromresolvedQuerywhen a synonym matched.
Errors
Errors return {"error": "..."} with the status codes below.
| Status | Message | Cause |
|---|---|---|
400 | Parameter 'q' is required and must be a string | q is missing or not a string. |
400 | 'q' must not be empty | q is an empty string or only whitespace. |
400 | Unrecognized param(s): 'offset' | The body contains a field this endpoint does not accept. The message lists the offending fields. |
400 | instant-search is not enabled for this index. Contact your Marqo representative to enable it. | The request used top_suggestion or shortest_suggestion mode on an index that does not support typeahead. In typed_only mode the same index returns 200 with an empty suggestions list instead. |
| Search engine status | Search failed: <reason> | The product panel search was rejected, for example by an invalid filter. The status is the one the search engine returned. Unlike the suggestions call, a failure here fails the whole request. |
Out-of-range values for limit, suggestionLimit and an unlisted mode value also return 400 with a message describing the failed constraint.
The product panel runs the same validation as /search on the fields this endpoint forwards to it, which are q, limit, filter, attributesToRetrieve, userId and sessionId. Those /search validation errors apply here unchanged.
If you are not sure whether typeahead is available on your index, start with typed_only. It works everywhere, and the dropdown fills itself in wherever suggestions exist.
Best practices
- Debounce on the client. Wait 150 to 300 ms after the last keystroke before sending a request, and drop responses that arrive out of order.
- Keep the panel small. A
limitof 4 to 8 is enough for a typeahead panel. Every request runs on a keystroke, so cost scales with size. - Retrieve only what you render. Set
attributesToRetrieveto the title, price and image fields the panel displays. - Use a light search profile. Create a profile for typeahead that turns off highlights and keeps retrieval cheap, and pass it as
profileId. - Show
resolvedQueryto the shopper. When it differs from what they typed, a label such as "Showing results for running shoes" explains why the panel changed. - Submit to
/searchon enter. The results page needs facets, pagination, sorting and redirects, none of which this endpoint provides.