---
title: Product listing tutorial
description: A step-by-step guide to integrating product listing pages with Luigi's Box, including API requests, dataLayer events, and click tracking.
slug: tutorials/plp
docKind: tutorial
hub: tutorials
---

## Product listing integration

Product listing integration follows the same principles as the search integration. Follow the [search tutorial](/tutorials/search/) for more details.

This tutorial only highlights the couple of deviations from search.

![Product listing](/images/flows/plp.png)

## Product listing integration prerequisites

Before you begin the implementation, ensure that a valid pairing between products and categories exists. See the [Pairing docs](/plp/pairing/) for more details.

## PLP API request

![Product listing](/images/flows/plp.png)

```http
GET https://live.luigisbox.com/search
  ?f[]=type:item
  &f[]=category_id:<category_id>
  &plp=category_id
  &facets=price_amount,category
```

:::tip
`f[]=type:item` - each search must have exactly one main type which will be used to compute available filters, to provide pagination and to sort on. Use an explicit filter on `type` attribute to provide the main type.

`f[]=category_id:<category_id>` - you must provide a filter option to load the products specific to this particular product listing. Note that you don't have to use an explicit `category_id` attribute but you can filter on any attribute or a combination of attributes. You can also use the virtual `category_path` or `all_categories_path` attributes. See the [PLP docs](/plp/api/) for more details.

`plp=category_id` - This parameter indicated that this is a PLP request and is important for all the PLP customizations defined in the Luigi's Box application to work. The value of the `plp` attribute has to be set to the name of the attribute that is used to filter products for this specific Product listing.

`facets=price_amount,category` - the response will include breakdown of attribute values for attributes `price_amount` and `category`. This part will let you render filtering options.

`user_id` & `client_id` - refer to the search tutorial.
:::

## Fire a dataLayer event for product listing

After the results have been rendered, fire a [Product listing dataLayer event](/analytics/collector/#product-listing-example) describing what you have just rendered.

![Product listing](/images/flows/plp.png)

```javascript
dataLayer.push({
    event: "view_item_list",
    ecommerce: {
        item_list_name: "Product Listing",
        items: [
            {
                item_id: "PR-49738",
                item_name: "Fender Newporter Player",
                index: 0,
                price: 289
            },
            {
                item_id: "PR-103416",
                item_name: "Fender Malibu Classic Aged Cognac Burst",
                index: 1,
                price: 619
            }
        ],
        scopes: {
            "CategoryLabel": "Musicians | Guitars | Electro-Acoustic Guitars",
            "CategoryIdentity": "882827"
        },
        filters: {}
    }
});
```

dataLayer event shortened for brevity.

:::tip
Make sure that the `item_id` refers to the object identity (the `url` attribute in the top-level object data). The dataLayer event structure is the same as for Search, except the `item_list_name`. Make sure to use `Product Listing` here. The `CategoryLabel` within `scopes` refers to the full category hierarchy. It is used strictly for display purposes in PLP analytics. The `CategoryIdentity` within `scopes` refers to the category identity as indexed in the catalog.
:::

## dataLayer click event

When the user clicks on a result, immediately fire a dataLayer event.

![PLP click](/images/flows/plp_click.png)

```javascript
dataLayer.push({
    event: "select_item",
    ecommerce: {
        items: [
            {
                item_id: "PR-49738",
            }
        ]
    }
});
```
