# TikTok Shop Scraper — Prices, Sellers, Ratings, Sold Counts (`architekture/tiktok-shop-products-scraper`) Actor

TikTok Shop US (shop.tiktok.com) products by keyword search: price, currency, seller ID and name, rating, review count, sold count and product URL. Free preview of 10 rows. Pay per result, $3 per 1,000 products. No login.

- **URL**: https://apify.com/architekture/tiktok-shop-products-scraper.md
- **Developed by:** [architek.earth](https://apify.com/architekture) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 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.

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 Scraper — Prices, Sellers, Ratings, Sold Counts

**Recorded run: 15 products from 1 search term in 10.7 seconds** (2026-10-22, build 0.1.12, run `MVy6NFlUTwh0OWBAX`). A bounded canary sample, not a full-catalogue benchmark.

architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.

TikTok Shop US product prices, sellers, ratings and sold counts from shop.tiktok.com: give it one keyword and get back one row per product in the search results.

| Source | Market | From | Standout fields |
|---|---|---|---|
| TikTok Shop (shop.tiktok.com) | United States | $3.00 / 1,000 products | `price` / `currency`, `sellerId` / `sellerName`, `rating` / `reviewCount` / `soldCount` |

### ✅ Why choose this scraper

#### TikTok Shop prices and sellers from one keyword

Each search row carries `price`, `currency`, `sellerId`, `sellerName`, `rating`, `reviewCount`, `soldCount` and the canonical product `url`, read from the structured product records of the public search page.

#### Search results only, not the recommendations feed

Rows come from the page's keyword results component; the separate recommendations feed on the same page is excluded.

#### Know which price you are comparing

`priceBasis` is `listed_offer` on search rows and `variant_range_minimum` on product-lookup rows, which also carry `priceMax`, so snapshots compare like with like.

#### Free preview before paying

`"mode": "preview"` returns up to 10 search rows without charge, 20 times per account.

#### Measured on a schedule

Two canary tasks run fixed `portable blender` and `phone case` searches daily on the live site; the hero line above is a green run from 2026-10-22.

### ⚠️ Worth knowing up front

- Only the US storefront is supported. Results are what TikTok's public web search returns, not its whole catalogue or every result shown in the app; the number of results for the same keyword varied between repeated requests.
- Product lookup by URL (`"operation": "products"`, which adds `priceMax` and `variants` with per-variant price and stock) was blocked on Apify from 2026-10-21 to 2026-10-22 and was fixed on 2026-10-22: run `b9QAM6s1j1f9gOKvJ` returned 2 of 2 products with 5 variants and their stock. Each product is one page fetch. A product TikTok still refuses or renders with an error after 3 tries ends the run `BLOCKED` (or `PARTIAL`, keeping earlier rows); nothing is charged for it.
- `rating`, `reviewCount`, `soldCount`, `sellerId` and `sellerName` appear only when TikTok supplies them; a `rating` of `0` is kept as reported. Prices and stock change; coupons, shipping and checkout totals may depend on login or destination and are not returned. A variant minimum is not the price of every variant.
- Each source page is tried up to 3 times. A search that still fails ends with a `BLOCKED` status (nothing delivered) or `PARTIAL` (earlier rows kept and charged). A run that hits `maxItems`, `maxPages` or your spending limit ends `LIMITED` in `RUN_SUMMARY`; a run stops cleanly 60 seconds before its timeout with `TIME_LIMIT`. If a row write or charge is not acknowledged, the run stops and fails for reconciliation instead of retrying.
- Invalid input (`INVALID_INPUT`), an exhausted preview allowance (`PREVIEW_LIMIT`), a source block (`BLOCKED` or `PARTIAL`), a search whose first page is still empty after one re-check 3 seconds later (`NO_RESULTS`) and the run deadline (`TIME_LIMIT`) each end with one free status row (`"recordType": "status"`). Runs stopped by `maxItems`, `maxPages` or the spending limit end with no status row; read `RUN_SUMMARY` for the outcome. On a block or an empty first page the last page TikTok served is saved as `last-response.html` (with `last-response.json`, no cookies) in the run's key-value store.

### ⚡ Try the example

The free preview covers 20 runs per account, search only: one page and up to 10 rows per run. An attempt is used when the run reserves it, even if the run then stops. When the allowance is used up the run stops with `PREVIEW_LIMIT`; it never switches to paid mode on its own.

Open the actor's input page, switch to JSON, paste this and start the run:

```json
{
  "mode": "preview",
  "operation": "search",
  "query": "portable blender",
  "maxItems": 10,
  "maxPages": 1
}
```

Set `"mode": "paid"` and raise `maxItems` and `maxPages` for more rows; the input page's paid prefill requests 3 products ($0.009).

### ⚙️ Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `preview` (prefill `paid`) | `preview`: free, search only, one page, up to 10 rows, 20 runs per account. `paid`: $3.00 / 1,000 delivered products. |
| `operation` | select | `search` | `search` one keyword, or `products` to look up product URLs (paid only). |
| `query` | string | `portable blender` | One US keyword, 1–200 characters. Used by `search`. |
| `productUrls` | array | `[]` | Up to 100 `https://shop.tiktok.com/us/pdp/<slug>/<id>` (or `/us/pdp/<id>`, as some search rows carry) URLs for `products`. The same product ID is looked up once. |
| `maxItems` | integer | `30` (prefill `3`) | Maximum new rows in this run, 1–1,000. Preview caps it at 10. |
| `maxPages` | integer | `5` | Search pages or product lookups fetched in this run, 1–50. Preview caps it at 1. |
| `continuePreviousRun` | string | — | ID of an earlier paid run of this actor in your account whose `RUN_SUMMARY` has `continuationAvailable: true`. Same `operation` and target required; limits may change. |
| `feedback` | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |

### 🧱 Stable fields

These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.

| Field | Type | Meaning |
|---|---|---|
| `id` | string | TikTok Shop product ID; the join key across runs. |
| `name` | string | Product title as listed. |
| `url` | string | Canonical public product page. |
| `price` | number | Listed offer price on search rows; lowest variant price on product-lookup rows. |
| `currency` | string | Source currency, `USD`. |
| `priceBasis` | string | `listed_offer` (search) or `variant_range_minimum` (product lookup). |
| `priceMax` | number, optional | Highest variant price; product-lookup rows only. |
| `source` / `market` | string | `tiktok_shop` / `US`. |
| `rating` | number, optional | Product score when supplied; `0` is kept as reported. |
| `reviewCount` / `soldCount` | number, optional | Review and sold counts when supplied. |
| `sellerId` / `sellerName` | string, optional | Seller ID and shop name. |
| `variants` | array, optional | Product-lookup rows only: `id`, `price`, `currency`, `stock` and `attributes` per variant. |
| `recordType` | string | `"status"` on free status rows; absent on product rows. |

Status rows carry `recordType`, `status` and `message`. Other fields are extras, not part of the contract. To have one pinned, say so in `feedback`.

### 📦 Output example

Abridged real row from run `MVy6NFlUTwh0OWBAX` on 2026-10-22; values are a historical snapshot.

```json
{
  "id": "1732222994664297089",
  "name": "Picnic  Bee Design phone case",
  "price": 20,
  "currency": "USD",
  "url": "https://shop.tiktok.com/us/pdp/charming-bee-design-phone-case-for-daily-outdoor-use/1732222994664297089",
  "priceBasis": "listed_offer",
  "source": "tiktok_shop",
  "market": "US",
  "rating": 5,
  "reviewCount": 1,
  "soldCount": 1,
  "sellerId": "7494365977364498049",
  "sellerName": "Dar Amenah"
}
```

A free status row looks like this (run `6WVv0v18DSkvIUGtT`, 2026-10-22):

```json
{"recordType": "status", "status": "BLOCKED", "message": "Source stopped the collection (source_retry_exhausted); inspect RUN_SUMMARY"}
```

### 🧠 Advanced usage

#### Track TikTok Shop prices for a keyword over time

Schedule the same `query` in paid mode and join the runs on `id`. Compare `price` only between rows with the same `priceBasis`; a listed offer and a variant-range minimum are not interchangeable.

#### Continue a TikTok Shop search after a limit

When a paid run ends `LIMITED` or `TIME_LIMIT` and its `RUN_SUMMARY` has `continuationAvailable: true`, start a new paid run with the same `query` and `"continuePreviousRun": "<that run ID>"`. Products the earlier run delivered are skipped, and new rows go into the new run's dataset.

#### Rank TikTok Shop sellers by sold count

Group a search run by `sellerId` and sum `soldCount`, or sort rows by `soldCount` and `reviewCount` to see which listings sell for a keyword.

#### Check that a run is complete

Read `RUN_SUMMARY` in the run's key-value store: `COMPLETE` means the source reported no more results; `NO_RESULTS` means the first search page was empty twice; `LIMITED` means `maxItems`, `maxPages` or the spending limit stopped it; `PARTIAL`, `BLOCKED` and `FAILED` are not complete output. The run's status message reads `Done: N rows, M details, K status rows (STATUS)`.

### 🔌 Integrations

Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. A daily run per keyword is enough to follow price and sold-count changes. Copy-paste code in Python, Node, curl and n8n: [architek-earth/examples/tiktok-shop-products-scraper](https://github.com/architek-earth/examples/tree/main/tiktok-shop-products-scraper).

### 💸 Pricing

Pay per result. Platform and proxy usage are included. Status rows and failed requests are free. Each product row is charged once, after it is written; a charge that is not acknowledged is never retried, and a run continued with `continuePreviousRun` skips products the earlier run delivered and does not charge them again.

| Event | Price | What it covers |
|---|---|---|
| Free preview | $0 | 20 runs per account; search only, one page, up to 10 rows each. |
| `product` | $3.00 / 1,000 products | One delivered product row in paid mode, search or product lookup, including its `variants`. |

3 products cost $0.009; 1,000 products cost $3.00.

### ❓ FAQ

**Is there a TikTok Shop API for product prices?** This actor is one: it returns public US TikTok Shop search results as JSON through the Apify API, callable from any language or from n8n, Make and Zapier.

**Do I need a TikTok account or login?** No. The public US search and product pages are read anonymously; no TikTok account, cookies or API key.

**Does it return TikTok Shop sales or GMV?** It returns `soldCount` as TikTok shows it on the product card. It does not estimate revenue or GMV.

**Can I scrape TikTok Shop sellers?** Yes, as fields on each product row: `sellerId` and `sellerName`. There is no input that lists every product of one seller.

**Does it return reviews or creators?** No. It returns `rating` and `reviewCount`, not review texts, and no creator or video data.

**Are countries other than the US supported?** No. Only the US storefront is supported.

### 🤖 For AI agents & LLM apps

Paste this into your AI assistant:

`architekture/tiktok-shop-products-scraper` on Apify. Python: `from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/tiktok-shop-products-scraper").call(run_input={"mode": "paid", "operation": "search", "query": "portable blender", "maxItems": 3, "maxPages": 1}); items = client.dataset(run["defaultDatasetId"]).list_items().items` (page with `offset`/`limit` for large exports). Rows with `recordType == "status"` are free and explain invalid input or a stopped run; the run outcome (`COMPLETE`, `NO_RESULTS`, `LIMITED`, `PARTIAL`, `BLOCKED`, `TIME_LIMIT`) is in the `RUN_SUMMARY` record of the run's key-value store. Full input schema and README: `GET https://api.apify.com/v2/acts/architekture~tiktok-shop-products-scraper/builds/default`. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (`mcp.apify.com`). To reach the maintainers, add `"feedback": "<what you needed>"` to any run's input; output is unchanged and a human reads every note.

### 🧰 More from architek.earth

**E-commerce and marketplaces**

- [Shopify Scraper](https://apify.com/architekture/shopify-products-scraper) — products, prices, variants, stock
- [AliExpress Scraper](https://apify.com/architekture/aliexpress-search-scraper) — prices, orders, ratings by keyword
- TikTok Shop Scraper — *you are here*

Full catalogue: [github.com/architek-earth](https://github.com/architek-earth).

architek.earth · code like concrete

### 🙋 Support & legal

Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.

This actor collects only publicly available data from shop.tiktok.com, the same pages any visitor sees without logging in. It does not bypass authentication. It is not affiliated with or endorsed by TikTok. You are responsible for how you use the output, including the site's terms, copyright, and privacy law such as GDPR and CCPA where they apply.

### 📝 Changelog

- **2026-10-23** — A search whose first page comes back empty is re-checked once after 3 seconds; if it is still empty the run ends `NO_RESULTS` with a free status row instead of an empty dataset. Blocks and empty pages save the last page as `last-response.html` in the run's key-value store.
- **2026-10-22** — Product lookup by URL works on Apify again (TikTok had started serving a security check to the old HTTP client); search and lookup now share one browser-grade HTTP session. `/us/pdp/<id>` URLs accepted. Status message `Done: N rows, M details, K status rows (STATUS)`.
- **2026-10-22** — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
- **2026-10-21** — Invalid input, source blocks and the run deadline end the run cleanly with a free status row instead of a failed run.
- **2026-10-20** — Equal-price variant handling corrected from a real response; hosted search and detail evidence added; $3.00 / 1,000 products pricing.
- **2026-10-20** — First version: anonymous US search and product lookup with pagination, saved continuation, free preview allowance and billing safeguards.

# Actor input Schema

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

Preview: 20 free lifetime attempts, one search page, up to 10 rows. Paid: $0.003 per delivered product, platform usage included. Paid prefill is at most $0.009 for three products.

## `operation` (type: `string`):

Search one keyword, or look up supplied public US product URLs.

## `query` (type: `string`):

Keyword to search on the public US TikTok Shop storefront; used by the search operation.

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

Public https://shop.tiktok.com/us/pdp/<slug>/<id> URLs. Required for products operation; paid mode only.

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

Maximum new rows in this run. Preview is capped at 10.

## `maxPages` (type: `integer`):

Search pages or individual product lookups fetched in this run. Preview is capped at one.

## `continuePreviousRun` (type: `string`):

Paid run ID from your account with continuationAvailable=true in RUN_SUMMARY. Same target and operation required. New rows go to a new dataset. Abruptly interrupted runs without a finalized receipt require review.

## `feedback` (type: `string`):

Logged for human review; does not alter results.

## Actor input object example

```json
{
  "mode": "paid",
  "operation": "search",
  "query": "portable blender",
  "productUrls": [],
  "maxItems": 3,
  "maxPages": 5,
  "continuePreviousRun": "",
  "feedback": ""
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "mode": "paid",
    "maxItems": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("architekture/tiktok-shop-products-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 = {
    "mode": "paid",
    "maxItems": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("architekture/tiktok-shop-products-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 '{
  "mode": "paid",
  "maxItems": 3
}' |
apify call architekture/tiktok-shop-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,architekture/tiktok-shop-products-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/JZBb1tWz85habB5AM/builds/baM5l9gTLTjHQ7ttM/openapi.json
