> ## Documentation Index
> Fetch the complete documentation index at: https://developers.criteo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Insights - Digital Shelf Intelligence

> Programmatic access to weekly aggregated shelf performance, visibility, and competitive benchmark data for brands and products.

export const EndpointBadge = ({method = "GET", children}) => {
  const METHOD_STYLES = {
    GET: {
      bg: "mint-bg-[#2AB673]"
    },
    POST: {
      bg: "mint-bg-[#3064E3]"
    },
    PUT: {
      bg: "mint-bg-[#C28C30]"
    },
    PATCH: {
      bg: "mint-bg-[#DA622B]"
    },
    DELETE: {
      bg: "mint-bg-[#CB3A32]"
    },
    API: {
      bg: "mint-bg-black"
    }
  };
  const key = method.toUpperCase();
  const styles = METHOD_STYLES[key] ?? METHOD_STYLES.API;
  return <div className="relative mt-7">
      <span className={`absolute -top-2 -left-2 z-10 ${styles.bg} text-white px-2.5 py-0.5 rounded-full text-xs font-bold tracking-wide`}>
        {key}
      </span>
      {children}
    </div>;
};

## Overview

The Digital Shelf Intelligence API gives brands and advertisers programmatic access to weekly aggregated performance data for products sold through supported retailers. It enables automated retrieval of:

* **Sales performance data** — units sold, sales, and listing prices at brand or SKU level
* **Visibility and ranking data** — PDP views and category-level sales rank
* **Competitive benchmark indices** — relative performance vs. category benchmarks for consideration and sales conversion

The API is designed for users who need to pull shelf intelligence data at scale — integrating it into internal reporting pipelines, BI tools, or automated alerting workflows — without relying on manual dashboard exports.

All data is aligned to the retailer's local time zone and reported in **weekly intervals (Monday–Sunday)**.

The report supports two aggregation levels:

* **Brand-level** — metrics grouped across all SKUs belonging to a brand (portfolio tracking, brand vs. category benchmarking, retailer comparisons)
* **SKU-level** — metrics reported per individual product (performance diagnostics, assortment optimization, PDP traffic and conversion analysis)

<Info>
  You can find the Digital Shelf Intelligence endpoints in the API Reference under Insights.
</Info>

<Info>
  **Regional availability**

  Currently available for the following countries: **United States (US)**, **Canada (CA)**, and **United Kingdom (UK)**.
</Info>

***

## Endpoints

| Verb | Endpoint | Description |
| :- | :- | :- |
| `POST` | `/experimental/retail-media/insights/digital-shelf-intelligence` | Submit a Digital Shelf Intelligence report request |
| `GET` | `/experimental/retail-media/insights/{reportId}/status` | Poll the status of a submitted report |
| `GET` | `/experimental/retail-media/insights/{reportId}/output` | Retrieve the output of a completed report |

***

## Dimensions

Dimensions describe how report data is grouped. The dimension set is fixed by `aggregationLevel` — clients do not provide a custom dimensions list.

### Time dimensions

| Dimension | Description |
| - | - |
| `startDate` | Start of the reporting week (always a Monday) |
| `endDate` | End of the reporting week (always a Sunday, inclusive) |

### Retailer dimensions

| Dimension | Description |
| - | - |
| `retailerId` | Unique identifier for the retailer |
| `retailerName` | Display name of the retailer |

### Product & taxonomy dimensions

| Dimension | Availability | Description |
| - | - | - |
| `category` | Brand & SKU | Product category |
| `brandId` | Brand only | Unique identifier for the brand |
| `brandName` | Brand only | Brand name |
| `skuId` | SKU only | Retailer-specific SKU identifier |
| `skuName` | SKU only | Retailer-specific product name |

***

## Metrics

Metrics represent performance, sales, and visibility indicators. Availability varies by aggregation level.

### Commercial performance

| Metric | Availability | Description |
| - | - | - |
| `totalSalesOnline` | Brand & SKU | Total online sales, in the account's local currency |
| `totalSoldUnitsOnline` | Brand & SKU | Total number of units sold online |
| `listingPrice` | SKU only | Product listing price from the retailer catalog |

### Visibility & ranking

