Quickstart: Building custom recommendations with the Recommender API
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. See full example.
This is a demonstration guide, not production code. For most frontend integrations, Luigi's Box recommends using Recco.js, which provides a more robust and production-ready solution.
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
- Developers building single-page applications or custom storefront UIs.
- Anyone evaluating the Recommender API for custom integration.
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
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
<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
To get recommendations, you send a POST
request to the Recommender API endpoint with a JSON payload.
Endpoint
Post https://live.luigisbox.com/v1/recommend
Required parameters
-
tracker_id
: Your Luigi's Box Tracker ID.
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 placement, 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
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
The API responds with a JSON array containing a response object for each request you sent.
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.50
}
}
]
}
]
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 placement. -
hits
: Array of recommended product objects.
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
// --- 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
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
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
This is the most critical step. You must manually construct and send analytics events.
The "view_items_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.
Example: Tracking the event
function 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
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 theanalyticsData
object we saved. -
RecommenderClientId
: (Required) The unique name for this placement. 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
You must also track when a user clicks on a recommended item.
Example: Tracking a click event
function 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
-
Analytics is not optional: Sending
view_list_tem
andclick
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, theCLIENT_ID
should be a persistent identifier stored in a long-term cookie or local storage.