Navigation

Quickstart: Rendering banner campaigns with the Autocomplete API

Introduction

This guide is for developers who already use the Autocomplete API directly and now want to display banner campaigns inside their own custom autocomplete UI.

Banner campaigns are returned as part of the normal autocomplete response. There is no separate banners endpoint. Your application must read the campaigns array, choose the correct banner image, and render it in the right part of your interface. See what you'll build »

At a high level, nothing changes about how you fetch autocomplete suggestions. You still send the same request and still render the normal hits array. The difference is that you now also inspect the campaigns array and decide whether your UI should show a banner above the suggestions, beside them, or not at all for the current query.

What you'll learn

  • How to read banner campaign data from the Autocomplete API response.
  • How to identify supported autocomplete banner positions.
  • How to choose between desktop and mobile banner image URLs.
  • How to render banner campaigns alongside your existing autocomplete suggestions.

Who is this guide for

  • Developers who already render autocomplete results from the API directly.
  • Teams building custom web or mobile autocomplete interfaces.
  • Developers who want banner campaigns without using Autocomplete.js.

Prerequisites

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

  • A working custom autocomplete UI that already calls the Autocomplete API.
  • The ability to make HTTP GET requests and render the JSON response.
  • Your Luigi's Box trackerId.
  • Banner campaigns configured in the Luigi's Box application for at least one test query.

Important This guide builds on the same integration style as the Autocomplete API quickstart. If you do not already have a custom autocomplete request and rendering flow, start there first.

Step-by-step

Step 1: Keep your existing autocomplete request

Banner campaigns use the same endpoint as regular autocomplete suggestions.

That is an important architectural point: banners are not an additional API call and they are not a separate feature toggle in the request. Luigi's Box evaluates the current query and, if a matching campaign exists, includes the banner definitions in the response automatically.

Endpoint

GET https://live.luigisbox.com/autocomplete/v2

Required parameters

  • tracker_id: Your unique site identifier.
  • q: The current user query.
  • type: The suggestion types and counts you want to render, for example product:6,category:3,query:3.
  • hit_fields: Request only the fields you need for rendering, such as title,url,price,image_link.

Example

The code below does two things. First, it fetches the regular autocomplete suggestions into hits. Second, it reads the campaigns array from the same response and passes both datasets to separate rendering functions. Keeping those rendering paths separate makes the code much easier to reason about.

const TRACKER_ID = 'YOUR_TRACKER_ID';
const AUTOCOMPLETE_API_URL = 'https://live.luigisbox.com/autocomplete/v2';

async function loadAutocomplete(query) {
  const response = await axios.get(AUTOCOMPLETE_API_URL, {
    params: {
      tracker_id: TRACKER_ID,
      q: query,
      type: 'product:6,category:3,query:3',
      hit_fields: 'title,url,price,image_link',
    },
  });

  const hits = response.data.hits || [];
  const campaigns = response.data.campaigns || [];

  renderResults(hits);
  renderCampaigns(campaigns);
}

The important change is that you now read both hits and campaigns from the same response.

Step 2: Understand the banner campaign response

Autocomplete banner campaigns are returned in the top-level campaigns array.

Think of the campaigns array as a list of banner definitions that matched the current query. Each campaign object tells you three things:

  • which campaign matched, through its id
  • where the user should land after clicking, through target_url
  • which visual banner positions are available, through the banners object

Inside the banners object, the keys are fixed Luigi's Box position names such as autocomplete_top and autocomplete_list. Each of those position objects then contains the actual image URLs you can render.

Example response fragment

{
  "hits": [
    {
      "url": "https://yourshop.com/products/acoustic-guitar",
      "attributes": {
        "title": "Acoustic Guitar",
        "price": "499 EUR",
        "image_link": "https://yourshop.com/images/guitar.jpg"
      },
      "type": "product"
    }
  ],
  "campaigns": [
    {
      "id": 9,
      "target_url": "https://yourshop.com/brands/fender",
      "banners": {
        "autocomplete_list": {
          "desktop_url": "https://yourshop.com/banners/fender-list-desktop.jpg",
          "mobile_url": "https://yourshop.com/banners/fender-list-mobile.jpg"
        }
      }
    },
    {
      "id": 10,
      "target_url": "https://yourshop.com/sale",
      "banners": {
        "autocomplete_top": {
          "desktop_url": "https://yourshop.com/banners/sale-top-desktop.jpg",
          "mobile_url": "https://yourshop.com/banners/sale-top-mobile.jpg"
        }
      }
    }
  ]
}

