How to work with the APIs for storefront search.
APIs in this topic
| Resource | Purpose | Reference |
|---|---|---|
| Product Search | Create storefront discovery experiences where customers can browse and search across your Products. | Product Search |
Working with the APIs
Product Search
Classic Projects, Product Search works primarily with Product Projections rather than with the entire, complex Product resource. In Modular Projects, Variant-level data additionally requires a separate Variant Projections query.Classic. Projects with the ProductCatalogModel set to Modular and InMigration are supported in BETA.view_products:{projectKey} in the token scope. Don't rely on implied permissions when defining the token scope.Activate the Product Search API
The Product Search API is not active for the Project by default. Before first use, you have to activate it by one of the following options:
- using the Merchant Center by navigating to Settings > Project settings > Storefront Search.
- using the Change Product Search Indexing Enabled update action with
mode: ProductsSearchon the Update Project endpoint.
Automatic deactivation
You can reactivate the Product Search API as described above.
Data integration
Compare product retrieval methods
You can use the following methods for retrieving product data, each suited to different use cases.
availability of a ProductVariant in a Product Projection is eventually consistent and may not reflect immediate stock.
Instead, use the Inventory API.Modular ProductCatalogModel. For more information, see Variant Projections.current and staged representations.
Use this method only for product data management in backoffice applications.Product indexing
| Functionality | Product Search |
|---|---|
| Indexed projection of the Products | current |
| Support for Variant-level Attributes | variants.attributes.<attribute-name> expression, up to 50 Attributes per Product Variant |
| Support for Product-level Attributes BETA | attributes.<attribute-name> expression, up to 50 Attributes per Product Variant |
| Support for Standalone Prices | for sorting, filtering, and faceting, up to 10 000 Standalone Prices per Product |
| Support for validity periods of Prices | variants.prices.* keyword fields |
| Support for Stores | stores keyword field, up to 15 000 Stores per Product |
| Support for Product Selections | productSelections keyword field, up to 15 000 Product Selections per Product |
| Support for Product Tailoring | with GraphQL |
Attributes
isSearchable field is set as true.
Only Attributes that are present in the Product or Product Variant are indexed.
If a Product Type defines an Attribute but the corresponding Product Variant does not have a value for this Attribute, then this Attribute is not indexed.Large number of Attributes
50.
The limit applies to both Product Attributes and Variant Attributes separately.
For example, if a Product Variant has 42 Variant Attributes and the respective Product has additional 15 Product Attributes, then all 57 Attributes are indexed.50 Attributes available for Product Search.Indexing strategy
For each Product Variant, the indexer:
- Counts the number of Attributes used on the Product Variant in the order they are defined on the corresponding Product Type.
- Skips any Attribute that is not present on the Product Variant.
- Skips any Attribute that is present but not marked as searchable.
- Indexes any Attribute that is present and marked as searchable.
- Continues until the limit of
50Attributes is reached.
Example
attr_1, ..., attr_100) defined and all of them are marked as searchable.-
Given a Product Variant that has all Attributes of this Product Type in use, Product Search would index the first 50 Attributes (from
attr_1toattr_50). The remaining Attributes (attr_51toattr_100) would not be indexed due to the limit. -
Let's say there is another Product Variant that uses 60 Attributes from the example Product Type (
attr_21toattr_80). Product Search would skip the first 20 Attributes since they are not present on the Product Variant, and would start fromattr_21this time. The indexer would then finish withattr_70because the limit has been reached leaving outattr_71toattr_80.
Product Attributes BETA
50.
Overall, Product Search can index up to (50 + 50) Attributes per Product Variant.Prices
priceMode determines which Prices are indexed in the variants.prices field.For the
Embedded ProductPriceMode, the API indexes Embedded Prices; for Standalone, the API indexes the Standalone Prices associated with the Product Variant.
The Modular ProductCatalogModel supports only Standalone Prices.variants.prices field for sorting, filtering, and faceting on Products, but its content is not retrievable through the Product Search API.
Standalone Prices cannot be retrieved through the Product Search API.
To retrieve Price data, use the GraphQL API or the Product Projection API for Embedded Prices. In Projects with the ProductCatalogModel set to Modular, use the Variant Projections API.10 000 in Classic Projects.
In Projects with the ProductCatalogModel set to Modular, indexed Variants and indexed Standalone Prices of a Product share this same 10 000 limit. The indexer prioritizes Variants, indexing as many Variants of the Product as possible first, then uses any remaining capacity within the limit to index Standalone Prices.
For Products with Standalone Prices that exceed the available limit, the system indexes a selection of the Prices as explained in the Large number of Standalone Prices section.currency, country, customerGroup, and distributionChannel) is actually indexed. Like other attributes, this process operates on an eventually consistent basis, meaning that invalid prices might still be present in the index immediately after an update. When trying later, the price will be updated.In case of multiple prices for the same scope, the one with the narrower (valid) time range is always selected over the price without time ranges.
Large number of Standalone Prices
Classic Projects, the search index can hold up to 10 000 Standalone Prices per Product.
In Modular Projects, this same limit is shared with indexed Variants, as described in Prices; The indexer fills this shared limit with Variants first, then indexes Standalone Prices using whatever capacity remains.
For example, a Product has 9500 Variants and 1200 Standalone Prices. Since Variants are indexed first, they consume 9500 of the 10,000 limit, leaving only 500 slots for Standalone Prices. As a result, only 500 of the 1200 Standalone Prices can be indexed.If a Product has more Standalone Prices than the remaining capacity allows, the API applies a sorting algorithm when indexing Product Prices to ensure a fair distribution of indexed Prices across all Product Variants.
For each Product in the Project:
- Calculate the per-Variant Price limit. To do this, divide the available Standalone Prices capacity (
10 000inClassicProjects, or the remaining capacity after Variants are indexed inModularProjects) by the number of Variants on the Product. This gives the maximum number of Standalone Prices indexed per Variant. - fetch Prices for all Variants.
- for each Variant:
-
sort Prices by
- currency (alphabetically by the
value.currencyCode) - country (alphabetically, missing field is sorted as first)
- channel (alphabetically by
key, missing field is sorted as first) - Customer Group (alphabetically by
key, missing field is sorted as first)
- currency (alphabetically by the
-
truncate Prices that are beyond the Variant limit.
Examples
-
Example 1: A Product has 4 Variants and 1000 Standalone Prices. Each Variant is allowed a maximum of 250 (1000 / 4) indexed Standalone Prices. If a Variant has 300 Standalone Prices, they are sorted by currency, then country, then channel, then Customer Group. Only the first 250 (in that sorted order) are indexed; the remaining 50 are discarded.
-
Example 2: A Product has 10,500 Variants, which exceeds the available Standalone Prices capacity of 10,000. Since Variants are indexed first, no capacity remains for Standalone Prices, so the per-Variant Price limit is 0. As a result, none of the Product's Standalone Prices are indexed.
Prices with validity dates
The Price with the most exact data and price scope is indexed for a Product Variant. This means that a Price with a valid time range is given priority over a Price without a validity period. Prices that are not valid at the time of indexing are not considered for indexing. Product Search does not automatically update the search index when a Price reaches its validity date or becomes invalid.
Stores
15 000 Stores per Product.
If a Product belongs to more than 15 000 Stores, the API will return non-deterministic results for expressions on the stores keyword field.Product Selections
15 000 Product Selections per Product.
If a Product belongs to more than 15 000 Product Selections, the API will return non-deterministic results for expressions on the productSelections keyword field.Localized content
Language settings
languages are set on the Project, the analyzers for the following languages are used by default: en, en-GB, en-CA, en-US, en-AU, de, de-DE, es, se, fi, da, fr, and fr-FR.en and de, and a Product has localized name fields, one for name.ro and one for name.de.
Since the Romanian language is not part of the Project language settings, the name.ro field is not searchable, only the name.de field is in this case.Supported languages
| subtag | language |
|---|---|
ar | Arabic |
ca | Catalan |
cs | Czech |
da | Danish |
de | German |
el | Greek |
en | English |
es | Spanish |
eu | Basque |
fa | Persian |
fi | Finnish |
fr | French |
hi | Hindi |
hu | Hungarian |
hy | Armenian |
id | Indonesian |
it | Italian |
ko | Korean |
lv | Latvian |
nl | Dutch |
no | Norwegian |
pl | Polish |
pt | Portuguese |
ro | Romanian |
ru | Russian |
sk | Slovak |
sv | Swedish |
th | Thai |
tr | Turkish |
zh | Chinese |
Index updates
Our search infrastructure uses an event-driven indexing process to ensure product and price information remains current. The indexing method depends on the type of operation on the product catalog:
Incremental indexing
The following changes will cause an incremental indexing:
- A creation, update, or deletion of a Product, a Product Variant or a Price (Embedded or Standalone).
- An activation or deactivation of a Product Discount leading to changes on Prices.
- An addition of Products or Product Variants to a Product Selection (same for removal).
On average, an incremental index update is performed within a few minutes. Depending on the amount of Products to be updated, for example in a batched process, the actual delay—until all the Products are up-to-date—can be higher.
Full reindexing
The following changes will require a full reindexing:
- A creation, update, or deletion of a Product Type.
- A creation, update, or deletion of a Store.
- A deletion of a Product Selection.
On average, a full reindex is performed within 15 minutes. The actual delay—until the product data is up-to-date—can be higher and depends on the following factors:
- The number of Products in the catalog.
- The number and size of the Product Variants of the Products.
- The number of Standalone Prices used in the Project.
- The number of Product Selections and Stores used in the Project.
Query expressions
| Functionality | Product Search |
|---|---|
| Full-text search | fullText expression |
| Fuzzy search | fuzzy expression |
| Exact match | exact expression |
| Range of values | range expression |
| If field exists | exists expression |
| Prefix match | prefix expression |
| Wildcard match | wildcard expression |
| Boosting particular searchable fields | boosting |
| Specify query expressions per searchable field | by field name |
| Operators to define compound expressions | and, or, not, filter operators |
Query result
Classic or Modular. For details, see Data integration.| Functionality | Product Search |
|---|---|
| IDs of the matching Products | by default |
| Full representations of the matching Products | with GraphQL |
| Price selection | with GraphQL |
| Scoped Price search | with filter by price |
Full-text search
| Functionality | Product Search |
|---|---|
| Specific to different languages | supported languages |
| Allow to specify which Product fields should be taken into account. | language parameter in fullText expression |
Fuzzy search
| Functionality | Product Search |
|---|---|
| The fuzziness level adjusts automatically based on term length. | Set maximum level with level parameter in fuzzy expression. |
| Allows you to specify which Product fields to include. | field parameter in fuzzy expression |
Filtering
| Functionality | Product Search |
|---|---|
| Non-facet-driven filter (for example by Category, Attribute values, full-text search) that both results and every facet's base set inherit. | query in ProductSearchRequest |
| Narrow the set of returned products after facets are calculated. | postFilter in ProductSearchRequest |
| Narrow the calculated facet results. | filter in facet expressions |
Faceting
| Functionality | Product Search |
|---|---|
| Count all occurrences of distinct values. | distinct facets |
| Count Products that have values within a specified range. | ranges facets |
| Count the number of Products or Product Variants. | count facets |
| Calculate statistics for facet values. | stats facets |
| Allow for handling missing values when calculating facets. | missing values |
| Filter facet results. | filter facet |
| Specify scope of facets. | scope of facets |
| Assign a custom name to a facet result. | name in facet expression |
Sorting
| Functionality | Product Search |
|---|---|
| Sort order (ascending or descending) | asc and desc sort order |
| Sort mode to specify which value to sort on when a Product contains multiple values for the field | min, max, avg, sum sort mode |
| Specify exactly which resources should be considered for sorting. | sort filter |
Sort by a localized field, such as name | language property on the sort object |
| Sort by CategoryOrderHints | structured sort object |
Pagination
| Functionality | Product Search |
|---|---|
Maximum results per page (limit) | 100 |
| Maximum total results reachable through pagination | first 10 000 results |
| Error when the offset exceeds the maximum | InvalidInput error |
Limits
Product Search supports searching through Products across the following resources:
| Resource | Limit per Product |
|---|---|
| Stores | 15 000 |
| Product Selections | 15 000 |
| Standalone Prices | 10 000 |
These limits are soft:
- If you exceed the limits for Stores or Product Selections, the API returns non-deterministic results.
- If you exceed the limit for Standalone Prices, the API applies a sorting algorithm during Product indexing to offer a balanced selection of Prices across Variants.