Search with Marqo
This guide shows you how to search your product catalog using Marqo's powerful ecommerce search API. Learn how to perform text search, composite search, search by image, sort and filter results, implement faceted search, and "search as you type".
Prerequisites
- A Marqo Cloud account (sign up here)
- Your Marqo index ID (used for the
x-marqo-index-idheader) - An existing ecommerce index with products (add products guide)
Search Requests
Search requests are made via a POST /search request with a search body containing your query and search parameters.
Example
- cURL
- JavaScript
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "wireless headphones",
"limit": 25
}'
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "wireless headphones",
limit: 25,
}),
});
Note: Replace {index_name} with your actual index name and {index_id} with the ID provided for your ecommerce deployment.
Response
{
"hits": [
{
"productTitle": "Premium Wireless Bluetooth Headphones",
"variantTitle": "Premium Wireless Bluetooth Headphones - Black",
"price": 199.99,
"variantImageUrl": "https://cdn.example.com/headphones-black.jpg",
"color": "Black",
"productCollections": ["electronics", "audio"],
"_id": "headphones-black-001",
"_score": 1.23456
},
{
"productTitle": "Wireless Earbuds Pro",
"variantTitle": "Wireless Earbuds Pro - White",
"price": 149.99,
"variantImageUrl": "https://cdn.example.com/earbuds-white.jpg",
"color": "White",
"productCollections": ["electronics", "audio"],
"_id": "earbuds-white-002",
"_score": 1.15432
}
],
"limit": 25,
"offset": 0,
"totalHits": 138,
"totalMatches": 138,
"processingTimeMs": 42,
"query": "wireless headphones",
"facets": {},
"availableOptionFacets": []
}
Response fields
| Field | Type | Description |
|---|---|---|
hits | Array | The matching products, each with its retrieved fields plus _id and _score. |
query | String or Object | The query that was searched. This is the query after synonym substitution and any query prefix configured for your index, so it can differ from the text the shopper typed. For a weighted query it echoes the object. |
limit, offset | Integer | The pagination values applied to the request. |
totalHits | Integer | The number of matching products reachable via pagination. Use this to drive the pagination control (for example, "page X of Y"). Capped at 10,000. When the request includes sortBy, this is the number of sort candidates rather than the full match count. |
totalMatches | Integer | The total number of products matching the search criteria, uncapped. Use this to display a headline count (for example, "12,457 results"). When totalMatches exceeds totalHits, only the first totalHits products can be paged through. |
processingTimeMs | Integer | Search engine processing time in milliseconds. |
facets | Object | Facet counts, keyed by field. Always present; an empty object when no facets were requested or configured. See Faceted Search. |
fieldLabels | Object | Display names for the facet fields in facets, keyed by field name, for example {"brand": "Brand"}. Present only when display names are configured for your index and at least one returned facet field has one. |
availableOptionFacets | Array of strings | The facet fields configured as product options for your index, for example ["size", "color"]. Always present; an empty array when none are configured. |
relevanceScore | Number | A 0 to 10 estimate of how relevant the result set is to the query. Present only when the request sets relevanceScore: true and relevance scoring is configured for your index. Not computed for image, wildcard or weighted queries, or when there are fewer than three hits. |
personalized | Boolean | Whether the results were personalized for the userId sent with the request. Present whenever a userId was sent. It is false when personalization did not apply, including when personalization is not enabled for your index. |
Response headers
| Header | Values | Description |
|---|---|---|
x-marqo-cache-hit | true or false | Whether the response was served from the results cache. |
x-marqo-results-personalized | true or false | Present whenever the request carried a userId. false means personalization did not apply to this request. |
For the full list of response headers and error responses, see Responses and errors.
Search redirects
If the query matches a redirect rule configured in the console, the search returns 200 with a redirect object instead of results. Send the shopper to redirect.url.
{
"redirect": {
"url": "https://shop.example.com/pages/returns",
"query": "returns policy",
"matchType": "CONTAINS"
}
}
| Field | Type | Description |
|---|---|---|
url | String | The absolute URL to send the shopper to. |
query | String | The normalized shopper query that matched the rule. |
matchType | String | Present only when a keyword rule matched a single word of the query, in which case it is CONTAINS. Whole-query matches omit it. |
A redirect response has no hits and carries none of the response headers listed above. Redirects are evaluated after synonym substitution, so a synonym can cause a query to match a rule. They are not evaluated for image queries or weighted queries. On /composite-search they are evaluated against originalQuery and never fire when it is omitted.
Search Parameters
Here is a full list of parameters available for /search:
| Parameter | Type | Default | Description |
|---|---|---|---|
q | String or Object | required | The search query. A string searches for that text; "*" matches every product (useful with filter and sortBy). A recognised image URL or a data:image/... URL searches by image; see Search by Image for which URLs qualify. An object of {"term": weight} pairs runs a weighted multi-term query, which is semantic only and may use negative weights; see Composite Search. Image queries and weighted queries skip synonyms, redirects, query-level rules, merchandising rules and dynamic facets. Must not be empty. |
limit | Integer | Index setting | Maximum number of products to return. The default is the search limit configured for your index (12 when none is configured). limit + offset must not exceed 10,000. |
offset | Integer | 0 | Number of products to skip (for pagination) |
filter | String | null | Filter string using Marqo's query DSL to narrow search results. Combined with any filter configured for your index using AND; wrap a filter whose top level is OR in parentheses. |
attributesToRetrieve | Array of strings | Index setting | Specific product fields to return. If not specified, returns the attribute list configured for your index. parentProductId is always included in the returned fields. |
facets | Object | Index setting | Facet configuration for aggregated results. Replaces the facets configured for your index. Mutually exclusive with useDynamicFacets; sending both returns 422. See Faceted Search. |
useDynamicFacets | Boolean | false | Opt in to merchandised, dynamically ordered facets configured in the console. Mutually exclusive with facets; sending both returns 422. When true, the facets configured for your index are not used, even if no merchandised facets resolve. Not applied to image or weighted queries. Requires the Dynamic Facets feature to be enabled on your account. See Dynamic Facets. |
sortBy | Object | Index setting | Sort configuration for results ordering. Merged over the sort configured for your index. See Sorting for the interactions with personalization and merchandising. |
collapseFields | Array | Index setting | Fields to group results by, so that only one hit per group is returned. Each entry is a field name string or an object {"name": "field", "sortBy": {...}} that chooses which variant represents the group. Replaces the collapse configured for your index; an empty array disables collapsing for this request. See Deduping Variants. |
profileId | String | null | (optional) The ID of a named configuration profile to use for the request. Profiles allow fine-tuned configurations tailored to specific use cases. If the specified profile does not exist, Marqo will fallback to the default configuration. Please contact your Marqo representative to set up profiles for your requirements. Example: summer-sale-2026 |
userId | String | null | User ID for personalization. When personalization is configured for your index, results are reranked toward the shopper's recent activity; a request with sortBy is never personalized. See Personalization. |
sessionId | String | null | Session identifier. Sent alongside userId it refines personalization, and it is used to keep a shopper in the same variant of a merchandising experiment. On its own it does not personalize results. |
geoLocation | Object | null | (optional) Geographic context for the request, e.g. { "country": "US", "region": "Georgia" }. country is required when geoLocation is supplied; region is optional. Used to tailor ranking by location when geo-aware ranking is configured for your index. Please contact your Marqo representative to enable this. See Geo-Aware Ranking. |
sizeAffinity | Array of strings or Object | null | Sizes the shopper prefers, either as a list with equal weight (["S", "M"]) or as a map of size to weight ({"S": 1.0, "M": 0.5}). Boosts products available in those sizes when size-aware ranking is configured for your index; otherwise it has no effect. List entries must be non-empty strings and weights must be finite numbers, or the request returns 400. |
language | String | Index setting | ISO 639-1 language code used for text analysis of the query, for example de. Supported codes: ar, ca, da, de, el, en, es, fi, fr, ga, hu, id, it, nb, nl, pt, ro, ru, sv, tr. Unsupported codes are ignored and the language is detected automatically. |
relevanceScore | Boolean | false | Set to true to include a relevanceScore field in the response. Requires relevance scoring to be configured for your index; otherwise the field is omitted. |
disableMerchandising | Boolean | false | Skip all merchandising rules for this request. Must be enabled for your index; otherwise the flag is ignored. Non-boolean values return 422. It suppresses merchandising rules only: useDynamicFacets, redirects, synonyms and query-level rules are unaffected. |
Query string parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
flattenMaps | Boolean | false | By default, map fields are returned as nested objects ({"specs": {"weight": 1}}). Set ?flattenMaps=true on the request URL to receive dotted keys instead ({"specs.weight": 1}). |
Validation errors
Validation failures return a JSON body of the form {"error": "..."}. The messages specific to /search are:
| Status | Error |
|---|---|
400 | 'q' required in body |
400 | 'q' must not be empty |
400 | 'q' cannot be an empty object for multi-term queries |
400 | 'q' must be an object with only numeric values for multi-term queries |
400 | 'collectionName' not allowed in body for search requests |
400 | 'limit' + 'offset' must not exceed 10000 |
400 | 'geoLocation' must be an object, 'geoLocation.country' must be a string, 'geoLocation.country' must not be empty, 'geoLocation.region' must be a string, 'geoLocation.region' must not be empty |
400 | 'sizeAffinity' must be a list of sizes or an object mapping sizes to weights, 'sizeAffinity' list entries must be non-empty strings, 'sizeAffinity' weights must be finite numbers |
400 | sortBy supports a maximum of 3 fields, but N were provided (only for "q": "*" searches that use lexical retrieval) |
422 | 'useDynamicFacets' and 'facets' cannot both be set |
422 | 'disableMerchandising' must be a boolean |
Errors shared by all endpoints, such as authentication failures, are listed in Responses and errors.
:::warning Known limitation
A filter that is not a string, or an attributesToRetrieve that is not an array, is not type-checked by the API. These requests currently fail with 500 {"error": "Internal server error"} instead of a 400. Send the documented types to avoid it.
:::
Behaviour notes
- Hits never include internal fields. Fields whose names start with
_merch_,_pixel_or_mq_are removed, along with the internal scoring fields_price_norm_steep,_top_queries_rank_score,_top_query_modifiers_last_updated_at,_pre_rerank_score,_tensor_scoreand_lexical_score._id,_score,_highlightsand_foundare always kept, even when your index restricts which fields storefront callers receive. _highlightsis not returned unless highlighting is enabled for your index.- The response
queryis the query after synonym substitution and any query prefix configured for your index, not the text the shopper typed. A query-level rule can also replace the query, in which casequeryreports the replacement. Redirects are matched against the same substituted query. - The request
filteris combined with the filter configured for your index as<index filter> AND <request filter>. Wrap a request filter whose top level isORin parentheses so it is grouped as intended. totalHitsreports the number of sort candidates instead of the full match count when the request includessortBy.limitandoffsetmust be positive integers. Other values are not validated by the API and are rejected by the search engine asSearch failed: ....popularitySettingscannot be set per request. Ordering facet values bypopularityalways uses the settings configured for your index.- Unknown body fields are ignored rather than rejected.
Query-level rules
Merchandisers can attach a rule to a specific shopper query in the console. When a request's query matches a rule, the rule can:
- Replace the query with a representative one, so that related phrasings retrieve the same results. The response
queryreports the replacement rather than the text the shopper typed. - Supply a search configuration for the request. A query-level configuration takes precedence over
profileId: when a rule matches and carries a configuration, the named profile is not applied.
Query-level rules must be enabled for your index. Contact your Marqo representative to use them. They are not applied to image queries or weighted queries.
Filtering
You can use Marqo's filter string DSL to refine search results. Filter strings can be used with all search methods and use a syntax based on Lucene with some differences. For complete filter syntax and examples, see our Filter DSL Guide.
Key Filter Features
- Fielded searches - All term filters must be connected to a field, e.g.,
brand:Nike - Range filters - Efficient range filters for numeric types, e.g.,
price:[50 TO 300] - Boolean operators - Support for AND, OR, NOT with proper grouping
- Array filtering - Filter over array fields like collections or tags
Examples
- cURL
- JavaScript
# Price range and availability filters
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "headphones",
"filter": "price:[50 TO 300] AND is_available:true"
}'
# Logical operators with grouping
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "shoes",
"filter": "(brand:Nike AND collections:athletic) OR (brand:Adidas AND price:[* TO 150])"
}'
# Array field filtering
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "electronics",
"filter": "collections:audio OR collections:wireless"
}'
# Boolean and exact match
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "*",
"filter": "is_featured:true AND brand:(Apple OR Samsung)"
}'
// Price range and availability filters
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "headphones",
filter: "price:[50 TO 300] AND is_available:true",
}),
});
// Logical operators with grouping
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "shoes",
filter:
"(brand:Nike AND collections:athletic) OR (brand:Adidas AND price:[* TO 150])",
}),
});
// Array field filtering
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "electronics",
filter: "collections:audio OR collections:wireless",
}),
});
// Boolean and exact match
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "*",
filter: "is_featured:true AND brand:(Apple OR Samsung)",
}),
});
Common Filter Patterns
| Filter Type | Example | Description |
|---|---|---|
| Price Range | price:[50 TO 300] | Products between $50-$300 |
| Open Range | price:[100 TO *] | Products $100 and above |
| Exact Match | brand:Nike | Products with exact brand match |
| Boolean | is_available:true | Products that are available |
| Array Contains | collections:electronics | Products in electronics collection |
| Logical AND | brand:Nike AND price:[* TO 200] | Nike products under $200 |
| Logical OR | brand:(Nike OR Adidas) | Products from either brand |
| Complex Expression | (brand:Nike AND collections:athletic) OR (price:[* TO 50] AND is_sale:true) | Nike athletic items OR sale items under $50 |
| NOT Operator | collections:electronics AND NOT brand:Apple | Electronics excluding Apple products |
Faceted Search
Facets allow you to aggregate data from your documents based on specific fields. This can be useful for creating filters, showing data distributions, or implementing drill-down search functionality.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
fields | Object | null | Fields to facet on and their configuration |
maxResults | Integer | 100 | Maximum facet results per field (max: 10000) |
maxDepth | Integer | null | Max documents to consider for aggregation |
order | String | "desc" | Order of facet results: asc or desc |
useAutomaticExcludeTerms | Boolean | false | Derive each facet field's exclusions from the request filter. Cannot be combined with excludeTerms |
sortBy | String | null | Ordering of facet values for every field: count, alphanumeric or popularity. popularity orders values by shopper engagement using settings configured for your index |
A facets object in the request replaces the facets configured for your index for that request. To use the merchandised facets configured in the console instead, send useDynamicFacets: true and omit facets.
Field Parameters
| Parameter | Type | Description |
|---|---|---|
type | String | Facet type: string, number, or array |
sortBy | String | Ordering of this field's values: count, alphanumeric or popularity. Overrides the top-level sortBy |
maxResults | Integer | Maximum values for this field. Overrides the top-level maxResults |
order | String | asc or desc for this field. Overrides the top-level order |
ranges | Array | For numeric fields: define value ranges |
excludeTerms | Array | Remove specific filter terms while calculating this facet |
Range Parameters (Numeric Fields)
| Parameter | Type | Default | Description |
|---|---|---|---|
from | Number | null | Lower bound (inclusive), -Inf if not specified |
to | Number | null | Upper bound (exclusive), Inf if not specified |
name | String | null | Custom range name (defaults to "from:to") |
Excluding Filter Terms
By default, every term in the request's filter restricts every facet calculation. If a shopper selects color:Black and product_type:Shoes, the color facet is restricted to black and the product_type facet is restricted to shoes.
Use excludeTerms to remove each field's own selected term from that field's facet calculation. This changes the facet values and counts without changing the returned products:
{
"q": "casual wear",
"filter": "color:Black AND product_type:Shoes",
"facets": {
"fields": {
"color": {
"type": "string",
"excludeTerms": ["color:Black"]
},
"product_type": {
"type": "string",
"excludeTerms": ["product_type:Shoes"]
}
}
}
}
The returned products still match both filter terms. The color facet ignores color:Black but keeps product_type:Shoes, so it shows the colors available for shoes. The product_type facet ignores product_type:Shoes but keeps color:Black, so it shows the product types available in black.
Automatically Deriving Exclusions
For filters that change with each shopper interaction, set useAutomaticExcludeTerms to true and remove all field-level excludeTerms:
{
"q": "casual wear",
"filter": "color:Black AND product_type:Shoes",
"facets": {
"useAutomaticExcludeTerms": true,
"fields": {
"color": {"type": "string"},
"product_type": {"type": "string"}
}
}
}
This request produces the same exclusions as the manual example. If the shopper changes color:Black to color:White, update only the filter; Marqo derives the new color:White exclusion automatically.
Automatic mode derives equality and range terms. It does not derive IN or CONTAINS terms. It cannot be combined with field-level excludeTerms. Combining both modes returns HTTP 422.
Examples
- cURL
- JavaScript
# Price ranges and category facets
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "electronics",
"facets": {
"fields": {
"price": {
"type": "number",
"ranges": [
{"to": 50},
{"from": 50, "to": 100},
{"from": 100, "to": 200},
{"from": 200, "name": "$200+"}
]
},
"brand": {"type": "string"},
"collections": {"type": "array"}
}
}
}'
# Simple string facet
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "shoes",
"facets": {
"fields": {
"brand": {"type": "string", "maxResults": 10}
}
}
}'
// Price ranges and category facets
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "electronics",
facets: {
fields: {
price: {
type: "number",
ranges: [
{ to: 50 },
{ from: 50, to: 100 },
{ from: 100, to: 200 },
{ from: 200, name: "$200+" },
],
},
brand: { type: "string" },
collections: { type: "array" },
},
},
}),
});
// Simple string facet
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "shoes",
facets: {
fields: {
brand: { type: "string", maxResults: 10 },
},
},
}),
});
Response
{
"hits": [...],
"totalHits": 10000,
"totalMatches": 43210,
"facets": {
"price": {
"0.0:50.0": {
"count": 15,
"min": 19.99,
"max": 49.99,
"avg": 34.50,
"sum": 517.50
},
"$200+": {
"count": 8,
"min": 200.00,
"max": 599.99,
"avg": 325.00
}
},
"brand": {
"Nike": {"count": 25},
"Adidas": {"count": 18},
"Apple": {"count": 12}
},
"collections": {
"electronics": {"count": 120},
"audio": {"count": 45}
}
}
}
Sorting
sortBy allows you to sort the search results based on specific numerical fields. This can be useful for ordering results by price, rating, or any other numerical attribute.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
fields | Array of objects | No default | A list of fields to be sorted on, with sort order and missing policy |
sortDepth | Integer | null | The number of documents to be sorted in the global phase |
minSortCandidates | Integer | null | The minimum number of documents to be sorted on |
Field Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
fieldName | String | No default | The field to be sorted on |
order | String | "desc" | The sort order: "desc" or "asc" |
missing | String | "last" | Missing value policy: "first" or "last" |
How sorting interacts with other features
- A request
sortByis merged over the sort configured for your index, with the request taking precedence. - Entries without a
fieldNameare dropped. If no entries remain, the request is treated as unsorted. - A sorted request is never personalized, even when a
userIdis sent. - While a sort is applied, merchandising rules that reorder results (pins, boosts and recency) are suspended so the requested order is preserved.
totalHitsreports the number of sort candidates rather than the full match count.- For
"q": "*"searches that use lexical retrieval, at most three sort fields are accepted; more return400.
Examples
- cURL
- JavaScript
# Sort by price descending
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "shoes",
"sortBy": {
"fields": [{"fieldName": "price"}]
}
}'
# Multi-field sort with missing value handling
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "electronics",
"sortBy": {
"fields": [
{"fieldName": "rating", "order": "desc", "missing": "last"},
{"fieldName": "price", "order": "asc", "missing": "first"}
]
}
}'
# Performance optimization with minSortCandidates
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "laptops",
"sortBy": {
"fields": [{"fieldName": "price", "order": "desc"}],
"minSortCandidates": 1000,
"sortDepth": 60
}
}'
// Sort by price descending
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "shoes",
sortBy: {
fields: [{ fieldName: "price" }],
},
}),
});
// Multi-field sort with missing value handling
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "electronics",
sortBy: {
fields: [
{ fieldName: "rating", order: "desc", missing: "last" },
{ fieldName: "price", order: "asc", missing: "first" },
],
},
}),
});
// Performance optimization with minSortCandidates
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "laptops",
sortBy: {
fields: [{ fieldName: "price", order: "desc" }],
minSortCandidates: 1000,
sortDepth: 60,
},
}),
});
Pagination
Handle large result sets with pagination using limit and offset parameters.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | Integer | Index setting | Maximum number of documents to return. Defaults to the search limit configured for your index (12 when none is configured) |
offset | Integer | 0 | Number of documents to skip |
The result window is capped at 10,000 documents: a request where limit + offset exceeds 10,000 returns 400 with the error 'limit' + 'offset' must not exceed 10000. Use totalHits to work out how many pages can be requested.
Examples
- cURL
- JavaScript
# Page 1 (first 20 results)
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "laptops",
"limit": 20,
"offset": 0
}'
# Page 2 (results 21-40)
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "laptops",
"limit": 20,
"offset": 20
}'
// Page 1 (first 20 results)
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "laptops",
limit: 20,
offset: 0,
}),
});
// Page 2 (results 21-40)
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "laptops",
limit: 20,
offset: 20,
}),
});
Search as You Type
Search as you type provides real-time search results as users input queries, creating an instant and responsive search experience.
The dedicated Instant Search endpoint returns query suggestions and a result panel in a single request and never computes facets. Prefer it for new integrations. The approach below uses the standard search endpoint.
Implementation
Use the standard search endpoint with these considerations for optimal real-time performance:
- Lower limits: Use smaller
limitvalues (5-10) for faster response times - Minimal attributes: Only retrieve essential fields using
attributesToRetrieve - No facets: Facets are not necessary for search as you type. If facets are configured for your index, override them with an empty
fieldsobject so none are computed - Client-side debouncing: Implement 200-300ms delays between requests
- Minimum query length: Consider requiring 2-3 characters before triggering search
Example
- cURL
- JavaScript
# Real-time search with optimized parameters
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "iph",
"limit": 5,
"attributesToRetrieve": ["productTitle", "variantTitle", "price", "variantImageUrl"],
"facets": {"fields": {}}
}'
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "iph",
limit: 5,
attributesToRetrieve: [
"productTitle",
"variantTitle",
"price",
"variantImageUrl",
],
facets: { fields: {} },
}),
});
Best Practices
- Debouncing: Implement client-side delays to prevent excessive API calls
- Progressive loading: Start with minimal results and allow expansion
- Caching: Cache recent results to improve perceived performance
- Error handling: Gracefully handle timeouts and network issues
- Accessibility: Ensure screen readers can navigate real-time results
Search by Image
Search by image returns products that look similar to an image you provide. It uses the same /search endpoint, with the image as q.
Image requirements
A query is treated as an image query when it is either:
- a data URL beginning
data:image/that carries a base64-encoded image, or - a single
httporhttpsURL, containing no whitespace, whose path ends in.jpg,.jpeg,.png,.webp,.tiffor.avif. The extension is matched case-insensitively and the query string is ignored.
A URL that carries the image format in a query parameter rather than the path, such as https://cdn.example.com/img/123?format=jpeg, does not meet the second rule. That request is not handled as an image query and no error is returned: synonyms, redirects, query-level rules and merchandising rules are applied to it as they would be to a text query. Use a URL whose path ends in one of the extensions above, or a data URL, when you want image search.
Image queries run as semantic (tensor) searches. Synonyms, redirects, query-level rules, merchandising rules such as pins, boosts and exclusions, dynamic facets and relevanceScore are not applied to them.
Example
With URL to an image:
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "https://example.com/image.jpg"
}'
With data URL of a base64-encoded image:
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD//gAMQX..."
}'
Composite Search
Composite search enables multi-term queries where each search term can have a different weight. The same weighted form can be sent to /search as an object q.
A weighted query is semantic only. Retrieval and ranking both use the vector index, keyword matching does not contribute to the result, and any keyword score modifiers configured for your index are not applied. Weights may be negative, which pushes results away from that term.
Example
- cURL
- JavaScript
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/composite-search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"weightedQuery": {
"wireless headphones": 1.0,
"high quality": 0.5
},
"originalQuery": "wireless headphones",
"limit": 20
}'
fetch(
"https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/composite-search",
{
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
weightedQuery: {
"wireless headphones": 1.0,
"high quality": 0.5,
},
originalQuery: "wireless headphones",
limit: 20,
}),
}
);
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
weightedQuery | Object | required | Multi-term query as key-value pairs where keys are search terms and values are weights. Must be a non-empty object with numeric values |
originalQuery | String | null | The original user query string. Include this if the user entered a single string that was transformed into the weightedQuery. It drives synonym substitution, redirects, merchandising rules and the response query. Must be a non-empty string when provided |
limit | Integer | Index setting | Maximum number of products to return. Defaults to the search limit configured for your index (12 when none is configured). limit + offset must not exceed 10,000 |
offset | Integer | 0 | Number of products to skip (for pagination) |
filter | String | null | Filter string using Marqo's query DSL. Combined with the filter configured for your index as described in Behaviour notes |
sortBy | Object | Index setting | Sort configuration for results ordering. See Sorting |
attributesToRetrieve | Array of strings | Index setting | Specific product fields to return. Defaults to the attribute list configured for your index; parentProductId is always included |
facets | Object | Index setting | Facet configuration for aggregated results. See Faceted Search. useDynamicFacets is accepted on this endpoint but does not attach merchandised facets; it only suppresses the facets configured for your index |
collapseFields | Array | Index setting | Fields to group results by. See the /search parameter table |
profileId | String | null | (optional) The ID of a named configuration profile to use for the request. On this endpoint it selects the search profile only: profile-scoped merchandising rules and merchandised facets are not applied. If the specified profile does not exist, Marqo will fallback to the default configuration. Please contact your Marqo representative to set up profiles for your requirements. Example: summer-sale-2026 |
userId | String | null | User ID. Forwarded with the request but results are not personalized on this endpoint |
sessionId | String | null | Session ID. Forwarded with the request but results are not personalized on this endpoint |
geoLocation | Object | null | Geographic context, e.g. { "country": "US", "region": "Georgia" }. See Geo-Aware Ranking |
sizeAffinity | Array of strings or Object | null | Preferred sizes. See the /search parameter table |
language | String | Index setting | ISO 639-1 language code for query analysis. See the /search parameter table |
relevanceScore | Boolean | false | Include a relevanceScore in the response. Requires originalQuery and relevance scoring configured for your index |
disableMerchandising | Boolean | false | Skip all merchandising rules for this request. Must be enabled for your index; otherwise ignored. Non-boolean values return 422 |
Validation errors specific to this endpoint:
| Status | Error |
|---|---|
400 | 'weightedQuery' required in body |
400 | 'weightedQuery' must be a multi-term query object |
400 | 'weightedQuery' cannot be an empty object |
400 | 'weightedQuery' must be an object with only numeric values |
400 | 'originalQuery' must be a non-empty string if provided |
The limit, geoLocation, sizeAffinity, useDynamicFacets and disableMerchandising errors listed under Validation errors also apply.
Without originalQuery the response has no query field, redirects are never returned, and merchandising rules are not applied.
Response
{
"hits": [
{
"productTitle": "Premium Noise-Cancelling Wireless Headphones",
"variantTitle": "Premium Noise-Cancelling Wireless Headphones - Black",
"price": 249.99,
"variantImageUrl": "https://cdn.example.com/headphones-pro.jpg",
"productCollections": ["electronics", "audio", "wireless"],
"_id": "headphones-pro-001",
"_score": 1.85432
}
],
"limit": 20,
"offset": 0,
"totalHits": 57,
"totalMatches": 57,
"processingTimeMs": 38,
"query": "wireless headphones",
"facets": {},
"availableOptionFacets": []
}
The response has the same shape as /search (see Response fields). query echoes originalQuery after synonym substitution and is omitted when originalQuery was not sent. Redirect responses apply as described in Search redirects when originalQuery matches a rule.
Geo-Aware Ranking
When geo-aware ranking is configured for your index, pass a geoLocation object to tailor result ordering to the caller's location. Provide a country (required), and optionally a region, to boost products that perform well in that location. Requests without geoLocation are unaffected and continue to work unchanged.
geoLocation is supported on the /search, /collections and /composite-search endpoints. Invalid values return 400; see Validation errors for the messages.
Location codes
Both subfields follow ISO 3166-2, which is where the country codes and region (subdivision) names come from:
country— the two-letter ISO 3166-1 alpha-2 country code, e.g.US,GB,DE.region— the subdivision name as listed for that country in ISO 3166-2, e.g.Georgia.
Example
- cURL
- JavaScript
# Country and region
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "wireless headphones",
"geoLocation": { "country": "US", "region": "Georgia" }
}'
# Country only (region omitted)
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "wireless headphones",
"geoLocation": { "country": "US" }
}'
// Country and region
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "wireless headphones",
geoLocation: { country: "US", region: "Georgia" },
}),
});
// Country only (region omitted)
fetch("https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search", {
method: "POST",
headers: {
"x-marqo-index-id": "{index_id}",
"Content-Type": "application/json",
},
body: JSON.stringify({
q: "wireless headphones",
geoLocation: { country: "US" },
}),
});