| Metric | Availability | Description |
| - | - | - |
| `totalPdpPageViews` | Brand & SKU | Total product detail page (PDP) views |
| `pdpViewRank` | Brand & SKU | Rank of products by PDP views within the category |
| `salesRank` | Brand & SKU | Rank of products by total sales within the category |

### Performance indices

| Metric | Availability | Description |
| - | - | - |
| `considerationIndex` | Brand & SKU | Relative PDP views per SKU vs. category benchmark. Values >1 indicate above benchmark. |
| `salesIndex` | Brand & SKU | Relative sales per PDP view vs. category benchmark. Values >1 indicate above benchmark. |

***

## Endpoint: Submit report request

Submit a Digital Shelf Intelligence report job. The API is **asynchronous** — this call returns a `reportId` to poll for results.

<EndpointBadge method="post">
  ```http theme={null}
  https://api.criteo.com/experimental/retail-media/insights/digital-shelf-intelligence
  ```
</EndpointBadge>

### Request attributes

| Attribute | Type | Required | Description |
| - | - | :-: | - |
| `accountId` | `string` | Yes | External Retail Media account ID. Must be numeric. |
| `startDate` | `string` (date) | Yes | Inclusive start date. If not a Monday, the report is accepted and shifted back to the Monday of that week, with a warning. |
| `endDate` | `string` (date) | Yes | Inclusive end date. If not a Sunday, the report is accepted and shifted forward to the Sunday of that week, with a warning. |
| `aggregationLevel` | `string` | Yes | One of `brand` or `sku`. |
| `metrics` | `string[]` | Yes | At least one metric. Allowed values: `considerationIndex`, `listingPrice`, `pdpViewRank`, `salesIndex`, `salesRank`, `totalPdpPageViews`, `totalSalesOnline`, `totalSoldUnitsOnline`. |
| `filters` | `object` | No | Optional filter criteria applied to the report. |
| `filters.retailerIds` | `string[]` | No | Retailer IDs to filter on. Must be numeric internal IDs. |
| `filters.brandIds` | `string[]` | No | Brand IDs to filter on. Must be numeric external IDs. |
| `filters.skuIds` | `object[]` | No | SKU filters grouped by retailer. Ignored with a warning when `aggregationLevel` is `brand`. Each entry requires `retailerId` (string) and `retailerSkuIds` (string\[]). |
| `filters.categories` | `string[]` | No | Category names to filter on. |
| `format` | `string` | No | Output format. One of `json`, `json-compact`, `json-newline`, `csv`. Default: `json-compact`. |

<Note>
  `startDate` must not be after `endDate`, and the requested interval must be **100 days or less**.
</Note>

