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.

!
Warning

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

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 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.

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.