machine-learningrecommendation-systemsjsonai

Recommendation Systems: The JSON Behind "You Might Also Like"

·8 min read·AI & JSON

Every "recommended for you" row is a JSON array

Product recommendation carousels, "customers also bought" widgets, and streaming "because you watched" rows are all, structurally, the same thing: a ranked JSON array of item IDs with scores, served from a recommendation engine to a frontend that has no idea how the ranking was computed. Understanding the JSON contract between the recommender and the UI is what lets you build, test, and debug these features without needing to understand the ML model producing them.

The basic recommendation response

json
{
  "userId": "usr_9182",
  "context": "product_page",
  "items": [
    { "productId": "sku_4471", "score": 0.94, "reason": "frequently_bought_together" },
    { "productId": "sku_2290", "score": 0.87, "reason": "similar_category" },
    { "productId": "sku_8842", "score": 0.81, "reason": "trending" }
  ],
  "modelVersion": "collab-filter-v12"
}

The array's order is the recommendation — items are pre-ranked by the engine, and the frontend is expected to render them in the order received, not re-sort by score itself (which is often on a model-specific scale that isn't meaningful to compare across different reason types).

The reason field: turning a score into an explanation

score alone tells you nothing about *why* an item was recommended — reason (sometimes called explanation or strategy) is what lets a UI show "Because you bought X" instead of an unexplained list, which measurably improves user trust in recommendations:

ReasonTypical meaning
frequently_bought_togetherCo-purchase pattern from other customers' orders
similar_categoryContent-based similarity (same category/attributes)
collaborative_filtering"Users like you also liked this"
trendingPopular right now, not personalized
continue_watchingBased on the user's own in-progress history

A single response often blends items from multiple reasons (as in the example above) to diversify the results rather than showing ten near-identical "similar category" items in a row.

Multiple carousels, one page

A real product or home page typically requests several *distinct* recommendation slots at once, each independently ranked for a different purpose:

json
{
  "slots": {
    "similar_items": { "items": [{ "productId": "sku_2290", "score": 0.87 }] },
    "frequently_bought_together": { "items": [{ "productId": "sku_4471", "score": 0.94 }] },
    "recently_viewed": { "items": [{ "productId": "sku_1120", "score": null }] }
  }
}

recently_viewed here has score: null because it's not a model prediction at all — it's a simple chronological list from the user's own history, returned through the same API shape as the ML-ranked slots for the frontend's convenience (one JSON contract, whether or not a model was actually involved).

A/B testing: the same response, tagged with a variant

Recommendation engines are A/B tested constantly, and the response JSON typically carries the experiment assignment so the frontend can log which variant a user saw (for later analysis) without needing its own separate experiment-assignment logic:

json
{
  "items": [{ "productId": "sku_4471", "score": 0.94 }],
  "experiment": {
    "id": "reco-ranking-2026-07",
    "variant": "treatment_b",
    "modelVersion": "collab-filter-v12"
  }
}

The frontend logs experiment.variant alongside any click or purchase event it sends back, which is how the recommendation team later computes "did variant B actually convert better than the control" — without this field, click events would be unattributable to a specific experiment arm.

Feedback: closing the loop

Recommendations improve from user interactions, so the events sent back — a click, an add-to-cart, a purchase — need to reference the exact recommendation shown, including its model version and position in the list:

json
{
  "eventType": "click",
  "userId": "usr_9182",
  "productId": "sku_4471",
  "position": 1,
  "modelVersion": "collab-filter-v12",
  "experiment": { "id": "reco-ranking-2026-07", "variant": "treatment_b" },
  "timestamp": "2026-07-06T10:15:00Z"
}

position matters because of a well-known bias: users click the first item far more often *because it's first*, not necessarily because it's the best recommendation — analysis has to account for position when judging whether a ranking is actually good, which requires it to be logged on every event.

Debugging a recommendation pipeline

  1. Inspect a full response. A production recommendation payload often nests several slots and experiment metadata — the JSON Formatter makes a deeply nested response readable at a glance.
  2. Diff two model versions' output. Run the same user through the old and new model and JSON Diff the resulting item lists to see exactly what changed in the ranking, not just aggregate metrics.
  3. Validate the contract. If your frontend depends on reason always being one of a fixed set of values, lock it down with a JSON Schema so a backend change that introduces a new, unhandled reason value is caught before it ships silent UI bugs.

Frequently asked questions

Because score is frequently not comparable across different recommendation strategies blended into one response (a collaborative-filtering score and a trending-popularity score live on different scales) — the backend has already done the cross-strategy ranking and ordering, and the frontend should preserve it.

reason is for explainability to the end user ("Frequently bought together"), not ranking — it's a UX signal, while score is an internal ranking signal, and conflating them leads to either exposing meaningless raw scores to users or losing the ability to show a helpful explanation.

Items shown earlier in a list get more clicks purely due to position, independent of relevance — which is why click events log position, letting analysis correct for this bias (called position bias) rather than concluding the top item is simply "better."

No — depending on the model, it might be a probability (0–1, comparable within that model), a raw similarity metric, or an arbitrary internal ranking value. Never assume cross-model comparability without checking the specific recommender's documentation.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.