### Sample request

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://api.criteo.com/experimental/retail-media/insights/digital-shelf-intelligence' \
    --header 'Authorization: Bearer <ACCESS_TOKEN>' \
    --header 'Content-Type: application/json' \
    --data '{
      "data": {
        "attributes": {
          "accountId": "123",
          "startDate": "2025-08-11",
          "endDate": "2025-08-17",
          "aggregationLevel": "sku",
          "metrics": ["totalSalesOnline", "totalSoldUnitsOnline", "listingPrice"],
          "filters": {
            "retailerIds": ["101", "102"],
            "brandIds": ["2001", "2002"],
            "skuIds": [
              {
                "retailerId": "101",
                "retailerSkuIds": ["sku-001", "sku-002"]
              }
            ],
            "categories": ["Beverages", "Snacks"]
          },
          "format": "json-compact"
        }
      }
    }'
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.criteo.com/experimental/retail-media/insights/digital-shelf-intelligence"
  headers = {
      "Authorization": "Bearer <ACCESS_TOKEN>",
      "Content-Type": "application/json",
  }
  payload = {
      "data": {
          "attributes": {
              "accountId": "123",
              "startDate": "2025-08-11",
              "endDate": "2025-08-17",
              "aggregationLevel": "sku",
              "metrics": ["totalSalesOnline", "totalSoldUnitsOnline", "listingPrice"],
              "filters": {
                  "retailerIds": ["101", "102"],
                  "brandIds": ["2001", "2002"],
                  "skuIds": [{"retailerId": "101", "retailerSkuIds": ["sku-001", "sku-002"]}],
                  "categories": ["Beverages", "Snacks"],
              },
              "format": "json-compact",
          }
      }
  }

  response = requests.post(url, headers=headers, json=payload)
  print(response.json())
  ```

  ```java Java theme={null}
  OkHttpClient client = new OkHttpClient();

  MediaType mediaType = MediaType.parse("application/json");
  RequestBody body = RequestBody.create(mediaType, """
  {
    "data": {
      "attributes": {
        "accountId": "123",
        "startDate": "2025-08-11",
        "endDate": "2025-08-17",
        "aggregationLevel": "sku",
        "metrics": ["totalSalesOnline", "totalSoldUnitsOnline", "listingPrice"],
        "filters": {
          "retailerIds": ["101", "102"],
          "brandIds": ["2001", "2002"],
          "skuIds": [{"retailerId": "101", "retailerSkuIds": ["sku-001", "sku-002"]}],
          "categories": ["Beverages", "Snacks"]
        },
        "format": "json-compact"
      }
    }
  }
  """);

  Request request = new Request.Builder()
    .url("https://api.criteo.com/experimental/retail-media/insights/digital-shelf-intelligence")
    .post(body)
    .addHeader("Authorization", "Bearer <ACCESS_TOKEN>")
    .addHeader("Content-Type", "application/json")
    .build();

  Response response = client.newCall(request).execute();
  ```

  ```php PHP theme={null}
  $request = new HTTP_Request2();
  $request->setUrl('https://api.criteo.com/experimental/retail-media/insights/digital-shelf-intelligence');
  $request->setMethod(HTTP_Request2::METHOD_POST);
  $request->setHeader(['Authorization' => 'Bearer <ACCESS_TOKEN>', 'Content-Type' => 'application/json']);
  $request->setBody(json_encode([
      'data' => [
          'attributes' => [
              'accountId' => '123',
              'startDate' => '2025-08-11',
              'endDate' => '2025-08-17',
              'aggregationLevel' => 'sku',
              'metrics' => ['totalSalesOnline', 'totalSoldUnitsOnline', 'listingPrice'],
              'filters' => [
                  'retailerIds' => ['101', '102'],
                  'brandIds' => ['2001', '2002'],
                  'skuIds' => [['retailerId' => '101', 'retailerSkuIds' => ['sku-001', 'sku-002']]],
                  'categories' => ['Beverages', 'Snacks'],
              ],
              'format' => 'json-compact',
          ],
      ],
  ]));

  echo $request->send()->getBody();
  ```
</CodeGroup>

### Sample response

```json theme={null}
{
  "data": {
    "id": "87a8e280-9cc9-47f3-9bf9-b68e947cade5",
    "type": "InsightStatusResponse",
    "attributes": {
      "status": "Pending",
      "message": null,
      "createdAt": "2026-06-12T14:57:07.336Z",
      "expiresAt": null,
      "rowCount": null,
      "md5CheckSum": null,
      "fileSizeBytes": null
    }
  },
  "warnings": [],
  "errors": []
}
```

***

## Endpoint: Poll report status

Check whether a submitted report job has completed. Returns `status: "Pending"` while processing, or HTTP `410` if the report failed, expired, or was invalidated.

<EndpointBadge method="get">
  ```http theme={null}
  https://api.criteo.com/experimental/retail-media/insights/{reportId}/status
  ```
</EndpointBadge>

### Sample request

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/status' \
    --header 'Authorization: Bearer <ACCESS_TOKEN>'
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/status",
      headers={"Authorization": "Bearer <ACCESS_TOKEN>"},
  )
  print(response.json())
  ```

  ```java Java theme={null}
  Request request = new Request.Builder()
    .url("https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/status")
    .get()
    .addHeader("Authorization", "Bearer <ACCESS_TOKEN>")
    .build();

  Response response = client.newCall(request).execute();
  ```

  ```php PHP theme={null}
  $request = new HTTP_Request2();
  $request->setUrl('https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/status');
  $request->setMethod(HTTP_Request2::METHOD_GET);
  $request->setHeader(['Authorization' => 'Bearer <ACCESS_TOKEN>']);
  echo $request->send()->getBody();
  ```
