Building custom recommendations with the Recommender API
Introduction
Section titled “Introduction”This guide provides a comprehensive walkthrough for building a custom recommendation widget by calling the Recommender API directly from your frontend. This approach gives you complete control over the final look and feel, allowing you to create a user experience that is perfectly tailored to your brand.
By the end of this guide, you will have a fully functional recommendation widget, powered by your own client-side JavaScript, and a clear understanding of how to implement the detailed analytics tracking required to make it effective.
What you’ll learn
Section titled “What you’ll learn”- How to call the Recommender API directly from the frontend.
- How to render recommendation results from the API response.
- How to manually and correctly track analytics events, which is critical for model performance.
Who is this guide for
Section titled “Who is this guide for”- Developers building single-page applications or custom storefront UIs.
- Anyone evaluating the Recommender API for custom integration.
Prerequisites
Section titled “Prerequisites”- Your Luigi’s Box
TrackerId. - The ability to write and serve a standard HTML, CSS, and JavaScript file.
- The ability to make HTTP requests from your frontend code.
- An existing product page with a unique product ID available.
Step-by-step
Section titled “Step-by-step”Step 1: Set up the HTML structure
Section titled “Step 1: Set up the HTML structure”Start by creating the basic structure for your widget. This will be a simple container on a product page where the recommended items will be displayed.
Example: basic HTML layout on a product page
Section titled “Example: basic HTML layout on a product page”<html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Product Page</title> </head> <body> <h1>Rider Aqua Thong Slipper</h1> <p>ID: /rider-aqua-thong-slipper-blue-green-38/</p>
<div id="recommender-container"></div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script> // --- DOM ELEMENTS --- const recommenderContainer = document.getElementById( "recommender-container", );
// JS will go here </script> </body></html>Step 2: Understand the Recommender API request
Section titled “Step 2: Understand the Recommender API request”To get recommendations, you send a POST request to the Recommender API endpoint with a JSON payload.
Endpoint
Section titled “Endpoint”POST https://live.luigisbox.com/v1/recommend
Required parameters
Section titled “Required parameters”tracker_id: Your Luigi’s Box Tracker ID.
Request body (JSON array)
Section titled “Request body (JSON array)”The body of your POST request is an array containing one or more recommendation request objects.
recommendation_type: The identifier of the recommender model you want to use.recommender_client_identifier: A unique name for this specific widget, used for analytics.item_ids: An array of product IDs to base the recommendation on.hit_fields: An array of product attributes to return. (Highly recommended) Requesting only the fields you need (e.g.,title,url,price,image_link) significantly improves performance by reducing the response size.size: The number of results to return.user_id: A unique user identifier for personalization.
Example: Recommender API request
Section titled “Example: Recommender API request”POST https://live.luigisbox.com/v1/recommend?tracker_id=111111-111111
[ { "recommendation_type": "item_detail_complements_1", "recommender_client_identifier": "item_detail_complements_1", "item_ids": ["/rider-aqua-thong-slipper-blue-green-38/"], "size": 4, "hit_fields": ["title", "url", "price_amount", "image_link", "brand", "id"], "user_id": "123456789012345678" }]Step 3: Understand the API response
Section titled “Step 3: Understand the API response”The API responds with a JSON array containing a response object for each request you sent.
Example: Recommender API response
Section titled “Example: Recommender API response”[ { "recommendation_id": "a24588e9-0664-4637-91d5-165313a6eac8", "recommender": "c01", "recommendation_type": "item_detail_complements_1", "recommender_client_identifier": "item_detail_complements_1", "hits": [ { "url": "/complementary-item-789", "attributes": { "title": "Matching Item", "id": ["ITEM-789"], "price_amount": 35.5 } } ] }]Key fields overview
Section titled “Key fields overview”recommendation_id: Unique identifier for the recommendation.recommendation_type: Type of recommendation (e.g., “item_detail_complements_1”).recommender_client_identifier: Client identifier for the specific widget.hits: Array of recommended product objects.
Step 4: Fetch recommendations
Section titled “Step 4: Fetch recommendations”This function builds the request, makes the POST call, and, most importantly, stores the necessary data for analytics tracking.
Example: fetching recommendations with axios and preparing for analytics
Section titled “Example: fetching recommendations with axios and preparing for analytics”// --- CONFIGURATION ---const TRACKER_ID = "YOUR_TRACKER_ID";const API_ENDPOINT = `https://live.luigisbox.com/v1/recommend?tracker_id=${TRACKER_ID}`;const CLIENT_ID = String(Math.floor(Math.random() * 1e18));const CURRENT_PRODUCT_ID = "/rider-aqua-thong-slipper-blue-green-38/";let analyticsData = {}; // Object to hold data for tracking
// --- API CALL ---async function getRecommendations() { const requestPayload = { recommendation_type: "item_detail_complements_1", recommender_client_identifier: "item_detail_complements_1", item_ids: [CURRENT_PRODUCT_ID], size: 4, hit_fields: ["title", "url", "price_amount", "image_link", "brand", "id"], user_id: CLIENT_ID, };
try { const response = await axios.post(API_ENDPOINT, [requestPayload]); const recommendationData = response.data[0];
if (recommendationData && recommendationData.hits.length > 0) { // Store all necessary data for the analytics payload analyticsData = { RecommendationId: recommendationData.recommendation_id, Recommender: recommendationData.recommender, Type: recommendationData.recommendation_type, RecommenderClientId: recommendationData.recommender_client_identifier, ItemIds: requestPayload.item_ids, };
renderRecommendations(recommendationData.hits); trackRecommendationView(recommendationData.hits); } } catch (error) { console.error("Error fetching recommendations:", error); }}Step 5: Render the results
Section titled “Step 5: Render the results”This function takes the API response and generates the HTML for the product cards. This part is fully customizable to your needs.
Example: render recommendations
Section titled “Example: render recommendations”function renderRecommendations(hits, title = "Complement Products") { const productsHTML = hits .map((hit) => { const { url, attributes } = hit; const imageUrl = attributes.image_link || "https://placehold.co/400x400/eee/ccc?text=No+Image"; const productTitle = attributes.title || "No Title Available"; const brand = attributes.brand ? attributes.brand[0] : ""; const price = attributes.price_amount ? `${attributes.price_amount} EUR` : ""; const productId = attributes.id ? attributes.id[0] : url;
return ` <div class="product-card"> <a href="${url}" target="_blank" class="product-link" data-product-id="${productId}"> <img src="${imageUrl}" alt="${productTitle}" class="product-image" onerror="this.onerror=null;this.src='https://placehold.co/400x400/eee/ccc?text=No+Image';"> <div class="product-info"> <h3 class="product-title">${productTitle}</h3> <p class="product-brand">${brand}</p> <div class="product-price">${price}</div> </div> </a> </div>`; }) .join("");
recommenderContainer.innerHTML = ` <h2>${title}</h2> <div class="recommender-grid">${productsHTML}</div> `;}Step 6: Track analytics events manually
Section titled “Step 6: Track analytics events manually”This is the most critical step. You must manually construct and send analytics events. You have two options for sending analytics: the DataLayer Collector (recommended for web integrations that already use a dataLayer) or the Events API (recommended for backend or mobile integrations). If you choose the DataLayer Collector, make sure the LBX script is included on your page.
The “view_item_list” event
Section titled “The “view_item_list” event”After displaying the recommendations, you must immediately send a “view_item_list” event. This tells Luigi’s Box which products were shown to the user.
// DataLayer approachfunction trackRecommendationView(hits) { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "view_item_list", ecommerce: { item_list_name: "Recommendation", items: hits.map((hit, index) => ({ item_id: hit.attributes.id ? hit.attributes.id[0] : hit.url, // Must match your catalog identity item_name: hit.attributes.title, index: index + 1, price: hit.attributes.price_amount, type: "item" })), filters: { "RecommenderClientId": analyticsData.RecommenderClientId, "RecommendationId": analyticsData.RecommendationId, "ItemIds": analyticsData.ItemIds, "Recommender": analyticsData.Recommender, "Type": analyticsData.Type, } } });}// Events API approachfunction trackRecommendationView(hits) { const viewPayload = { id: crypto.randomUUID(), type: "event", tracker_id: TRACKER_ID, client_id: CLIENT_ID, lists: { "Recommendation": { items: hits.map((hit, index) => ({ title: hit.attributes.title, type: "item", url: hit.attributes.id ? hit.attributes.id[0] : hit.url, // Object identity position: index + 1, price: hit.attributes.price_amount })), query: { filters: { RecommendationId: analyticsData.RecommendationId, RecommenderClientId: analyticsData.RecommenderClientId, ItemIds: analyticsData.ItemIds, Recommender: analyticsData.Recommender, Type: analyticsData.Type, } } } } }; sendAnalyticsEvent(viewPayload);}Breaking down the query.filters object
Section titled “Breaking down the query.filters object”This object provides the full context for the recommendation and is required for analytics to work correctly.
RecommendationId: (Required) The unique ID for this set of results. We get this from theanalyticsDataobject we saved.RecommenderClientId: (Required) The unique name for this widget. We also get this fromanalyticsData.ItemIds: The list of product IDs used in the original request.Recommender: The name of the recommender model, from the API response.Type: The type of the recommender, also from the API response.
The “click” event
Section titled “The “click” event”You must also track when a user clicks on a recommended item.
// DataLayer approachfunction trackClickEvent(productId) { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "select_item", ecommerce: { items: [ { item_id: productId, }, ], }, });}// Events API approachfunction trackClickEvent(productId) { const clickPayload = { id: crypto.randomUUID(), type: "click", tracker_id: TRACKER_ID, client_id: CLIENT_ID, action: { type: "click", resource_identifier: productId, recommendation_id: analyticsData.RecommendationId, // Link the click to the view }, }; sendAnalyticsEvent(clickPayload);}Best practices
Section titled “Best practices”- Analytics is not optional: Sending
view_item_listandclickevents is mandatory. Without them, the models cannot learn, and you will not see performance data in your dashboard. - Use a persistent
CLIENT_ID: For accurate tracking, theCLIENT_IDshould be a persistent identifier stored in a long-term cookie or local storage.
Next steps
Section titled “Next steps”- Implement batching: If you want to show multiple recommenders on a single page, send both request objects in the same API call. This improves performance and ensures you don’t see duplicate products across widgets.
Was this page helpful?
Thanks.