Skip to main content

Collections with Marqo

This guide shows you how to retrieve collections from your product catalog using Marqo's API. Learn how to build collection pages with filtering, faceted search, sorting, and pagination to create curated browsing experiences for your customers.

Prerequisites

  • A Marqo Cloud account
  • Your Marqo index ID (used for the x-marqo-index-id header)
  • An existing ecommerce index with products (add products guide)

Collections Requests

Collections requests are made via a POST /indexes/{index_name}/collections request or via a GET /indexes/{index_name}/collections request. The latter may be preferred for SEO optimization on some sites, and requires first encoding the request body in base 64.

POST Endpoint: POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections

GET Endpoint: GET https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections

Both forms accept the same request body and return the same response. Both authenticate with the x-marqo-index-id header; an API key sent as Authorization: Bearer {api_key} is also accepted.

Without a q parameter the request is a browse: every product in the collection is returned in the order defined by your index's collections configuration. With a q parameter it becomes a search within the collection (see Search Within a Collection).

Example: POST

curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "shirts"
}'

Note: Replace {index_name} with your actual index name and {index_id} with your ecommerce index ID.

How collectionName maps to your products

Each product in your catalog carries a productCollections field: an array listing every collection the product belongs to. When you browse a collection, the collectionName you send is matched against that field. A product is returned only when its productCollections array contains a value equal to the collectionName in the request. In the response below, every hit reports its own productCollections, and the productCollections facet counts how many products carry each collection value.

By default the match uses the productCollections field. If a different field was configured as your collections field when the index was created, collectionName is matched against that field instead. The filter and facet examples on this page use productCollections; substitute your configured field name if it differs.

Browsing the whole index

The reserved value "all" browses the entire index with no collection filter applied. Every other parameter (filter, facets, sortBy, pagination, personalization) works as normal, so collectionName: "all" is the way to build an "all products" page. "all" is also a valid page trigger for merchandising rules, so rules can be authored against the all-products page.

{
"collectionName": "all",
"sortBy": { "fields": [{ "fieldName": "price", "order": "asc" }] }
}

Per-collection configuration

An index can carry a per-collection configuration, keyed by collection name, alongside its default collections configuration. When the collectionName in a request matches one, that configuration replaces the default for the request: its own filter is used in place of the generated productCollections:(name) filter, and its own search parameters (sort, facets, retrieved attributes, and so on) apply. Collections without a specific configuration use the default. Per-collection configuration is set up per index; contact your Marqo representative if you need it.

Example: Search Within a Collection

Use the q parameter to search within a collection. Results are ranked by relevance to the query. Sending q changes how the request is built:

  • The request uses your index's search configuration rather than its collections configuration, so ranking, score modifiers, retrieved attributes and default facets are the ones configured for /search.
  • The collection filter is still applied on top of that configuration.
  • profileId selects a search profile (the same profiles used by /search), not a collection profile.
  • Merchandising rules and dynamic facets (useDynamicFacets) apply exactly as they do on /search.
  • Collection personalization does not apply; userId and sessionId are accepted but the results are not personalized.

A q of "*" is treated the same as omitting q: the request is a browse.

q accepts the same forms as /search: a text string, a weighted {"term": weight} object, or an image URL or data URL. Merchandising rules are not applied to the weighted and image forms, which matches how /search treats them.

curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "shirts",
"q": "red cotton"
}'

Example: GET

Base64 encoding: To submit a request via the GET endpoint, pass the base64-encoded request body as the body query parameter, URL-encoded.

curl -X GET \
"https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections?body=eyJjb2xsZWN0aW9uTmFtZSI6InNoaXJ0cyJ9" \
-H "x-marqo-index-id: {index_id}"

Note: Replace {index_name} with your actual index name and {index_id} with your ecommerce index ID.

Example encoding:

  • JSON: {"collectionName":"shirts"}
  • Base64: eyJjb2xsZWN0aW9uTmFtZSI6InNoaXJ0cyJ9

The body query parameter is required on the GET endpoint. A GET request without it returns 400 with {"error": "Missing 'body' query parameter"}; a value that does not decode to valid JSON returns 400 with {"error": "Invalid base64 encoded body parameter"}.

Use standard base64 and URL-encode the result before putting it in the query string. Base64 output can contain +, which a query string reads as a space, and the request then fails with 400 and {"error": "Invalid base64 encoded body parameter"}. encodeURIComponent in JavaScript, or curl -G --data-urlencode "body=$BODY", handles this. Base64url, which uses - and _ in place of + and /, is not accepted.