</CodeGroup>

### Sample response

```json theme={null}
{
  "data": {
    "id": "87a8e280-9cc9-47f3-9bf9-b68e947cade5",
    "type": "InsightStatusResponse",
    "attributes": {
      "status": "Success",
      "message": null,
      "createdAt": "2026-06-12T15:00:43.000Z",
      "expiresAt": "2026-06-19T15:00:43.000Z",
      "rowCount": 0,
      "md5CheckSum": "d41d8cd98f00b204e9800998ecf8427e",
      "fileSizeBytes": 0
    }
  },
  "warnings": [],
  "errors": []
}
```

***

## Endpoint: Retrieve report output

Download the results of a completed report. Only succeeds when report status is `Success`. Returns HTTP `400` with code `insight-not-ready` if the report is still `Pending`.

<EndpointBadge method="get">
  ```http theme={null}
  https://api.criteo.com/experimental/retail-media/insights/{reportId}/output
  ```
</EndpointBadge>

### Sample request

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/output' \
    --header 'Authorization: Bearer <ACCESS_TOKEN>' \
    --header 'Accept: text/plain'
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/output",
      headers={"Authorization": "Bearer <ACCESS_TOKEN>", "Accept": "text/plain"},
  )
  print(response.text)
  ```

  ```java Java theme={null}
  Request request = new Request.Builder()
    .url("https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/output")
    .get()
    .addHeader("Authorization", "Bearer <ACCESS_TOKEN>")
    .addHeader("Accept", "text/plain")
    .build();

  Response response = client.newCall(request).execute();
  ```

  ```php PHP theme={null}
  $request = new HTTP_Request2();
  $request->setUrl('https://api.criteo.com/experimental/retail-media/insights/<REPORT_ID>/output');
  $request->setMethod(HTTP_Request2::METHOD_GET);
  $request->setHeader(['Authorization' => 'Bearer <ACCESS_TOKEN>', 'Accept' => 'text/plain']);
  echo $request->send()->getBody();
  ```
</CodeGroup>

### Sample response

```json theme={null}
{"columns":["startDate","endDate","retailerId","retailerName","category","skuId","skuName","considerationIndex"],
"data":[],
"rows":0}
```

***

## Responses

| HTTP status | Code | Description | Troubleshooting |
| - | - | - | - |
| `200 OK` | — | Report accepted. Response contains `reportId` in `data.id`. | Poll `/insights/{reportId}/status` until status is `Success`. |
| `200 OK` | — | `status: "Pending"` — report is still processing. | Retry the status call later. |
| `200 OK` | — | `status: "Success"` — output is ready. | Call `/insights/{reportId}/output` to download. |
| `200 OK` + warning | `invalid-date` | `startDate` not a Monday or `endDate` not a Sunday. Request accepted and shifted to week boundaries. | Send Monday–Sunday dates to avoid auto-adjustment warnings. |
| `200 OK` + warning | `incompatible-filter` | `skuIds` sent with `aggregationLevel: brand` — filter ignored. | Use `aggregationLevel: sku` when filtering by SKU. |
| `200 OK` + warning | `invalid-metric` | `listingPrice` requested with `aggregationLevel: brand` — metric omitted. | Use `aggregationLevel: sku` when requesting `listingPrice`. |
| `400 Bad Request` | `attribute-required` | Missing required attribute, invalid ID format, `startDate > endDate`, or interval > 100 days. | Validate the request body before submitting. |
| `400 Bad Request` | `insight-not-ready` | Output endpoint called before report reached `Success`. | Poll the status endpoint first. |
| `403 Forbidden` | `forbidden` | Caller not allowed to access the requested account or endpoint. | Confirm account permissions and feature enablement. |
| `404 Not Found` | `insight-not-found` | Report ID does not exist. | Verify the `reportId` and try again. |
| `410 Gone` | `insight-failed` | Report execution failed. | Submit a new report request. |
| `410 Gone` | `insight-invalidated` | Report invalidated and no longer available. | Submit a new report request. |
| `410 Gone` | `insight-expired` | Report expired and no longer available. | Submit a new report request. |
