---
title: "Implementing top items with the API"
description: "Learn how to use the Top Items and Personalized Top Items APIs to show top items when a user focuses an empty search bar."
slug: quickstart/autocomplete/top-items-api
docKind: guide
hub: quickstart
---

## Introduction

A powerful way to enhance product discovery is to show **Top Items** the moment a user shows intent by clicking into your search bar, even before they start typing. This guide will show you how to use the [Top Items API](/autocomplete/api/v1/top-items/) to implement this feature for a custom UI.

This approach is for developers who need full control over the user experience and are responsible for rendering the results and tracking all related analytics events.

### What you'll learn

- How to call the [Top Items API](/autocomplete/api/v2/top-items/) to fetch popular items.
- How to call the [Personalized Top Items API](/autocomplete/api/v1/top-items/#personalized-top-items-last-searched-queries) to fetch user-specific top items.
- The critical importance of manually tracking analytics when top items are shown on focus.

### Who is this guide for

- Developers who have understood the ["Getting query suggestions via the Autocomplete API"](/quickstart/autocomplete/query-suggestions/) and now want to add top items shown on focus.
- Mobile developers (iOS, Android) who are integrating search suggestions.
- Any developer needing to fetch top or personalized item data directly.

### Prerequisites

Before you start, please ensure you have the following in place:

- A working search input field in your application.
- The ability to make HTTP `GET` requests from your application and render the results.
- Your Luigi's Box `trackerId`.
- A setup for manually sending analytics events, as detailed in the Events API guides.

:::note[Runnable example HTML]
Open or download the full standalone sample: [top-items-datalayer.html](/examples/autocomplete/top-items-datalayer.html) — demonstrates focus-triggered Top Items with DataLayer analytics.
:::

## Step-by-step

We will now modify the code from the previous guide. The changes involve adding a new API endpoint, creating a function to fetch top items, updating your analytics tracking, and adding a `focus` event listener.

### Step 1: Add the top items API endpoint

In your JavaScript configuration section, add a new constant for the Top Items API URL.

#### Example

```javascript
// --- CONFIGURATION ---
const TRACKER_ID = "YOUR_TRACKER_ID";
const TOP_ITEMS_API_URL = "https://live.luigisbox.com/v1/top_items"; // <-- ADD THIS
const AUTOCOMPLETE_API_URL = "https://live.luigisbox.com/autocomplete/v2";
// ... rest of your config
```

### Step 2: Create a function to fetch top items

Next, add a new asynchronous function specifically for fetching top items. This function will call the `TOP_ITEMS_API_URL` and then use your existing `renderResults` function to display the data.

#### Endpoint

`GET` `https://live.luigisbox.com/v1/top_items`

#### Required parameters

- **`tracker_id`:** Your unique site identifier.
- **`type`:** A comma-separated list of the content type you want top items for, along with the quantity for each (e.g, `product:6,category:3`).

#### Optional parameters (recommended)

- **`hit_fields`:** A comma-separated list of attributes you need (e.g., title,url,price,image_link). Using this parameter is highly recommended to keep the API response fast and small by only fetching the data you will display.

#### Example: Add this new function to your core logic

```javascript
const getTopItemsSuggestions = async () => {
  try {
    const response = await axios.get(TOP_ITEMS_API_URL, {
      params: {
        tracker_id: TRACKER_ID,
        type: "product:5,category:3",
        hit_fields: "title,url,price,image_link",
      },
    });
    const hits = response.data.hits;

    // Override the default group titles for this specific view
    const customTitles = {
      item: "Popular Products",
      category: "Top Categories",
    };
    renderResults(hits, customTitles);

    // We will send analytics in the next step
  } catch (error) {
    console.error("Failed to fetch top items:", error);
  }
};
```

This function is very similar to your existing `getQuerySuggestions` function but calls a different endpoint and doesn't require a `q` (query) parameter. It also prepares custom titles for the groups.

### Step 3: Update analytics for different suggestion types

To distinguish between regular query-based suggestions and top items shown on focus, we need to adjust our analytics tracking. 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](/lbx-script/) is included on your page.

- **Query-driven suggestions** (from the Autocomplete API) should be tracked as an **Autocomplete** event.
- **Top items shown on focus** (loaded from the Top Items endpoint) should be tracked as a **Recommendation** event.

#### Autocomplete (query-driven) view event

```javascript+dataLayer
// Send VIEW event (Autocomplete list)
function sendAutocompleteViewAnalytics(query, hits) {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      event: "view_item_list",
      ecommerce: {
        item_list_name: "Autocomplete",
        search_term: query || "",
        items: hits.map((hit, index) => ({
          item_id: hit.url || hit.attributes.title,
          item_name: hit.attributes.title,
          index: index + 1
        }))
      }
    });
}
```

```javascript+API
// Send VIEW event (Autocomplete list)
function sendAutocompleteViewAnalytics(query, hits, customFilters = {}) {
    const analyticsPayload = {
        id: uuid.v4(),
        type: "event",
        tracker_id: TRACKER_ID,
        client_id: CLIENT_ID,
        lists: {
            "Autocomplete": {
                query: {
                    string: query || "",
                    // Add custom filters to distinguish the source
                    filters: customFilters
                },
                items: hits.map((hit, index) => ({
                    title: hit.attributes.title,
                    url: hit.url || hit.attributes.title,
                    position: index + 1
                }))
            }
        }
    };
    sendAnalyticsEvent(analyticsPayload);
}
```