In this example, the response contains two separate campaigns:

  • one campaign that provides a banner for the autocomplete_list position
  • one campaign that provides a banner for the autocomplete_top position

That does not mean both positions must always be present. Many queries will return no banners at all, some will return only one position, and some may return multiple campaign objects. Your rendering code should therefore always treat campaigns as optional data.

The supported autocomplete banner positions are:

  • autocomplete_top
  • autocomplete_list

Each banner position contains:

  • desktop_url for larger screens
  • mobile_url for smaller screens

At the campaign level, you will also typically use:

  • target_url as the click destination for the banner
  • id if you want to debug which campaign was returned

The practical rendering model is: the campaign tells you where the click should go, while the nested banner position tells you which image to show there.

Step 3: Add a helper to extract banner positions

In practice, you will usually look for the first campaign that contains a banner for a specific position.

This is useful because your UI usually renders by slot, not by campaign object. For example, your layout may have a dedicated area for a top banner and a separate area for a list banner. A small normalization helper turns the raw API structure into something your rendering code can use directly.

Example

The helper below loops through all campaigns, keeps only those that contain the requested position, and returns the first matching entry in a simplified shape.

function firstBannerForPosition(campaigns, position) {
  return campaigns
    .filter((campaign) => campaign.banners && campaign.banners[position])
    .map((campaign) => ({
      targetUrl: campaign.target_url,
      banner: campaign.banners[position],
      position,
    }))[0];
}

This gives you a normalized object you can pass to your rendering logic.

Step 4: Choose the correct image for the current viewport

Your UI should prefer the mobile banner image on smaller screens and the desktop banner image on larger screens.

This step matters because Luigi's Box gives you two image variants for the same banner placement. Your frontend is responsible for deciding which one to use. The simplest approach is to inspect the viewport width and fall back to whichever URL is available.

Example

The following helper first detects whether the current viewport should be treated as mobile and then returns the best available image URL for that context.

function isMobileViewport() {
  return window.matchMedia('(max-width: 767px)').matches;
}

function resolveBannerImage(entry) {
  if (!entry) return null;

  return isMobileViewport()
    ? entry.banner.mobile_url || entry.banner.desktop_url
    : entry.banner.desktop_url || entry.banner.mobile_url;
}

This fallback logic is useful when only one of the image URLs is present.

Step 5: Render the autocomplete banners

Once you have extracted the campaign entries, render them into the relevant parts of your autocomplete layout.

The key idea here is to keep the banner rendering logic small and explicit:

  • if there is no matching banner, render an empty or placeholder state
  • if a banner exists, build a clickable image that points to the campaign target_url
  • render each supported position into its dedicated DOM container

Example

In the example below, createBannerMarkup handles a single banner entry, while renderCampaigns decides which entry belongs to which autocomplete slot.

function createBannerMarkup(entry, label) {
  if (!entry) {
    return `<div class="placeholder">No ${label} banner returned for this query.</div>`;
  }

  const imageUrl = resolveBannerImage(entry);
  if (!imageUrl) {
    return `<div class="placeholder">No usable ${label} image URL returned.</div>`;
  }

  return `
    <a class="campaign-card" href="${entry.targetUrl}" target="_blank" rel="noreferrer noopener">
      <img src="${imageUrl}" alt="${label} banner campaign">
    </a>
  `;
}

function renderCampaigns(campaigns) {
  const topBanner = firstBannerForPosition(campaigns, 'autocomplete_top');
  const listBanner = firstBannerForPosition(campaigns, 'autocomplete_list');

  document.getElementById('autocomplete-top').innerHTML =
    createBannerMarkup(topBanner, 'autocomplete top');

  document.getElementById('autocomplete-list-banner').innerHTML =
    createBannerMarkup(listBanner, 'autocomplete list');
}

This approach keeps your campaign rendering separate from your hits rendering, which makes the code easier to maintain.

It also makes failures easier to reason about. If the query suggestions render correctly but the banner does not, you only need to inspect the campaign-related helpers instead of your entire autocomplete renderer.

Step 6: Keep your manual analytics flow intact

If you use the API directly, your regular autocomplete analytics tracking still needs to stay in place. Banner campaigns do not change that requirement.

Use the same manual analytics approach described in Quickstart: Getting query suggestions via the Autocomplete API.

Note Banner campaigns affect rendering, not the endpoint contract. You do not need to add extra request parameters for banners. If a query matches an active campaign, the campaigns array is included automatically.

Finished example

You can see the complete working example here:

Next Steps