Skip to content

Building custom recommendations with the Recommender API

View MD

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.

  • 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.
  • Developers building single-page applications or custom storefront UIs.
  • Anyone evaluating the Recommender API for custom integration.
  • 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.

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.

POST https://live.luigisbox.com/v1/recommend

  • tracker_id: Your Luigi’s Box Tracker ID.

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.

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"
}
]

The API responds with a JSON array containing a response object for each request you sent.

[
{
"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
}
}
]
}
]
  • 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.

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);
}
}

This function takes the API response and generates the HTML for the product cards. This part is fully customizable to your needs.

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>
`;
}

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.

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 approach
function 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,
}
}
});
}

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 the analyticsData object we saved.
  • RecommenderClientId: (Required) The unique name for this widget. We also get this from analyticsData.
  • 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.

You must also track when a user clicks on a recommended item.

// DataLayer approach
function trackClickEvent(productId) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "select_item",
ecommerce: {
items: [
{
item_id: productId,
},
],
},
});
}
  • Analytics is not optional: Sending view_item_list and click events 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, the CLIENT_ID should be a persistent identifier stored in a long-term cookie or local storage.
  • 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.