---
title: "Top products over the window, with GMV and orders by channel"
method: POST
path: "/shop-performance/products"
tags: ["Shop Performance"]
---

# Top products over the window, with GMV and orders by channel

`POST /shop-performance/products`

## Request

- Base URL: `https://api.reacherapp.com/public/v1`
- URL: `https://api.reacherapp.com/public/v1/shop-performance/products`
- Auth: API key in header `x-api-key` and API key in header `x-shop-id`

One shop's products summed over the window from TikTok's daily product list, ranked by ``sort`` (``gmv``, ``orders``, ``product_clicks`` or ``add_cart_count``), ``limit`` products per ``page``; ``product_total`` says how many products the window held in all. Products the catalogue knows are deactivated, rejected or deleted are left out unless ``include_inactive`` is true; each row carries ``listing_status`` (live, out_of_stock, seller_deactivated, platform_deactivated, rejected, deleted, or null when unknown) and the listed ``price_min``/``price_max`` in ``price_currency``; ``listing_data_available`` is false when the catalogue could not be read (nothing excluded, listings null). Per product: GMV, refunds, orders, units, impressions, clicks, add-to-cart, and GMV plus orders for each channel — creators (affiliate), the shop's own videos and lives, product page — with ``shop_tab_gmv`` as an overlapping view and ``other_gmv`` the remainder: ``gmv`` = affiliate + seller_video + seller_live + product_card + other. Same window grammar as ``/shop-performance/timeseries``. Single-shop only; a product's ``days_observed`` says on how many days of the window TikTok listed it.

## Request body

- ProductsRequest: Body for ``/shop-performance/products`` — the shop's products ranked over the window.
  - `preset` string, nullable: Relative window ending at the shop's newest settled day: ``<n>d`` (1-999), ``<n>m`` (1-99) or ``all``, always capped at 365 days. Mutually exclusive with explicit dates; defaults to ``30d`` when neither is given.
  - `start_date` string, date, nullable: Inclusive window start (YYYY-MM-DD).
  - `end_date` string, date, nullable: Inclusive window end (YYYY-MM-DD).
  - `limit` integer: Products per page.
  - `page` integer: 1-based page over the ranked list.
  - `sort` 'gmv' | 'orders' | 'product_clicks' | 'add_cart_count': Rank by this window total, descending.
  - `include_inactive` boolean: Also list products the catalogue knows are deactivated or rejected. Off by default; out-of-stock products are always listed.

## Response `200`

Successful Response

- ShopProductsByChannelResponse
  - `shop_id` integer, required
  - `currency_code` string, nullable: Null when the products mix currencies.
  - `start_date` string, required
  - `end_date` string, required
  - `latest_available_date` string, nullable
  - `earliest_available_date` string, nullable
  - `sort` 'gmv' | 'orders' | 'product_clicks' | 'add_cart_count', required
  - `page` integer: The page these products are on; ``limit`` per page.
  - `product_total` integer, required: Distinct products TikTok listed in the window, across all pages.
  - `listing_data_available` boolean: False when the product catalogue could not be read for this response: nothing was excluded and every listing_status and price is null.
  - `products` ShopProductChannelRow[], required
    - `product_id` string, required
    - `product_name` string, nullable: Null when the product catalogue has no name for it.
    - `image_url` string, nullable: First cover image from the product catalogue, if any.
    - `days_observed` integer, required: Days in the window on which TikTok listed the product.
    - `listing_status` 'live' | 'out_of_stock' | 'seller_deactivated' | 'platform_deactivated' | 'rejected' | 'deleted', nullable: From the product catalogue: live, out_of_stock, seller_deactivated, platform_deactivated, rejected or deleted; null when the catalogue does not know the product or its status code, or when listing_data_available is false.
    - `price_min` number, nullable: Lowest listed SKU price, in ``price_currency``.
    - `price_max` number, nullable: Highest listed SKU price.
    - `price_currency` string, nullable
    - `currency_code` string, nullable
    - `gmv` number, nullable
    - `refunds` number, nullable
    - `orders` integer, nullable
    - `items_sold` integer, nullable
    - `product_impressions` integer, nullable
    - `product_clicks` integer, nullable
    - `add_cart_count` integer, nullable
    - `affiliate_gmv` number, nullable
    - `affiliate_orders` integer, nullable
    - `seller_video_gmv` number, nullable
    - `seller_video_orders` integer, nullable
    - `seller_live_gmv` number, nullable
    - `seller_live_orders` integer, nullable
    - `product_card_gmv` number, nullable
    - `product_card_orders` integer, nullable
    - `shop_tab_gmv` number, nullable: Overlaps the four channels; not part of the sum.
    - `other_gmv` number, nullable: gmv minus the four exclusive channels.

## Other responses

- `422`: Validation Error

## Changes

> 76 revisions in range; 1 not diffed.

- **2026-10-20** `0dba7d760bdd`: 1 warning
  - added the new `deleted` enum value to the `products/items/listing_status/anyOf[subschema #1]/` response property for the response status `200`
- **2026-09-16** `30ad5ba59c8e`: 6 info
  - added the new optional request property `include_inactive`
  - added the optional property `listing_data_available` to the response with the `200` status
  - added the optional property `products/items/listing_status` to the response with the `200` status
  - added the optional property `products/items/price_currency` to the response with the `200` status
  - …2 more
- **2026-09-09** `a5d66477ac03`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/reacherapp/apis/reacher-data-api/changes/shop-performance/products/post.md)

---

[API](https://skmtc.dev/reacherapp/apis/reacher-data-api.md) - [All operations](https://skmtc.dev/reacherapp/apis/reacher-data-api/llms.txt) - [OpenAPI document](https://skmtc.dev/reacherapp/apis/reacher-data-api/revisions/63ce74b30b5a?raw)
