# TikTok Shop Scraper: Products, Sales & Prices, No Login (`cliqtomedia/tiktok-shop-scraper`) Actor

Collect TikTok Shop products, prices, units sold, ratings, variants, shipping and shop stats from keywords, categories or links. Find what sells and track price and sales changes between runs. No login or cookies.

- **URL**: https://apify.com/cliqtomedia/tiktok-shop-scraper.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** E-commerce, Social media
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 product saveds

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

Give this Actor search keywords or public [TikTok Shop](https://shop.tiktok.com/us) links, and get one table row per product with price, units sold, rating, variants, shipping and shop stats. It needs no login or cookies, and a typical run of 10 products costs about $0.03.

![TikTok Shop Scraper: keywords or links go in, product rows and change tracking come out](https://api.apify.com/v2/key-value-stores/qDcfxcp1eBeTP0LYc/records/hero-20261104.png)

### Table of contents

- [What it does](#what-it-does)
- [How it works](#how-it-works)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Output](#output)
- [Pricing](#pricing)
- [Limits](#limits)
- [API, export and integrations](#api-export-and-integrations)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Related Actors](#related-actors)
- [Support](#support)

### What it does

You want to know which TikTok Shop products sell, at what price, and who sells them. This Actor reads the public US storefront and saves each product as one row. You can search by keyword or start from product, category or shop links. You can then filter, sort and download the rows, or run the same input again to see what changed.

#### Good fit

- You research products before you sell them.
- You compare prices and units sold across shops.
- You watch a list of products or a shop every day.
- You need public data in a spreadsheet or an API, without a TikTok account.

It is not a good fit if you need markets other than the US, every review of a product, or a sales rank across all of TikTok Shop.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### How it works

1. You add keywords or public links, and set how many products you want.
2. The Actor opens the public result pages. It opens each product too, if you keep full details on.
3. You get a table of products, a shop summary and a short run report. With **Track changes** on, each row also shows what is new or different since the last run.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Quick start

You need a free Apify account. The form below opens with a small example, so you can run it as it is.

[![The input form with Mode, Search keywords, Maximum products and Full product details marked](https://api.apify.com/v2/key-value-stores/qDcfxcp1eBeTP0LYc/records/quick-start-form-20261104.png)](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)

1. Open the [input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input). In box 1, keep **Search by keywords**.
2. In box 2, type what you want to find, one phrase per line. The example is `phone stand`.
3. In box 3, set **Maximum products**. The example is `10`.
4. In box 4, keep **Full product details** on to get variants, shipping and reviews. Turn it off for a faster and cheaper list.
5. Click **Start**. When the run ends, open the **Output** tab and download the table. Box 5 shows the main columns.

![The output table with product, price, units sold and shop columns marked](https://api.apify.com/v2/key-value-stores/qDcfxcp1eBeTP0LYc/records/output-table-20261104.png)

**Expected result:** 10 product rows in about 40 seconds, for about $0.03 on the Free plan. The products and numbers are live, so they change over time.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Use cases

| Buyer job | Input path | Output entity | Key fields | Next integration |
| --- | --- | --- | --- | --- |
| Find products that sell | Keywords, sort by **Most units sold** | One product row | `title`, `price`, `soldCount`, `rating`, `shopName` | Google Sheets, CSV |
| Check a competitor shop | **Shop links** | Product rows and a shop summary | `price`, `soldCount`, `shop`, `skus` | Excel, API |
| Watch prices and sales every day | Product links, **Track changes** on, schedule | Rows with a change status | `changeStatus`, `salesDelta`, `price`, `previousFetchedAt` | Slack, Notion, webhook |
| Explore one category | **Category links**, price and rating filters | Filtered product rows | `categories`, `price`, `rating`, `reviewCount` | Dataset API |
| Find creators who promote a product | Keywords, **Full product details** off | Rows with a promotional video | `video`, `soldCount`, `url` | CSV |

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Input

Start with **Mode** and one list of keywords or links. All other fields are optional.

#### Input fields

| Field | Plain name | Type | Required | Default or prefill | Allowed | Effect | Example | Recommendation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `mode` | Mode | Text | No | `search` | `search`, `product`, `category`, `shop` | Picks what to start from | `search` | Keep Search unless you only use links |
| `keywords` | Search keywords | List | No | Prefill `phone stand` | Any phrases | Runs one search per phrase, in Search mode only | `phone stand` | Use a few words, as a shopper would |
| `maxItems` | Maximum products | Whole number | No | `10` | 1 or more | Stops after this many saved rows, across all inputs | `50` | Start small, then raise it |
| `scanLimit` | Products to check per input | Whole number | No | `100` | 1 or more | Limits how many products each input looks through for filters, sorting and changes | `300` | Raise it when a filter finds too little |
| `includeProductDetails` | Full product details | Yes or no | No | Yes | Yes, no | Opens each product for variants, shipping and reviews | Yes | Turn off for long lists |
| `regions` | Markets | List | No | Prefill `US` | `US` | Picks the storefront | `US` | Keep US |
| `region` | Primary market | Text | No | `US` | `US` | Used when Markets is empty | `US` | Keep US |
| `monitor` | Track changes | Yes or no | No | No | Yes, no | Compares each run with the last one | Yes | Use with a schedule |
| `stateName` | Monitor name | Text | No | Made from the inputs | Letters, numbers, dashes, up to 63 | Names the saved comparison | `phone-stands-daily` | Set one name per watch list |
| `onlyChanges` | Only changed products | Yes or no | No | No | Yes, no | Saves new and changed products only | Yes | Needs Track changes |
| `outputFormat` | Output format | Text | No | `nested` | `nested`, `flat` | `flat` stores lists as JSON text in a cell | `flat` | Use `flat` for Google Sheets |
| `sortBy` | Sort products | Text | No | `relevance` | `relevance`, `sales`, `priceAscending`, `priceDescending` | Ranks the checked products, then saves the top ones | `sales` | Set `scanLimit` with it |
| `productUrls` | Product links | List | No | Empty | Public links | Adds these products in any mode | See the form | Use for a fixed watch list |
| `categoryUrls` | Category links | List | No | Empty | Public links | Adds these categories in any mode | See the form | Add filters for large categories |
| `shopUrls` | Shop links | List | No | Empty | Public links | Adds these shops in any mode | See the form | Use for competitor checks |
| `minPrice` | Minimum price | Number | No | Empty | 0 or more | Keeps this price or higher | `10` | Rows with no price are left out |
| `maxPrice` | Maximum price | Number | No | Empty | 0 or more | Keeps this price or lower | `30` | Rows with no price are left out |
| `minRating` | Minimum rating | Number | No | Empty | 0 or more | Keeps this rating or higher | `4.5` | Rows with no rating are left out |
| `minSoldCount` | Minimum units sold | Number | No | Empty | 0 or more | Keeps at least this many units sold | `1000` | Rows with no count are left out |
| `titleContains` | Title must contain | List | No | Empty | Any words | Keeps titles with all of these words | `magnetic` | Upper or lower case does not matter |
| `excludeKeywords` | Exclude title words | List | No | Empty | Any words | Leaves out titles with any of these words | `case` | Upper or lower case does not matter |
| `deliveryConnector` | Send results to | Connector | No | Empty | Your MCP connector | Sends a few lines about the finished run | Your Slack | See [Send results to Slack or Notion](#send-results-to-slack-or-notion) |
| `deliveryTarget` | Notion page or channel | Text | No | Empty | Notion link or Slack channel ID | Picks where the summary goes | A channel ID | Leave empty to let the connector decide |

**Empty input:** a run with no input searches for `phone stand` and saves up to 10 products.

**Duplicates:** the same product found twice in one run is saved and charged once.

#### Interacting settings

- Links always run, in any mode. Keywords only run in Search mode.
- Filters look only at the products an input checks. Raise `scanLimit` if a filter finds too few rows.
- `sortBy` ranks the checked products for each input, not the whole store. It also makes the Actor check up to `scanLimit` products before it saves any.
- `onlyChanges` needs `monitor`. Without it, the run stops with a clear error and only pays the start fee.
- A new `stateName` starts with no history, so every product shows as `new`.
- Full details add one paid page check per product and about 3 seconds per product.

#### Input examples

##### Find top sellers for a keyword

```json
{
  "mode": "search",
  "keywords": ["phone stand"],
  "maxItems": 5,
  "scanLimit": 50,
  "sortBy": "sales",
  "includeProductDetails": false
}
```

**Expected behavior:** the Actor checks up to 50 products, ranks them by units sold and saves the top 5.
**Expected output:** 5 rows, without variants or shipping. `scanLimitReached` is `true` if it stopped at 50.
**Limit:** this ranks the 50 checked products, not all of TikTok Shop.

##### Check one shop

```json
{
  "mode": "shop",
  "shopUrls": ["https://shop.tiktok.com/us/store/putare/7495880414145841487"],
  "maxItems": 3
}
```

**Expected behavior:** the Actor opens the shop and saves 3 products with full details.
**Expected output:** 3 rows and one shop in the `SHOPS` record.
**Limit:** a small shop can have fewer products than you ask for.

##### Track changes every day

```json
{
  "mode": "product",
  "productUrls": ["https://shop.tiktok.com/us/pdp/product/1730860052779143503"],
  "monitor": true,
  "stateName": "my-watch-list",
  "onlyChanges": true
}
```

**Expected behavior:** the first run saves the product as `new`. Later runs save it only if its title, price, units sold, rating, review count or stock changed.
**Expected output:** one row when something changed. No rows and the status `No changes since the last run.` when nothing changed.
**Limit:** page checks are paid even when nothing changed. Do not run two runs with the same `stateName` at the same time.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Output

The **Output** tab shows the products. Download them as JSON, CSV or Excel. The table has five views: **Products**, **Sales and changes**, **Product details**, **Promotion** and **All fields**.

#### Output fields

A value is `null` when TikTok Shop does not show it for that product. Without full details, `description`, `shipping` and `ratingDistribution` are `null`, and `skus` and `reviews` are empty lists.

| Field | Type | What it means | Example |
| --- | --- | --- | --- |
| `productId` | Text | TikTok Shop product ID | `1730860052779143503` |
| `title` | Text | Product title | `Remote Control for TikTok Scrolling…` |
| `price` | Number or null | Lowest current price | `20.79` |
| `currency` | Text or null | Currency of the price | `USD` |
| `originalPrice` | Number or null | Price before the discount | `45.49` |
| `discountPercent` | Number or null | Discount on the cheapest variant | `54` |
| `soldCount` | Number or null | Total units sold, as the store shows it | `11690` |
| `rating` | Number or null | Average stars, from 1 to 5 | `4.4` |
| `reviewCount` | Number or null | Number of reviews the store reports | `1497` |
| `ratingDistribution` | Object or null | Review count per star | `{"5": "1149"}` |
| `reviews` | List | A public sample of reviews, without buyer names | First page only |
| `reviewsHasMore` | Yes, no or null | More reviews exist beyond the sample | `true` |
| `shopName` | Text or null | Shop that sells the product | `PUTARE` |
| `sellerId` | Text or null | TikTok Shop seller ID | `7495880414145841487` |
| `shop` | Object or null | Shop totals: units sold, products, reviews, followers, rating | `{"sold_count": 576817}` |
| `url` | Text | Public product link | `https://shop.tiktok.com/us/pdp/…` |
| `images` | List | Gallery images. These links can expire | Image links |
| `descriptionImages` | List | Images inside the description | Image links |
| `description` | Text or null | Product description | Text |
| `specifications` | List | Name and value pairs, such as material | `Brand: PUTARE` |
| `brand` | Text or null | Brand, when the store shows one. Often empty | `PUTARE` |
| `categories` | List | Category path | `Phones & Electronics` |
| `variants` | List | Variant names and values | `Color: Green, Black` |
| `skus` | List | Price, stock and properties of each variant | Stock may be empty |
| `shipping` | Object or null | Delivery days, fee and free shipping, without a buyer address | `{"delivery_min_days": 7}` |
| `available` | Yes, no or null | The store says it can deliver the product | `true` |
| `video` | Object or null | Promotional video from search, with creator and views | `{"creatorName": "…"}` |
| `productVideo` | Object or null | Video on the product page | `{"durationSeconds": 21}` |
| `sourceGenerated` | Object | Text that TikTok Shop generated, kept apart from the description | Summary text |
| `region` | Text | Market | `US` |
| `source` | Text | Website | `shop.tiktok.com` |
| `fetchedAt` | Text | UTC time of the read | `2026-11-04T02:50:11+00:00` |
| `detailsFetched` | Yes or no | The product page was opened | `true` |
| `changeStatus` | Text or null | With Track changes: `new`, `changed`, `unchanged` or `unknown` | `changed` |
| `salesDelta` | Number or null | Units sold since the last run. Null when new or when the counter was reset | `120` |
| `salesTrend` | Text | `rising`, `flat` or `unknown` | `rising` |
| `salesCounterReset` | Yes or no | The sold counter went down between runs | `false` |
| `previousFetchedAt` | Text or null | Time of the last run that saw this product | A UTC time |

#### Output entities and surfaces

| Surface | Normal | Domain-empty | Partial | Technical failure |
| --- | --- | --- | --- | --- |
| Dataset (Products) | One row per product | No rows | The rows saved before the stop | The rows saved before the failure, or none |
| `RUN_SUMMARY` record | `status: complete` and counts | `emptyReason`: `NO_MATCHING_PRODUCTS` or `NO_CHANGES` | `status: partial`, `stopReason`, `failedInputs`, `unprocessedInputs` | `sourceError` and the same lists |
| `SHOPS` record | Shop stats by seller ID | Empty | Shops seen so far | Shops seen so far |
| Run status | SUCCEEDED | SUCCEEDED, with a message | SUCCEEDED, with a message | FAILED, with a message |

The empty cases are not errors. A search with no results and a filter with no matches both end as SUCCEEDED.

#### Output example

This row comes from the Quick start input, shortened:

```json
{
  "productId": "1730860052779143503",
  "title": "Remote Control for TikTok Scrolling, Wireless Page Turner & Selfie with Smartphone Holder Stand",
  "price": 20.79,
  "currency": "USD",
  "originalPrice": 45.49,
  "discountPercent": 54,
  "soldCount": 11690,
  "rating": 4.4,
  "reviewCount": 1497,
  "shopName": "PUTARE",
  "sellerId": "7495880414145841487",
  "url": "https://shop.tiktok.com/us/pdp/remote-control-by-putare-for-tiktok-kindle-with-wireless-page-turner/1730860052779143503",
  "region": "US",
  "source": "shop.tiktok.com",
  "brand": "PUTARE",
  "variants": [{ "name": "Color", "values": ["Green", "Purple", "Black", "Pink"] }],
  "shipping": {
    "free_shipping": false,
    "reachable": true,
    "delivery_min_days": 7,
    "delivery_max_days": 10,
    "delivery_name": "Standard shipping",
    "shipping_fee": { "price_str": "$5.99" }
  },
  "ratingDistribution": { "1": "111", "2": "51", "3": "68", "4": "118", "5": "1149" },
  "shop": {
    "sold_count": 576817,
    "on_sell_product_count": 7,
    "review_count": 63638,
    "followers_count": "41189",
    "video_count": "1219",
    "shop_rating": "4.5"
  },
  "changeStatus": null,
  "salesDelta": null,
  "salesTrend": "unknown",
  "detailsFetched": true
}
```

#### Send results to Slack or Notion

You can get a short summary after each run, with the run status and the first products.

1. Connect Slack, Notion or another service once in Apify Console, under **MCP connectors**.
2. Pick the connector in **Send results to**.
3. Optional: in **Notion page or channel**, paste the link of your Notion page, or the ID of your Slack channel. Each run is added on top of a Notion page.

If sending fails, your data stays safe in the Dataset and the run keeps its status. `RUN_SUMMARY` shows the result under `delivery`.

#### Use case → fields

| Use case | Fields to use |
| --- | --- |
| Product research | `title`, `price`, `soldCount`, `rating`, `reviewCount` |
| Competitor shop check | `shopName`, `shop`, `price`, `soldCount`, `skus` |
| Daily price and sales watch | `changeStatus`, `salesDelta`, `salesTrend`, `price`, `previousFetchedAt` |
| Listing analysis | `description`, `specifications`, `variants`, `images`, `reviews` |
| Creator research | `video`, `soldCount`, `url` |

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Pricing

You pay per event, and Apify platform usage is included in these prices.

#### Pricing unit

| Event | Free plan price | When you pay |
| --- | --- | --- |
| Actor start | $0.002 | Once per run. Apify charges it per GB of memory, and this Actor uses at most 512 MB, so you pay it once |
| Product | $0.002 | For each saved row |
| Page check | $0.0005 | For each result page or product page the Actor opens. Automatic retries of the same page are free |

On paid Apify plans, product and page check cost 10% to 30% less, and the Actor start price stays the same. The Store page shows the price for your plan.

- **Empty run:** a search with no results pays the start fee and its page check.
- **Filter with no matches:** you pay the start fee and each checked page, but no product.
- **Unchanged monitor:** you pay the start fee and the page checks, but no product.
- **Partial or failed run:** you pay for rows already saved and pages already opened. Rows stay in the Dataset.
- **Spending limit:** set **Maximum cost per run** in the run options. The Actor saves rows up to that limit and stops as a partial run. The lowest limit it accepts is $0.005.

#### Cost examples

These are Free plan prices.

| Run | Events | Cost | Source |
| --- | --- | --- | --- |
| Quick start: 10 products, full details | 1 start, 10 products, 11 page checks | $0.0275 | Measured |
| One product link | 1 start, 1 product, 1 page check | $0.0045 | Measured |
| Search with no results | 1 start, 1 page check | $0.0025 | Measured |
| Filter with no matches | 1 start, 2 page checks | $0.003 | Measured |
| Monitor, 3 products, no changes | 1 start, 4 page checks | $0.004 | Measured |
| 110 products, full details | 1 start, 110 products, 115 page checks | $0.2795 | Measured |
| 114 products, details off | 1 start, 114 products, 6 page checks | $0.233 | Measured |
| 200 products from 5 keywords, details off | 1 start, 200 products, 10 page checks | $0.407 | Measured |
| 1,000 products, full details | 1 start, 1,000 products, about 1,050 page checks | About $2.53 | Estimate |

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Limits

- **Market:** only the US storefront is verified. Any other market, in **Markets** or in a link, stops the run with an error before any page is paid for.
- **Speed:** full details take about 3 seconds per product. With the default **Timeout** of 300 seconds, three tests stopped at 89, 96 and 110 products. The source can block a run sooner when many runs go at the same time. The run then stops as partial and keeps the saved rows. For more, raise Timeout or turn off full details. With details off, 114 products took 24 seconds.
- **Inputs run in order:** in one test with 4 inputs and 300 seconds, the first keyword used the whole time and saved 89 products. The other inputs are listed in `unprocessedInputs`, and the run ends as a partial SUCCEEDED. Use a longer Timeout for many inputs.
- **One keyword has a limited list:** TikTok Shop gave 50 to 114 different products for one keyword on the test dates. Add more keywords or category links to get more.
- **Products to check:** `scanLimit` is 100 by default, per input.
- **Retries:** each page gets up to 3 tries. A product page that still fails keeps its row from the list, with `detailsFetched: false` and a warning. After 3 blocked product pages in a row, or one blocked list page, the run stops as partial and keeps the saved rows. A bad link goes to `failedInputs`, and the next input runs.
- **Reviews:** you get the public sample and the star counts, not the full history.
- **Sales rank and videos:** there is no rank across all of TikTok Shop. `video` shows the one video that search gives, not every promo video.
- **Sales numbers:** `soldCount` is the counter that the store shows. `salesDelta` is the difference we saw between two runs, not a verified sales report.
- **Stock and shipping:** these depend on the storefront, with no buyer address. Stock may be empty.
- **Image and video links** can expire. Save the files soon if you need them.
- **Monitor:** the comparison is kept in a named store under your Apify account. If the name is taken by another store, the Actor uses a new name and writes a warning.
- **Source terms:** this Actor reads public pages only. TikTok's [Terms of Service](https://www.tiktok.com/legal/page/us/terms-of-service/en) limit automated collection without written approval. You are responsible for how you use the data, so check these terms and your local law first.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### API, export and integrations

**API:** start a run and get the rows in one call. Use your Apify API token in the `Authorization` header.

```bash
curl -X POST "https://api.apify.com/v2/acts/cliqtomedia~tiktok-shop-scraper/run-sync-get-dataset-items?timeout=120" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["phone stand"],"maxItems":10}'
```

**Export:** save the Dataset in JSON, CSV, Excel, XML or HTML format. Use **Flat cells** for Google Sheets.

**Scheduling:** create a Schedule in Apify Console, with **Track changes** on and a fixed **Monitor name**.

**Webhooks:** add a webhook to your Task or run, and call your own link when the run ends.

**SDK:** use the [Apify client](https://docs.apify.com/api/client/python/docs) for Python or JavaScript.

#### Use with AI agents (MCP)

Add this Actor to any MCP client with this link:

```text
https://mcp.apify.com?tools=cliqtomedia/tiktok-shop-scraper
```

The smallest useful input:

```json
{ "keywords": ["phone stand"], "maxItems": 5, "includeProductDetails": false }
```

Read these fields first: `title`, `price`, `soldCount`, `rating`, `url`. A run like this takes about 5 seconds and costs about $0.013. It reads the US storefront only, and the limits above apply.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Troubleshooting

#### The run succeeded but there are no rows

**Likely reason:** TikTok Shop has no results for the search, or your filters removed everything.
**Check:** `emptyReason` and `scanLimitReached` in `RUN_SUMMARY`.
**Action:** use wider words, or remove a filter, or raise **Products to check per input**.

#### I got fewer rows than I asked for

**Likely reason:** the list for this keyword ended, or the Timeout ended, or your spending limit ended.
**Check:** `stopReason` and `unprocessedInputs` in `RUN_SUMMARY`.
**Action:** add keywords, or raise Timeout, or raise **Maximum cost per run**.

#### A link is in `failedInputs`

**Likely reason:** the product is gone, or the link is not a public TikTok Shop page.
**Check:** paste the link into an incognito window.
**Action:** remove the link. The other inputs still run.

#### The run failed

**Likely reason:** the input is not valid, or TikTok Shop blocked the run before any product was saved.
**Check:** the status message, and `sourceError` in `RUN_SUMMARY`.
**Action:** fix the input, or try again later.

#### The run stopped early with `SOURCE_ERROR`

**Likely reason:** TikTok Shop answered with a security check or a network error. It is rare, and more likely when you start many runs at once.
**Check:** `stopReason`, `sourceError` and `unprocessedInputs` in `RUN_SUMMARY`.
**Action:** the saved rows stay in the Dataset. Run the unprocessed inputs again, one run at a time.

#### `salesDelta` is empty

**Likely reason:** this is the first run for the product, or `salesCounterReset` is `true`.
**Check:** `changeStatus` and `previousFetchedAt`.
**Action:** run again with the same **Monitor name**.

#### Brand or stock is empty

**Likely reason:** the store does not show this value for the product.
**Check:** open the product link.
**Action:** use `shopName` or `specifications` instead.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### FAQ

**Do I need a TikTok account or cookies?**
No. Only public pages are read, so you stay logged out.

**Do I need my own proxy?**
No. The Actor uses Apify proxy, and the price includes it.

**Can I get markets other than the US?**
Not in this version. Only the US storefront is verified.

**Is `soldCount` the real number of sales?**
It is the counter that TikTok Shop shows on the page. We do not verify it.

**How do I get only what changed?**
Turn on **Track changes** and **Only changed products**, and keep the same **Monitor name** in each run.

**Why did I pay when I got no rows?**
The page checks were still done. An empty result costs $0.0025 to $0.004 in most cases.

**How many products can I get?**
It depends on Timeout and on how many products the source lists. See [Limits](#limits). The most we saved in one test run is 200 products, from several keywords.

**Does it collect buyer data?**
No. Reviews come without buyer names, and the Actor does not collect personal contacts.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Related Actors

- [TikTok Transcript Scraper](https://apify.com/cliqtomedia/tiktok-transcript): get the text of a product video that you found here.
- [AliExpress Scraper](https://apify.com/cliqtomedia/aliexpress-scraper): compare a product with its supplier prices.
- [Shopify Store Products Export](https://apify.com/cliqtomedia/shopify-products-scraper): compare a TikTok shop with a brand's own store.
- [Google Trends Scraper](https://apify.com/cliqtomedia/google-trends-scraper): check if interest in a product is growing.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

### Support

Open the **Issues** tab on this page and write what happened. Add the run ID, the mode and the error text. Do not send API tokens, cookies or passwords.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/q2XvPzgh45ec6EFyw/input)**

# Changelog

This Actor's version history is a separate document: https://apify.com/cliqtomedia/tiktok-shop-scraper/changelog.md

# Actor input Schema

## `mode` (type: `string`):

Choose what to start from. Search uses Search keywords. Links in the fields below always run too, in any mode. Default: Search by keywords.

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

Words to search in TikTok Shop, one phrase per line. Example: phone stand. Used only in Search mode. Leave empty with no links to search for phone stand.

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

Stop after saving this many products across all inputs. Example: 50. Default: 10.

## `scanLimit` (type: `integer`):

How many products to look through for each input when filters, sorting or Only changed products need more than Maximum products. Example: 300. Default: 100. Maximum products is used if it is higher. Each results page is a paid page check.

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

Open each product for its description, variants, stock, shipping and public review sample. Each opened product is one more paid page check and takes about 3 seconds. Turn off for faster, cheaper lists. Default: on.

## `regions` (type: `array`):

Storefront markets as two-letter codes. Example: US. Only US works in this release. Any other market stops the run with an error.

## `monitor` (type: `boolean`):

Keep a baseline in a named store and compare price, units sold and stock with the previous run. Default: off.

## `stateName` (type: `string`):

Use the same name to compare repeated runs. Letters, numbers and dashes, up to 63 characters. Example: phone-stands-daily. Leave empty to name it from the inputs.

## `onlyChanges` (type: `boolean`):

With Track changes, save new and changed products only. Page checks are still paid. Default: off.

## `outputFormat` (type: `string`):

Nested JSON keeps lists and objects. Flat cells store them as JSON text, which suits spreadsheets. Default: Nested JSON.

## `sortBy` (type: `string`):

Rank the products checked for each input (see Products to check per input), then save the top ones. This does not rank the whole TikTok Shop catalogue. Default: TikTok Shop order.

## `productUrls` (type: `array`):

One public link per line. Example: https://shop.tiktok.com/us/pdp/product/1730860052779143503

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

One public link per line. Example: https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925

## `shopUrls` (type: `array`):

One public link per line. Example: https://shop.tiktok.com/us/store/putare/7495880414145841487

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

Keep products at this price or higher. Example: 10. Products without this value are left out.

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

Keep products at this price or lower. Example: 30. Products without this value are left out.

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

Keep products with this rating or higher, from 1 to 5. Example: 4.5. Products without this value are left out.

## `minSoldCount` (type: `number`):

Keep products with at least this many units sold. Example: 1000. Products without this value are left out.

## `titleContains` (type: `array`):

Keep products whose title has all of these words. Upper or lower case does not matter. Example: magnetic.

## `excludeKeywords` (type: `array`):

Leave out products whose title has any of these words. Upper or lower case does not matter. Example: case.

## `region` (type: `string`):

Used when Markets is empty. US is the only verified storefront. Default: US.

## `deliveryConnector` (type: `string`):

Your Slack, Notion or other MCP connector. After the run the Actor posts a short summary there. Connect services once in Apify Console → MCP connectors.

## `deliveryTarget` (type: `string`):

Where to post the summary: a Notion page link (each run is added on top of that page) or a Slack channel ID. Leave empty to let the connector decide.

## Actor input object example

```json
{
  "mode": "search",
  "keywords": [
    "phone stand"
  ],
  "maxItems": 10,
  "scanLimit": 100,
  "includeProductDetails": true,
  "regions": [
    "US"
  ],
  "monitor": false,
  "onlyChanges": false,
  "outputFormat": "nested",
  "sortBy": "relevance",
  "region": "US"
}
```

# Actor output Schema

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

No description

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

No description

## `shops` (type: `string`):

No description

# 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": [
        "phone stand"
    ],
    "regions": [
        "US"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/tiktok-shop-scraper").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": ["phone stand"],
    "regions": ["US"],
}

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/tiktok-shop-scraper").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": [
    "phone stand"
  ],
  "regions": [
    "US"
  ]
}' |
apify call cliqtomedia/tiktok-shop-scraper --silent --output-dataset

```

## MCP server setup

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

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/q2XvPzgh45ec6EFyw/builds/fI6AfMFh1sHhWmVP5/openapi.json