warning

The GET form carries ASCII only. A collectionName or filter containing non-ASCII characters, such as accented letters, CJK text or emoji, is decoded incorrectly and the request either fails or matches nothing. Use the POST endpoint for those requests.

Response

The request above sends no facets, so the facets configured as defaults for your index are returned. The example below is from an index whose default facets are the standard Shopify product fields; your index will return whichever facet fields it has configured. Sending facets in the request replaces the configured defaults for that request.

{
"hits": [
{
"productCollections": ["shirts"],
"price": 300,
"_id": "red-shirt",
"productTitle": "Shirt",
"parentProductId": "parent-shirt",
"variantTitle": "Shirt - Red",
"variantImageUrl": "https://cdn.shopify.com/s/files/1/0705/8276/3687/files/red-tshirt.jpg?v=1754569578",
"color": "Red",
"_score": 0
},
{
"productCollections": ["shirts"],
"price": 200,
"_id": "blue-shirt",
"productTitle": "Shirt",
"parentProductId": "parent-shirt",
"variantTitle": "Shirt - Blue",
"variantImageUrl": "https://cdn.shopify.com/s/files/1/0705/8276/3687/files/blue-tshirt.jpg?v=1754569578",
"color": "Blue",
"_score": 0
},
{
"productCollections": ["shirts"],
"price": 100,
"_id": "light-blue-shirt",
"productTitle": "Shirt",
"parentProductId": "parent-shirt",
"variantTitle": "Shirt - Light Blue",
"variantImageUrl": "https://cdn.shopify.com/s/files/1/0705/8276/3687/files/light-blue-tshirt.jpg?v=1754569578",
"color": "Light Blue",
"_score": 0
}
],
"processingTimeMs": 31,
"query": "*",
"limit": 12,
"offset": 0,
"totalHits": 3,
"totalMatches": 3,
"facets": {
"variantOption2": {},
"variantOption3": {},
"variantWeight": {},
"variantPrice": {},
"productCollections": {
"shirts": {"count": 3}
},
"variantOption1": {},
"productType": {},
"productVendor": {},
"productCategory": {},
"productTags": {}
}
}

Response fields

FieldTypeDescription
hitsArrayThe matching products, each with the retrieved fields plus _id and _score.
processingTimeMsIntegerSearch engine processing time in milliseconds.
queryStringThe query used for the request. "*" for a browse. Omitted when the browse was personalized, and when the applied profile supplies its own tensor or lexical query, because the request then carries no query of its own.
limit, offsetIntegerThe pagination values applied to the request, after defaults.
totalHitsIntegerThe number of matching products reachable via pagination. Use this to drive the pagination control (for example, "page X of Y"). Capped at 10,000.
totalMatchesIntegerThe total number of products matching the request, 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.
facetsObjectFacet counts, keyed by field. See Faceted Search.
fieldLabelsObjectDisplay 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.
personalizedBooleanWhether the results were personalized for the userId sent with the request. Present only when personalization was attempted, which means an unsorted browse with a userId. Absent on a sorted browse and on a search within a collection, even when a userId was sent. See Personalization.
relevanceScoreNumberA 0 to 10 estimate of how relevant the result set is to q. Present only when the request sets relevanceScore: true, q is sent, and relevance scoring is configured for your index. Not computed when there are fewer than three hits.

totalHits and totalMatches describe how many products are in the collection after any query, filter, or faceting is applied.

Map fields in responses

The endpoint accepts one query string parameter, on both POST and GET:

ParameterTypeDefaultDescription
flattenMapsBooleanfalseBy 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}).

Collection Parameters

Here is a full list of parameters available for /collections:

ParameterTypeDefaultDescription
collectionNameStringnull (required)The collection to browse. This value is matched against the productCollections field on each product; a product appears only if its productCollections array contains this exact value. If a different collections field was configured when your index was created, it is matched against that field instead. The reserved value "all" browses the whole index with no collection filter.
qString or ObjectnullA query to search within the collection. When provided, results in the collection are ranked by relevance to the query and the request is built from your search configuration (see Search Within a Collection). "*" is treated as no query. Accepts the same forms as /search: a text string, a weighted {"term": weight} object, or an image URL or data URL. Works on both the POST and GET endpoints.
limitIntegerIndex settingMaximum number of products to return. On a browse the default is the limit configured in your index's collections settings; when q is sent it is the limit configured in your search settings. 12 when neither is configured. limit + offset must not exceed 10,000.
offsetInteger0Number of products to skip (for pagination)
filterStringnullFilter string using Marqo's query DSL to narrow search results. Combined with the collection filter and any filter configured for your index using AND.
attributesToRetrieveArray of stringsIndex settingSpecific product fields to return. If not specified, the attribute list configured for your index applies, and whole documents are returned when no list is configured. parentProductId is added to any list that omits it. _id and _score are always returned.
facetsObjectIndex settingFacet configuration for aggregated results. When omitted, the facets configured as defaults for your index are returned; when supplied, it replaces those defaults for the request. Mutually exclusive with useDynamicFacets; sending both returns 422.
useDynamicFacetsBooleanfalseOpt in to merchandised, dynamically ordered facets configured in the console. Mutually exclusive with facets; sending both returns 422. Requires the Dynamic Facets feature to be enabled on your account. See Dynamic Facets.
sortByObjectIndex settingSort configuration for results ordering. Merged over the sort configured for your index. On a browse, minSortCandidates defaults to 50000 when a sort is present. A sort disables personalization for the request. See Sorting.
collapseFieldsArrayIndex settingFields 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.
profileIdStringnullThe ID of a named configuration profile to use for the request. On a browse this selects a collection profile; when q is sent it selects a search profile. The same ID also selects the merchandising profile whose rules apply to the request. If the specified profile does not exist, the request falls back to the default configuration without an error. Please contact your Marqo representative to set up profiles for your requirements. Example: summer-sale-2026
userIdStringnullShopper identifier for personalization. Required for personalized collection results; see Personalization.
sessionIdStringnullSession identifier for personalization (has no effect unless userId is also set).
geoLocationObjectnullGeographic 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.
sizeAffinityArray of strings or ObjectnullSizes 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.
languageStringIndex settingISO 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.
relevanceScoreBooleanfalseSet to true to include a relevanceScore field in the response. Requires relevance scoring to be configured for your index; otherwise the field is omitted. Only computed when q is sent (a search within the collection), never on a browse.
disableMerchandisingBooleanfalseSkip all merchandising rules for this request. Must be enabled for your index; otherwise the flag is ignored. Non-boolean values return 422. Does not affect useDynamicFacets.

Unknown body fields are ignored. The ?flattenMaps=true query parameter is also accepted; see Map fields in responses.

Personalization

On a browse (no q), sending a userId personalizes the collection for that shopper when a pixel is linked to your index and personalization has not been disabled for it. The two products the shopper most recently interacted with are used as context, so products similar to those rank higher within the collection. Personalization needs at least two distinct products on record for the userId; when fewer are available, the request proceeds unpersonalized.

Personalization is skipped when the request applies a sort, because the sort determines the ordering. It does not apply when q is sent.

Whenever userId is sent, the x-marqo-results-personalized response header reports the outcome as true or false. The personalized body field is present only when personalization was attempted, so it appears on an unsorted browse and is absent on a sorted browse and on a search within a collection. Read the header rather than the body field if you need an answer on every request.

A personalized browse is never served from the cache, so x-marqo-cache-hit is false on one.

curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "shirts",
"userId": "shopper-123"
}'

See Personalization for how shopper behaviour is captured and used.

Errors

Errors are returned as {"error": "..."} with one of the following status codes:

StatusMessageWhen
400Invalid request bodyThe request body is not a JSON object, for example a string, number or null.
400'collectionName' required in bodyThe body has no collectionName.
400'collectionName' must not be emptycollectionName is not a string, or is empty or whitespace.
400'limit' + 'offset' must not exceed 10000The requested page lies beyond the 10,000-result window.
400Missing 'body' query parameterGET request without the body query parameter.
400Invalid base64 encoded body parameterGET request whose body does not decode to valid JSON.
422'useDynamicFacets' and 'facets' cannot both be setBoth useDynamicFacets: true and facets were sent.
422'disableMerchandising' must be a booleandisableMerchandising was sent with a non-boolean value.

When q is sent, the /search validation rules for q also apply (for example 'q' must not be empty).

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

# Price range and availability filters
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "audio-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}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "athletic-shoes",
"filter": "(brand:Nike AND productCollections:athletic) OR (brand:Adidas AND price:[* TO 150])"
}'

# Array field filtering
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "electronics",
"filter": "productCollections:audio OR productCollections:wireless"
}'

# Boolean and exact match
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "featured-electronics",
"filter": "is_featured:true AND brand:(Apple OR Samsung)"
}'

