Reference
Parameters
All parameters are optional. Defaults are shown in the table below.
| Parameter | Type | Default | Applies to | Description |
|---|---|---|---|---|
windowDays | number (1–365) | 28 | All order-based algorithms | How many days of history the engine reads. A wider window smooths out short-term noise; a narrower window makes results more responsive to recent changes. |
topN | number (1–50) | 12 | All algorithms | How many items to include in each output row. |
minSupport | number (1–100) | 3 | Trending, Viewed together, Bought together | Minimum number of events required for a product or product pair to appear in results. Filters out items with too little data to rank reliably. |
perCategory | boolean | false | Bestsellers, Trending, New arrivals | When true, the engine produces one output row per product category (key: cat:{category}) alongside the global row (key: _global). Uses the category field on products in your Live Catalog. |
topK | number (1–50) | 12 | Similar products | How many similar products to include per seed product. |
sameCategoryOnly | boolean | false | Similar products | When true, products are only matched against products in the same catalog category. |
excludeOutOfStock | boolean | false | All algorithms | When true, products that are out of stock are never recommended. A product is dropped at serve time when its Live Catalog availability is out_of_stock or removed, and the engine over-fetches extra candidates so dropping them still fills the placement. The toggle is off when you create a recipe, so turn it on if out-of-stock products should never appear. (A recipe created through the API without the parameter at all is served as though it were on, which is the safe reading of an unset value.) |
sessionAffinity | boolean | false | All algorithms | Shown in the recipe editor as Session affinity re-rank. When true, the recommendations a shopper is about to see are re-ranked towards the categories that shopper is already interested in: the product they are viewing, what is in their cart, and what they viewed recently. Items sharing a category with any of those seed products are scored 1.5x and move up; nothing is added and nothing is removed, so the same products are recommended in a different order. Up to three seed products are read per request, and a seed missing from the Live Catalog simply contributes nothing. Off by default: turning it on is a deliberate choice. |
Similar products ignores windowDays, topN, minSupport, and perCategory; the order-based algorithms ignore topK and sameCategoryOnly. excludeOutOfStock and sessionAffinity apply to every algorithm. The two differ in when they act: excludeOutOfStock decides which products may be recommended, sessionAffinity only changes the order of the ones that already may be.
Key scheme
How you look up results depends on the algorithm.
List algorithms (Bestsellers, Trending, New arrivals):
| Key | Returns |
|---|---|
_global | The site-wide ranked list |
cat:{category} | The ranked list for that category (only present when perCategory is true) |
Seed-based algorithms (Viewed together, Bought together, Similar products):
| Key | Returns |
|---|---|
{sku} | The products most commonly viewed or bought alongside this SKU, or, for Similar products, the products whose catalog text is most similar to it |
If there is no row for the key you request, result.found is false. This happens for new products with no history, or for a category with fewer items than minSupport requires.
Output row shape
Every row in the output dataset has this shape:
{ "key": "SHIRT123", "items": [ { "sku": "SHOE456", "score": 0.87, "title": "Runner", "image": "https://cdn.example.com/shoe456.jpg", "href": "/products/shoe456", "price": 5999, "compareAtPrice": 6999, "currency": "USD", "availability": "in_stock" } ]}sku and score are always present. The product details (title, image, href, category) come from your Live Catalog row for that SKU.
The fields that change often (price, compareAtPrice, availability, stock, and image) are resolved fresh from the Live Catalog at serve time, overlaid onto the nightly-baked row. price and compareAtPrice are integer minor units (the smallest unit of the currency: 5999 means $59.99 for a currency of USD); format them for display yourself. availability is one of in_stock, out_of_stock, or removed. The nightly ranking is unchanged by all of this: only which numbers a card shows.
score is a relative rank signal: higher is better for the seed product. It is not a probability or a currency value; do not display it to visitors.
Because price and stock resolve at serve time, a price you change in your store (or a product that sells out) is reflected within the edge-cache window (about a minute) or on the next catalog-epoch change. You do not need to re-run the recipe or republish the dataset for the new number to appear. The ranking (which products, and their order) still comes from the nightly run.
Cold-start floor
A recipe needs enough event data before it will produce output.
| Algorithm | Floor |
|---|---|
| Bestsellers | 250 orders in the recipe's window |
| Trending | 250 orders in the recipe's window |
| New arrivals | No floor: reads catalog only |
| Viewed together | 250 product-view events in the recipe's window |
| Bought together | 250 orders in the recipe's window |
| Similar products | No floor: reads catalog only |
While a recipe is below its floor, runs complete with outcome gathering_data. The recipe page shows this status. No output dataset version is written in this state. Once the floor is crossed, the next run produces output.
Run schedule and rate limits
| Setting | Value |
|---|---|
| Nightly automatic run | 03:00 UTC, enabled recipes only |
| Manual trigger ("Run now") | Rate-limited to once per 10 minutes per recipe |
Disabled recipes do not run automatically and cannot be triggered manually.
A Similar products recipe additionally runs automatically, outside the rate limit, the moment you turn it on (disabled → enabled). Its nightly run also skips re-building its product fingerprints when your Live Catalog has not changed since the last run, which is why a nightly run on a stable catalog finishes faster than the first one. See Similar Products.
Run stats fields
After each run, the recipe page shows stats from that run. The same fields are stored in lastRunStats on the recipe object returned by the API.
| Field | Type | Meaning |
|---|---|---|
status | string | success, failed, or gathering_data |
rows | number | Number of keys written to the output dataset version |
recommendedItems | number | Total product items kept after the denormalise pass (items that had a Live Catalog row) |
droppedNoCatalog | number | Items excluded because their SKU was not found in the Live Catalog |
durationMs | number | How long the compute job took, in milliseconds |
floorRequired | number | The cold-start floor for this recipe's algorithm |
floorActual | number | How many qualifying events were found in the window |
error | string | Present only when status is failed. A short description of what went wrong. |
droppedNoCatalog is worth monitoring. A high number means your Live Catalog is missing SKUs that appear in orders or product-view events: those products will never appear in recommendations until they come through the catalog (see Set up your product catalog).
recommendedItems records how many items were kept by the denormalise pass. The Product catalog page rolls up recommendedItems and droppedNoCatalog across all enabled recipes to produce the project-level coverage percentage. Recipes whose lastRunStats does not yet carry recommendedItems (run before this field was added) are excluded from that roll-up until their next successful run.
Similar products runs add these fields:
| Field | Type | Meaning |
|---|---|---|
embedded | number | Products turned into embeddings during this run |
skippedEmptyText | number | Products skipped because they have no title and no description |
emptyTopK | number | Seed products that ended up with no similar matches after filtering: they get no output row |
embeddedCatalogEpoch | number | The Live Catalog epoch the embeddings were built from: when this matches the current catalog epoch, the next run skips the embedding stage |
stage | string | Present only while a run is in progress: embedding or materializing. The recipe page shows it as "Embedding catalog…" / "Building similar-products feed…" |
Output dataset properties
Each recipe creates exactly one output dataset. Its properties are fixed and set by the engine:
| Property | Value |
|---|---|
| Type | FEED |
| Source | SYSTEM |
| Key field | key |
| Slug | rec-{recipe-name-kebab-case} (generated at creation) |
The dataset's slug, type, key field, and source are immutable. Renaming a recipe renames its output dataset to match, so the datasets list never shows a stale name: rename "Bestsellers" to "Top sellers" and the dataset becomes "Top sellers (engine)".
The slug never moves. It is the handle your variation code passes (recipe: 'rec-bestsellers'), so re-deriving it from a new name would break every live variation using that recipe. Rename freely: the name is a label, the slug is the address. To start fresh with a new slug, delete the recipe and create a new one.
Limits
| Limit | Value |
|---|---|
| Recipes per project | 25 |
| Datasets per project | 50 (each recipe uses one of them) |
| Fallback chain steps | 4 |
Maximum topN | 50 |
Maximum topK | 50 |
Maximum windowDays | 365 |
| Manual run rate limit | Once per 10 minutes per recipe |
Both project limits apply when you create or duplicate a recipe. Recipes you already have keep running and serving whatever the counts are, so a limit never takes an existing recipe away from you. Every recipe creates one output dataset, so 25 recipes use half of the 50 dataset slots and the other half stays free for datasets you upload yourself. If you hit either limit, delete a recipe or a dataset you no longer use and try again.