#### Top items on focus (recommendation) view event

```javascript+dataLayer
// Send VIEW event (Recommendation list for Top Items on focus)
function sendTopItemsViewAnalytics(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.url || hit.attributes.title,
        item_name: hit.attributes.title,
        index: index + 1,
        price: hit.attributes.price || null,
        type: hit.type,
      })),
      filters: {
        RecommenderClientId: "autocomplete_popup",
        Recommender: "autocomplete_popup",
      },
    },
  });
}
```

```javascript+API
// Send VIEW event (Recommendation list)
function sendTopItemsViewAnalytics(hits) {
  const analyticsPayload = {
    id: uuid.v4(),
    type: "event",
    tracker_id: TRACKER_ID,
    client_id: CLIENT_ID,
    lists: {
      Recommendation: {
        query: {
          filters: {
            // REQUIRED: stable identifier of this widget/list
            RecommenderClientId: "autocomplete_popup",
            // Optional but useful for debugging/segmentation
            Recommender: "autocomplete_popup",
          },
        },
        items: hits.map((hit, index) => ({
          title: hit.attributes.title,
          type: hit.type,
          url: hit.url || hit.attributes.title,
          position: index + 1,
          price: hit.attributes.price || null,
        })),
      },
    },
  };
  sendAnalyticsEvent(analyticsPayload);
}
```

#### Example: Update the two fetcher functions

In `getTopItemSuggestions`, add this line after `renderResults(hits)`:

```javascript
if (hits && hits.length > 0) {
  sendTopItemsViewAnalytics(hits);
}
```

In your existing `getQuerySuggestions`, modify the analytics call:

```javascript
// Change the original analytics call to this:
if (hits && hits.length > 0) {
  sendAutocompleteViewAnalytics(query, hits); // No custom filter needed here
}
```

### Step 4: Add the `focus` event listener

Finally, add a `focus` event listener to your search input. This will trigger your new `getTopItemsSuggestions` function whenever a user clicks into an empty search box.

#### Example

```javascript
// --- EVENT LISTENERS ---
searchInput.addEventListener(
  "input",
  debounce((e) => getQuerySuggestions(e.target.value), 300),
);

// ADD THIS NEW LISTENER
searchInput.addEventListener("focus", () => {
  // Only fetch top items if the search box is empty
  if (searchInput.value === "") {
    getTopItemsSuggestions();
  }
});
```

## Advanced: Adding personalized top items

### Step 1: Add the personalized API endpoint

First, add the URL for the personalized API to your configuration constants.

#### Example: Add the following line to your configuration

```javascript
const PERSONALIZED_TOP_ITEMS_API_URL =
  "https://live.luigisbox.com/v1/personalized_top_items";
```

### Step 2: Create a function for personalized suggestions

Now, create a new function to handle fetching personalized data. This function calls the personalized endpoint and requires a `user_id` parameter.

#### Example: Add the following function to your core logic

```javascript
const getPersonalizedTopItemsSuggestions = async (userId) => {
  if (!userId) return; // Don't run if there's no user ID

  try {
    const response = await axios.get(PERSONALIZED_TOP_ITEMS_API_URL, {
      params: {
        tracker_id: TRACKER_ID,
        user_id: userId, // The specific user's ID
        type: "items:5,query:3", // e.g., personalized items and last searched queries
      },
    });
    const hits = response.data.hits;
    const customTitles = {
      item: "Recommended For You",
      query: "Your Recent Searches",
    };
    renderResults(hits, customTitles);

    // Add the analytics call for this new function
    if (hits && hits.length > 0) {
      sendAutocompleteViewAnalytics(null, hits, {
        source: "personalized_top_items",
      });
    }
  } catch (error) {
    console.error("Failed to fetch personalized top items:", error);
  }
};
```

### Step 3: Update the `focus` event listener

The final step is to update your focus event listener with logic to choose which function to call based on whether a user is logged in.

#### Example: Replace your existing `focus` listener with the following version

```javascript
// Replace the previous searchInput.addEventListener('focus', ...) with this:
searchInput.addEventListener("focus", () => {
  if (searchInput.value === "") {
    // In a real application, you would check if the user is logged in
    // and get their actual ID from your session or auth system.
    const currentUserId = "user-123"; // Example: get this from your session

    if (currentUserId) {
      getPersonalizedTopItemsSuggestions(currentUserId);
    } else {
      getTopItemsSuggestions();
    }
  }
});
```

## Best practices

- **You are in control:** Remember that when using the direct API, you are fully responsible for the implementation, including all analytics tracking. Failure to send analytics events will prevent the system from learning.
- **Use `hit_fields`:** To minimize the response size and improve speed, use the optional `hit_fields` parameter in your API call to request only the attributes you actually need to display (e.g., `&hit_fields=title,price,image_link`).
- **Match user IDs for personalization:** For the Personalized Top Items API to be effective, the `user_id` you send in the API request must match the `customer_id` you send in your analytics events for that same user.

## Next steps

- **Implement trending queries:** Learn how to fetch and display popular search terms by following our ["Implementing trending queries suggestions"](/quickstart/autocomplete/trending-queries/) guide.
