# TikTok Shop Sales Tracker: Units Sold per Day & Top Products (`cleanpull/tiktok-shop-sales-tracker`) Actor

Know what's selling on TikTok Shop and how fast: exact units sold, units and revenue per day, rising products and GMV for every product, keyword, shop and category you track. Whole shop catalogues, deep keyword lists, stock per variant, filters, changes only. US, from $3 per 1,000 products.

- **URL**: https://apify.com/cleanpull/tiktok-shop-sales-tracker.md
- **Developed by:** [CleanPull](https://apify.com/cleanpull) (community)
- **Categories:** E-commerce, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## TikTok Shop Sales Tracker: Units Sold per Day & Top Products

**Know what's selling on TikTok Shop, and how fast.** This actor reads TikTok Shop US products by keyword, shop, category or URL with TikTok's **exact units sold** (9,857, not "9.8K"), and turns every run into **sales velocity**: units sold per day, revenue per day and a rising, steady or falling trend for every product. It also adds the totals for every keyword, shop and category you track. Run it once for a snapshot with **lifetime GMV estimates**. Schedule it and it becomes your own TikTok Shop sales tracker, from **$3 per 1,000 products**.

### What you get

- **Units sold per day for every product.** Each run remembers every product's sold count. The next run gives `unitsSoldSinceLastRun`, `unitsPerDay` and `revenuePerDayEstimate`. From the third run, `trend` says whether sales are speeding up or slowing down.
- **A sales leaderboard for every keyword, shop and category.** One summary row each, with units and revenue per day in total, the top 10 sellers, the products whose sales are rising, and products new to that keyword or shop since your last run.
- **A shop's daily sales, from its whole catalogue.** Give a store and the actor reads every product, up to 20,000. The shop summary adds up their sales, so you see the shop's units and revenue per day as they happen.
- **Lifetime GMV estimates.** `gmvEstimate` (units sold × price) for every product, and `gmvEstimateTotal` with a top 10 by GMV for every keyword, shop and category.
- **Deep keyword lists.** Each keyword is followed through TikTok's "View more" pages to the end of the list, typically 100 to 140 products for popular terms (raise `maxProductsPerKeyword` above its default of 100 to get them all), each with its exact sold count.
- **Sale and deal data on every row:** price, original price, discount %, when the deal ends, free shipping and TikTok's sale labels.
- **Product details when you want them:**
  - stock and price for every variant, with the stock change since the last reading with stock and a low-stock flag;
  - the product's own video, the description and specifications;
  - delivery fee and days, categories with IDs;
  - the star breakdown and featured reviews (verified and incentivized flags);
  - the seller's business name and address;
  - the shop's badge, totals and service scores (positive feedback, on-time delivery, chat).
- **One row per product.** A product found by two keywords and a shop is delivered once. It lists every source, its position there and its rank by units per day.
- **Filters that run before you pay:** price range, minimum rating, reviews, units sold or units per day; discounted or free-shipping only; title words to include or exclude; sellers to include or exclude. Filtered products are not billed. Filters apply to products found through keywords, shops and categories; products you list directly are always delivered.
- **Changes only.** Deliver just the products that sold, changed price or are new. Unchanged products cost $0.50 per 1,000, so a daily tracker stays cheap.

### Who it's for

- **Product researchers and dropshippers:** find what's taking off this week, not what sold the most since launch.
- **Brands and sellers:** track your products and your competitors' daily, with price moves and stock.
- **Agencies and analysts:** size a niche by keyword (units and revenue per day, lifetime GMV), compare shops, and feed a dashboard.

### Built to be relied on

- **Exact numbers.** Sold counts come straight from TikTok Shop's own data, as exact integers. TikTok's note says they count items sold since listing, excluding returns.
- **You never pay for a skipped day.** A reading becomes the baseline for next time only once it is delivered, so a run cut by your spending limit or `maxItems` never loses sales.
- **Runs that finish.**
  - A keyword TikTok has no page for, or a removed product, is noted in the run summary, and everything else is delivered.
  - Refused requests are retried automatically.
  - An empty input runs an example keyword, so you can see the output first.
- **Fast and light.** Plain HTTP, no browser.

### Input

| Field                                                        | Type             | What it does                                                                                                                     |
| ------------------------------------------------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `keywords`, `maxProductsPerKeyword`                          | array, integer   | Shopping terms; products per keyword (default 100, up to 1,000)                                                                  |
| `shops`, `maxProductsPerShop`                                | array, integer   | Store URLs (`shop.tiktok.com/us/store/name/ID`) or seller IDs; products per shop (default 200, up to 20,000)                     |
| `products`                                                   | array            | Product URLs (`…/pdp/name/ID`, `…/view/product/ID`) or IDs, read from their product pages                                        |
| `categoryUrls`, `maxPagesPerCategory`                        | array, integer   | Category pages (`shop.tiktok.com/us/c/name/ID`), 15 products a page; best for discovery, as category lists vary                  |
| `startUrls`                                                  | array            | Any mix of product, store, category and keyword URLs                                                                             |
| `datasetId`, `datasetField`                                  | string           | Products or shops from another dataset, such as a previous run                                                                   |
| `monitorName`                                                | string           | Units per day are measured against the last run with this name; use one name per list you track                                  |
| `changesOnly`                                                | boolean          | Only products that sold, changed price or are new (stock changes too, for products you list directly)                            |
| `minPrice`, `maxPrice`, `minRating`, `minReviews`            | number           | Filters on price (USD), rating (0–5) and review count                                                                            |
| `minUnitsSold`, `minUnitsPerDay`                             | number           | Filters on lifetime units sold and on units per day (products without a velocity yet are kept)                                   |
| `onlyDiscounted`, `onlyFreeShipping`                         | boolean          | Only products on sale, or with free shipping                                                                                     |
| `titleIncludes`, `titleExcludes`                             | array            | Words the title must contain, or must not contain                                                                                |
| `sellers`, `excludeSellers`                                  | array            | Seller IDs or shop names to keep, or to leave out                                                                                |
| `includeProductDetails`, `lowStockThreshold`                 | boolean, integer | Product pages: variants with stock, video, description, delivery, reviews, seller and shop details; low-stock level (default 10) |
| `includeSummaries`                                           | boolean          | One summary row per keyword, shop and category, and one for your listed products (default on)                                    |
| `maxItems`, `fields`, `maxConcurrency`, `proxyConfiguration` |                  | Limits and tuning; when `maxItems` cuts the list, the fastest sellers are kept                                                   |

#### Example inputs

What's selling fastest in two niches, every day (schedule it daily):

```json
{
  "keywords": ["lip gloss", "protein powder"],
  "maxProductsPerKeyword": 200,
  "monitorName": "beauty-daily"
}
```

A whole shop's daily sales:

```json
{
  "shops": ["https://shop.tiktok.com/us/store/style-rule/7495210231735224924"],
  "maxProductsPerShop": 5000
}
```

Discounted products under $30 rated 4.5 and up, without refills:

```json
{
  "keywords": ["lip oil"],
  "maxPrice": 30,
  "minRating": 4.5,
  "onlyDiscounted": true,
  "titleExcludes": ["refill"]
}
```

Your products, with stock per variant, only when something changed:

```json
{
  "products": ["https://shop.tiktok.com/us/pdp/1729400216682730076"],
  "includeProductDetails": true,
  "changesOnly": true
}
```

### Output

A product row (second run, 12 hours after the first):

```json
{
  "recordType": "product",
  "productId": "1729407202568147235",
  "title": "e.l.f. Glow Reviver Lip Oil, Nourishing Lip Oil High-Gloss Finish 0.25 oz (7.6 mL)",
  "url": "https://shop.tiktok.com/us/pdp/glow-reviver-lip-oil-by-e-l-f-cosmetics-hydrating-tinted-formula/1729407202568147235",
  "image": "https://p16-oec-general-useast5.ttcdn-us.com/…/lip-oil.webp",
  "price": 13,
  "originalPrice": 16,
  "discountPercent": 19,
  "dealEndsAt": "2026-10-04T06:59:00.000Z",
  "freeShipping": true,
  "currency": "USD",
  "rating": 4.8,
  "reviewCount": 18416,
  "unitsSold": 226826,
  "gmvEstimate": 2948738,
  "unitsSoldSinceLastRun": 412,
  "hoursSinceLastRun": 12,
  "unitsPerDay": 824,
  "revenuePerDayEstimate": 10712,
  "trend": "rising",
  "previousUnitsPerDay": 610,
  "previousPrice": 13,
  "priceChangePercent": 0,
  "previousStock": null,
  "firstSeen": "2026-10-01T08:00:00.000Z",
  "tracked": true,
  "sellerId": "7494913857786775843",
  "shopName": "e.l.f. Cosmetics",
  "brand": "e.l.f.",
  "labels": ["Flash sale", "Free shipping"],
  "sources": [
    {
      "type": "keyword",
      "value": "lip gloss",
      "position": 1,
      "velocityRank": 1,
      "newInSource": false,
      "relevantToKeyword": false,
      "keywordWordsMissing": ["gloss"]
    }
  ],
  "scrapedAt": "2026-10-02T08:00:00.000Z"
}
```

With `includeProductDetails`, a `details` object adds:

- **Stock:** `stock`, `stockChange`, `lowStock`, and `skus` (options, price, stock, available).
- **Content:** `video` (URL, duration, cover), `description`, `specifications`, `images`.
- **Logistics:** `delivery` (free shipping, fee, minimum and maximum days, estimate), `categories`, `categoryIds`.
- **Reviews:** `ratingBreakdown` and `featuredReviews`.
- **Seller and shop:** `sellerBusinessName`, `sellerBusinessAddress`, and `shop` (units sold, products, followers, rating, badge, service scores).

**A summary row** per keyword, shop and category, and one for the products you listed directly (`type: product`):

- **Coverage:** `products`, `complete`, `productsWithVelocity`.
- **Totals:** `unitsSoldTotal`, `gmvEstimateTotal`, `unitsPerDayTotal`, `revenuePerDayEstimate`.
- **Lists:** `topByUnitsPerDay`, `topByGmv`, `rising`, `newProducts`, `topNewProducts`.
- **Shops** also get TikTok's own `shop` totals.
- **Fees:** `marketplaceFees`, TikTok Shop US's published referral fee list rate (6% for most categories, 5% for some jewelry and pre-owned), with what it leaves out (creator commissions, Fulfilled by TikTok, shipping) and its source.
- **Keywords** also get `relevantToKeyword`: the same totals (`products`, `productsWithVelocity`, `unitsSoldTotal`, `gmvEstimateTotal`, `unitsPerDayTotal`, `revenuePerDayEstimate`) over the products whose title carries the keyword, with `relevanceBasis` stating the rule and `totalsCover` saying the main totals cover every product listed. The top lists mark each product `relevantToKeyword`.

**Keyword relevance.** TikTok's keyword search also lists related products (a "lip gloss" search lists lip oils, liners and blush), and its list cards carry no category, so a keyword's totals add up unrelated products. Each keyword source on a product row has `relevantToKeyword` and `keywordWordsMissing`: a plain title check, true when every word of the keyword (filler words like "for" and "the" left out) is in the title, singular or plural, or written as one ("lipgloss"). It is a clue, not a verdict: synonyms and misspellings are not matched. Nothing is filtered on it, and titles are delivered as TikTok gives them.

The run summary (key-value store, `RUN_SUMMARY`) gives the outcome (COMPLETE, PARTIAL, NO_CHANGES, EMPTY or FAILED), counts, charges, warnings and anything that could not be read.

### Pricing

| Event             | Price           | When                                                      |
| ----------------- | --------------- | --------------------------------------------------------- |
| `product`         | $3.00 per 1,000 | A delivered product row, velocity and GMV included        |
| `product-checked` | $0.50 per 1,000 | An unchanged product with Changes only                    |
| `product-details` | $1.00 per 1,000 | A product page read for `includeProductDetails`           |
| `source-summary`  | $0.001 each     | A summary row per keyword, shop, category or product list |

Products given in `products` are read from their product pages at the product price. Apify subscription discounts apply (10% Silver, 20% Gold and above).

### Good to know

- **TikTok Shop US.**
- **The first run is the baseline.** Units per day appear from the second run under the same monitor name, and the trend from the third. Schedule runs a few hours to a day apart for the steadiest numbers; runs less than 15 minutes apart count from the earlier reading.
- **GMV is an estimate:** lifetime units sold × today's price.
- **Summaries cover every product a keyword, shop or category lists,** including those your filters leave out of the product rows, so they describe the whole market. For keywords, `relevantToKeyword` gives the same totals over the products whose title carries the keyword.
- **Categories come with product details only.** TikTok's search and list cards carry no category or product type (checked on the keyword, category and shop lists); `details.categories` comes from the product page with `includeProductDetails`. A product found through a category page has that category among its `sources`.
- **Video and image links are signed by TikTok** and stop working after a few hours (`video.urlExpiresAt`); download what you want to keep.
- **Original price and discount** come from list cards (keywords, shops, categories); for products you list directly, the row shows the sale labels and the deal end.
- **Category pages vary between visits.** Use them to discover products, then track those through keywords, shops or products.
- **Reviews:** the star breakdown, the total and the featured reviews shown on the product page.
- **Nothing found**: when nothing could be read because none of it exists (a keyword, shop, category or product TikTok Shop has no page for, or a list with no products), the run still succeeds, with one free `recordType: "unreadable"` row per item giving the `reason`; nothing is charged. A run near its time limit stops starting sources and product pages, delivers what it read and lists the rest the same way.

### Changelog

- **0.2.x, reliability** (2026-10-24): runs that find nothing (keywords, shops, categories or products with no page) end SUCCEEDED with a free `unreadable` row per item saying why; a run stops starting sources before its time limit and ends with what it read, never TIMED-OUT. A run TikTok blocked on every route still fails. Prices and events unchanged.
- **0.2.x** (2026-10-23): `marketplaceFees` on summary rows: TikTok Shop US's published referral fee list rate. Prices and events unchanged.
- **0.2.x** (2026-10-16): keyword relevance: `relevantToKeyword` and `keywordWordsMissing` on each keyword source of a product row, and keyword summaries with totals over the relevant products next to the totals over all of them. Nothing changes in price, filters or existing fields.
- **0.2**: original price, discount and deal end; lifetime GMV estimates; filters (price, rating, reviews, units, discount, free shipping, title, sellers); product video, description, delivery, category IDs, seller business details, shop badge and service scores; stock change and low-stock flag.
- **0.1**: first release.

# Actor input Schema

## `keywords` (type: `array`):

One per line. Products TikTok Shop lists for each term, in TikTok's order, with exact units sold. Use real shopping terms ("lip gloss", "protein powder"); a term TikTok has no page for is skipped with a note.

## `maxProductsPerKeyword` (type: `integer`):

How many products to read for each keyword (about 30 per page; the actor follows TikTok's "View more" pages until the end of the list).

## `shops` (type: `array`):

Optional. TikTok Shop store URLs (shop.tiktok.com/us/store/name/ID) or seller IDs. Each shop's totals and its catalogue, with units sold per day for the whole shop.

## `maxProductsPerShop` (type: `integer`):

How many of each shop's products to read (20 per page). Set it above the shop's product count to cover the whole shop.

## `products` (type: `array`):

Optional. TikTok Shop product URLs (…/pdp/name/ID or …/view/product/ID) or product IDs, to track exactly these. Each is read from its product page (variants with stock included).

## `categoryUrls` (type: `array`):

Optional. TikTok Shop category pages (shop.tiktok.com/us/c/name/ID). Category lists change from visit to visit, so use them to discover products; track what you find through keywords, shops or products.

## `maxPagesPerCategory` (type: `integer`):

Category pages to read (15 products each).

## `startUrls` (type: `array`):

Optional. Any mix of TikTok Shop product, store, category and keyword URLs; each is read as its kind.

## `datasetId` (type: `string`):

Optional. Another dataset (for example a previous run) whose rows hold TikTok Shop product or store URLs or IDs.

## `datasetField` (type: `string`):

The field holding the URL or ID in that dataset. Bare IDs are read as products, or as shops when the field name mentions seller, shop or store.

## `monitorName` (type: `string`):

Units sold per day are measured against the last run with the same monitor name. Use one name per list you track; the first run under a name is the baseline.

## `changesOnly` (type: `boolean`):

Deliver only products that sold, changed price or are new since the last run (and stock changes for products listed directly, which are read from their pages). Unchanged products are billed at the lower checked rate. The first run delivers everything.

## `minUnitsPerDay` (type: `number`):

Leave out products selling fewer units per day than this. Products without a velocity yet (first seen this run) are kept. Filtered products are still read and remembered, and are not billed.

## `includeProductDetails` (type: `boolean`):

Also read each delivered product's page: stock and price per variant, specifications, all images, categories, star breakdown, featured reviews and the shop's totals. Billed per product page.

## `minPrice` (type: `number`):

Optional. Leave out products cheaper than this (USD). Filters apply to products found through keywords, shops and categories; products you list directly are always delivered.

## `maxPrice` (type: `number`):

Optional. Leave out products dearer than this (USD).

## `minRating` (type: `number`):

Optional. Leave out products rated below this (0–5).

## `minReviews` (type: `integer`):

Optional. Leave out products with fewer reviews.

## `minUnitsSold` (type: `integer`):

Optional. Leave out products that have sold fewer units in total.

## `onlyDiscounted` (type: `boolean`):

Only products on sale (TikTok shows a discount on the card).

## `onlyFreeShipping` (type: `boolean`):

Only products with free shipping.

## `titleIncludes` (type: `array`):

Optional. Words every product title must contain (case does not matter).

## `titleExcludes` (type: `array`):

Optional. Leave out products whose title contains any of these words.

## `sellers` (type: `array`):

Optional. Seller IDs, shop names or store URLs; only their products are delivered.

## `excludeSellers` (type: `array`):

Optional. Seller IDs, shop names or store URLs whose products are left out.

## `lowStockThreshold` (type: `integer`):

With product details, a product with this many units or fewer in stock is marked lowStock.

## `includeSummaries` (type: `boolean`):

One summary row per keyword, category and shop, and one for the products you list directly: units sold per day in total, the top 10 sellers, rising and new products.

## `maxItems` (type: `integer`):

Optional. Stop after this many product rows. When it cuts the list, the fastest sellers are kept.

## `fields` (type: `array`):

Optional. Only these fields in each row (dot paths like details.stock). recordType is always kept.

## `maxConcurrency` (type: `integer`):

Keywords, shops and product pages read at the same time.

## `proxyConfiguration` (type: `object`):

Optional. By default the actor picks its own route (direct, then US proxies, as needed).

## Actor input object example

```json
{
  "keywords": [
    "lip gloss"
  ],
  "maxProductsPerKeyword": 100,
  "maxProductsPerShop": 200,
  "maxPagesPerCategory": 3,
  "datasetField": "url",
  "monitorName": "default",
  "changesOnly": false,
  "minUnitsPerDay": 0,
  "includeProductDetails": false,
  "onlyDiscounted": false,
  "onlyFreeShipping": false,
  "lowStockThreshold": 10,
  "includeSummaries": true,
  "maxConcurrency": 3
}
```

# Actor output Schema

## `results` (type: `string`):

Product rows and one summary per keyword, shop and category.

## `products` (type: `string`):

No description

## `summaries` (type: `string`):

One row per keyword, shop and category; keyword rows add totals over the products whose title carries the keyword (relevantToKeyword).

## `summary` (type: `string`):

Outcome (COMPLETE, PARTIAL, NO_CHANGES, EMPTY, FAILED), counts, charges and anything that could not be read.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "lip gloss"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanpull/tiktok-shop-sales-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "keywords": ["lip gloss"] }

# Run the Actor and wait for it to finish
run = client.actor("cleanpull/tiktok-shop-sales-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "lip gloss"
  ]
}' |
apify call cleanpull/tiktok-shop-sales-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cleanpull/tiktok-shop-sales-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WkqjKRmgSafp8UYfH/builds/HWiNLrVFeg2zbkEUf/openapi.json
