Recommendations Quickstart
A vs B recommendations are API-first. A recipe computes a ranked product list nightly. A vs B serves it live, with current price and stock included. Your page renders it with your own markup. This page takes you from zero to a live, measured recommendation surface.
You need: a project with the snippet installed, and a product catalog imported (recommendations resolve against catalog product keys).
1. Create and run a recipe
In the dashboard, open Recommendations and create a recipe: for a product page start with Similar products or Bought together; for a home page, Bestsellers.
- Name it and pick the algorithm.
- Add a fallback chain (recommended). If the primary algorithm has no rows for a product, the chain falls back to the next one, for example Bought together → Bestsellers, so the surface has something to show.
- Run the recipe and wait for the run to complete, then enable it.
The recipe's public handle is its output dataset slug (shown on the recipe page: for example bought-together). Use the recipe's Preview panel to see exactly what the API will serve before you write any code.
2. Render it on a product page
const productSku = document.body.getAttribute('data-sku') ?? ''const result = await avsb.recs.get({ recipe: 'bought-together', context: { seed: productSku }, // the product the shopper is looking at maxItems: 4,})if (result.served) { const rail = document.querySelector('#also-bought') rail?.replaceChildren( ...result.items.map((item) => { const card = document.createElement('a') card.href = item.href ?? '#' // The click convention: this one attribute wires up click measurement. card.setAttribute('data-avsb-rec', item.id) card.innerHTML = ` <img src="${item.image ?? ''}" alt="${item.title ?? item.id}" width="200" height="200" /> <span>${item.title ?? item.id}</span>` return card }), )}Three things to notice:
servedgates rendering. A miss (unknown product, catalog still importing, network trouble) resolves{ served: false }: render nothing, never an error.data-avsb-recon each card recordsrec:clickautomatically via one shared listener. No per-card handlers needed.- Impressions are automatic: every completed
get()recordsrec:impression.
Prices arrive as integer minor units with a currency code (5999 + 'USD' = $59.99): see the API reference for a formatting helper.
3. Cart page: multi-seed
On a cart page, seed with up to 3 items from the cart. A vs B interleaves and dedupes the results server-side, and never recommends back a product already in the cart:
const cartSkus = ['SKU-123', 'SKU-456', 'SKU-789'] // from your own cart stateconst result = await avsb.recs.get({ recipe: 'bought-together', context: { seeds: cartSkus.slice(0, 3) }, maxItems: 6,})No product context at all? List recipes (bestsellers, trending) need none, and context: { recentlyViewed: 3 } seeds from what the visitor recently looked at.
4. A/B test two recipes
Recommendations are measured through ordinary experiments. Any experiment's variation (one specific version being tested, control or a challenger) can call the recs API. Results automatically break out impressions, click-through, and add-to-cart-after-click per variation and recipe.
Create a code experiment targeting your product pages, with two variations. Both call the same two helpers of yours, which is a good use for Project JavaScript since every variation can then reuse them:
/** @returns {string} the SKU of the product on this page */function getSeedSku() { return document.body.getAttribute('data-sku') ?? ''}/** @param {AvsbRecProduct[]} items */function renderRail(items) { // Your own rail markup, exactly as in step 2.}// Variation A: bought-together/** @type {(options: AvsbVariationOptions) => Promise<void>} */async function initVariation(options) { const result = await options.recs.get({ recipe: 'bought-together', context: { seed: getSeedSku() }, }) if (result.served) renderRail(result.items)}// Variation B: similar-products/** @type {(options: AvsbVariationOptions) => Promise<void>} */async function initVariation(options) { const result = await options.recs.get({ recipe: 'similar-products', context: { seed: getSeedSku() }, }) if (result.served) renderRail(result.items)}Use options.recs (not window.avsb.recs) inside variation code: it is pre-bound to the running variation, so every event is attributed with zero extra input. Want a no-recommendations holdout (a slice of visitors who see nothing, so you can measure the lift the recommendations add)? Add a variation whose code renders nothing.
Attach Revenue per visitor as the primary metric (the one metric the experiment is actually judged on) and launch. The results page shows the standard revenue analysis plus a recommendations block per variation and recipe.
5. Watch it perform, and manage it
Open the recipe page any time to see how it is doing and to manage it.
Serving performance. The Serving performance card charts, day by day, how many times the recipe was shown (impressions), how many of those actually rendered products (served), and how many were clicked. It also shows the click‑through rate and the number of shoppers who added to cart after clicking. This counts all serving traffic for the recipe, whether it was shown inside an A/B test or served directly. That lets you confirm a surface is live before, and after, you test it. Use the date picker to narrow the window. If nothing has served yet, the card says so. If analytics are briefly unavailable, it offers a retry.
Open any recipe's detail page to see its own Serving performance card:
- Daily impressions, served, and clicks, charted together.
- Totals for click-through rate and shoppers who added to cart after clicking.
Used by. The Used by card lists the experiments whose variation code references this recipe. It is a best‑effort scan of variation code for the recipe's handle. It will not find page‑level code or server‑side (Node SDK) calls. So treat an empty list as "not referenced in variation code", not "not used anywhere". The same list appears in the delete confirmation, so you don't remove a recipe an experiment still depends on.
Duplicate. Duplicate makes a disabled copy of the recipe (same algorithm, parameters, fallback chain, and the recipe's own merchandising rules) with a fresh output feed. Nothing serves until you enable the copy, so it is a safe way to try a variant (one specific version being tested, control or a challenger) of a recipe you already trust.