Common Filter Patterns

Filter TypeExampleDescription
Price Rangeprice:[50 TO 300]Products between $50-$300
Open Rangeprice:[100 TO *]Products $100 and above
Exact Matchbrand:NikeProducts with exact brand match
Booleanis_available:trueProducts that are available
Array ContainsproductCollections:electronicsProducts in electronics collection
Logical ANDbrand:Nike AND price:[* TO 200]Nike products under $200
Logical ORbrand:(Nike OR Adidas)Products from either brand
Complex Expression(brand:Nike AND productCollections:athletic) OR (price:[* TO 50] AND is_sale:true)Nike athletic items OR sale items under $50
NOT OperatorproductCollections:electronics AND NOT brand:AppleElectronics excluding Apple products

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

ParameterTypeDefaultDescription
fieldsObjectnullFields to facet on and their configuration
maxResultsInteger100Maximum facet results per field (max: 10000)
maxDepthIntegernullMax documents to consider for aggregation
sortByStringnullOrdering of facet values for every field: count, alphanumeric or popularity. popularity orders values by shopper engagement using settings configured for your index
orderString"desc"Order of facet results: asc or desc
useAutomaticExcludeTermsBooleanfalseDerive each facet field's exclusions from the request filter. Cannot be combined with excludeTerms

Field Parameters

ParameterTypeDescription
typeStringFacet type: string, number, or array
sortByStringOrdering of this field's values: count, alphanumeric or popularity. Overrides the top-level sortBy
maxResultsIntegerMaximum values for this field. Overrides the top-level maxResults
orderStringasc or desc for this field. Overrides the top-level order
rangesArrayFor numeric fields: define value ranges
excludeTermsArrayRemove specific filter terms while calculating this facet

See Excluding Filter Terms for manual and automatic behavior, constraints, and an example.

Range Parameters (Numeric Fields)

ParameterTypeDefaultDescription
fromNumbernullLower bound (inclusive), -Inf if not specified
toNumbernullUpper bound (exclusive), Inf if not specified
nameStringnullCustom range name (defaults to "from:to")

Examples

# Price ranges and category facets
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "electronics",
"facets": {
"fields": {
"price": {
"type": "number",
"ranges": [
{"to": 50},
{"from": 50, "to": 100},
{"from": 100, "to": 200},
{"from": 200, "name": "$200+"}
]
},
"brand": {"type": "string"},
"productCollections": {"type": "array"}
}
}
}'

# Simple string facet
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "shoes",
"facets": {
"fields": {
"brand": {"type": "string", "maxResults": 10}
}
}
}'

Response

{
"hits": [...],
"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}
},
"productCollections": {
"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

ParameterTypeDefaultDescription
fieldsArray of objectsNo defaultA list of fields to be sorted on, with sort order and missing policy
sortDepthIntegernullThe number of documents to be sorted in the global phase
minSortCandidatesInteger50000The minimum number of documents to be sorted on. Applied whenever a sort is present on a browse; set it explicitly to override

Field Parameters

ParameterTypeDefaultDescription
fieldNameStringNo defaultThe field to be sorted on
orderString"desc"The sort order: "desc" or "asc"
missingString"last"Missing value policy: "first" or "last"

Entries without a fieldName are dropped without an error. If that leaves no entries, the request runs unsorted.

Examples

# Sort by price descending
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "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}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "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}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "laptops",
"sortBy": {
"fields": [{"fieldName": "price", "order": "desc"}],
"minSortCandidates": 1000,
"sortDepth": 60
}
}'

Pagination

Handle large result sets with pagination using limit and offset parameters.

Parameters

ParameterTypeDefaultDescription
limitIntegerIndex settingMaximum number of documents to return. On a browse this is the limit configured in your collections settings, and when q is sent it is the limit configured in your search settings; 12 when neither is configured
offsetInteger0Number of documents to skip

limit + offset may not exceed 10,000; larger windows are rejected with 400.

Examples

# Page 1 (first 20 results)
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "laptops",
"limit": 20,
"offset": 0
}'

# Page 2 (results 21-40)
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "laptops",
"limit": 20,
"offset": 20
}'

Geo-Aware Ranking

When geo-aware ranking is configured for your index, pass a geoLocation object to tailor the ordering of collection results 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.

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

# Country and region
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "shirts",
"geoLocation": { "country": "US", "region": "Georgia" }
}'

# Country only (region omitted)
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/collections \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "shirts",
"geoLocation": { "country": "US" }
}'