---
title: "Catalog price per SKU, with the estimated live promotion price"
method: GET
path: "/shop-promotions/sku-prices"
tags: ["Promotion Snapshots"]
---

# Catalog price per SKU, with the estimated live promotion price

`GET /shop-promotions/sku-prices`

## Request

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

Every live SKU's catalog price from the newest stored price snapshot: sale_price (TikTok's price before any discount, from Get Product, refreshed about daily) and tax_exclusive_price (from Search Products, every 6 hours). estimated_promo_price is computed from ongoing product discounts and flash sales in the same snapshot; TikTok returns no post-promotion price. last_observed_price_change gives the two observations around the newest change, not its exact time or cause.

## Query parameters

- `product_id` string, nullable
- `sku_id` string, nullable
- `page` integer
- `page_size` integer
- `snapshot` string, nullable: The snapshot token from page 1, to read later pages from the same runs (valid 24 h).

## Response `200`

Successful Response

- SkuPricesResponse
  - `data` SkuPrice[], required
    - `shop_id` integer, required
    - `product_id` string, required
    - `sku_id` string, required
    - `seller_sku` string, nullable
    - `product_title` string, nullable
    - `product_status` string, required
    - `sale_price` string, nullable: Catalog price before any discount (Get Product).
    - `sale_price_observed_at` string, nullable
    - `tax_exclusive_price` string, nullable: Search Products' price excluding tax.
    - `currency` string, nullable
    - `price_source` string, required
    - `estimated_promo_price` EstimatedPrice: A derived live price: TikTok returns no post-promotion price, so this is computed, never observed.
      - `amount` string, nullable: Decimal string; null when no estimate can be made (see reason).
      - `currency` string, nullable
      - `method` 'promo_price' | 'percent_off', nullable
      - `basis_promotion_id` string, nullable
      - `basis_promotion_kind` 'activity' | 'coupon', nullable
      - `reason` string, nullable: Why amount is null: no_ongoing_product_promotion, unresolved_promotion_details, prices_not_in_run, sku_not_in_prices, currency_mismatch, no_sale_price, quantity_exhausted, or invalid_discount.
      - `unresolved_promotion_ids` string[]
      - `flags` string[]: e.g. quantity_exhausted on a skipped promotion, or quantity_used_unknown when a promotion with a stock limit did not report how much was used (the offer may already be gone).
      - `assumptions` string[]
      - `as_of` string, nullable: The promotions snapshot time the estimate reflects (UTC).
    - `last_observed_price_change` PriceChange: Two observations that bound a change; neither its exact time nor its cause is known.
      - `from_price` string, required
      - `to_price` string, required
      - `currency` string, nullable
      - `observed_before_at` string, required
      - `observed_after_at` string, required
  - `pagination` PublicApiCorePaginationPaginationMeta, required: Pagination metadata returned in responses.
    - `total_count` integer, required
    - `page` integer, required
    - `page_size` integer, required
    - `total_pages` integer, required
  - `coverage` ShopCoverage[], required
    - `shop_id` integer, required
    - `shop_timezone` string, required: IANA zone used for the *_shop_tz fields.
    - `promotions` DatasetCoverage, required: Which stored snapshot answered for one dataset of one shop, and how complete and fresh it is.
      - `state` 'collected' | 'degraded' | 'stale' | 'scope_missing' | 'not_collected', required: collected: a complete snapshot under 13 h old. degraded: served, but details are pending, a history branch is capped, or the newest refresh failed. stale: the newest servable snapshot is over 13 h old, or none is under 14 days (then nothing is served). scope_missing: the shop's grants cannot read this data. not_collected: no snapshot exists for this shop.
      - `reason` string, nullable: Why the state is not collected, e.g. no_grant_with_scope.
      - `run_id` string, nullable: The sync run whose snapshot was served.
      - `served_snapshot_at` string, nullable: When the served snapshot was taken (UTC).
      - `last_success_at` string, nullable: When this dataset last refreshed cleanly (UTC).
      - `history_gaps` string[]: Ended-promotion branches TikTok's 10,000-row depth cap hid, e.g. EXPIRED:FLASHSALE.
      - `unresolved_details` integer: Ongoing product-scoped promotions whose product list could not be read in the served snapshot.
    - `prices` DatasetCoverage, required: Which stored snapshot answered for one dataset of one shop, and how complete and fresh it is.
      - `state` 'collected' | 'degraded' | 'stale' | 'scope_missing' | 'not_collected', required: collected: a complete snapshot under 13 h old. degraded: served, but details are pending, a history branch is capped, or the newest refresh failed. stale: the newest servable snapshot is over 13 h old, or none is under 14 days (then nothing is served). scope_missing: the shop's grants cannot read this data. not_collected: no snapshot exists for this shop.
      - `reason` string, nullable: Why the state is not collected, e.g. no_grant_with_scope.
      - `run_id` string, nullable: The sync run whose snapshot was served.
      - `served_snapshot_at` string, nullable: When the served snapshot was taken (UTC).
      - `last_success_at` string, nullable: When this dataset last refreshed cleanly (UTC).
      - `history_gaps` string[]: Ended-promotion branches TikTok's 10,000-row depth cap hid, e.g. EXPIRED:FLASHSALE.
      - `unresolved_details` integer: Ongoing product-scoped promotions whose product list could not be read in the served snapshot.
    - `last_attempt_at` string, nullable: When the sync last tried this shop (UTC).
    - `last_attempt_outcome` string, nullable
  - `not_covered` NotCovered[], required
    - `kind` string, required
    - `reason` string, required
  - `snapshot` string, required

## Other responses

- `422`: Validation Error

## Changes

> 76 revisions in range; 1 not diffed.

- **2026-10-26** `e153bb966d50`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/reacherapp/apis/reacher-data-api/changes/shop-promotions/sku-prices/get.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)
