# TikTok Shop Scraper - Products, Prices, Sales & Reviews (`abotapi/tiktok-shop-scraper`) Actor

Scrape TikTok Shop listings by keyword search, category, or direct product links. Returns title, price, discount, sold count, rating, review count, images, shop name and variations, plus with details enabled: description, rating breakdown and item-level reviews. Supports incremental monitoring.

- **URL**: https://apify.com/abotapi/tiktok-shop-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.30 / 1,000 listing results

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 Scraper: Products, Prices, Sales & Reviews

TikTok Shop Scraper turns the TikTok Shop US storefront into structured product data. Scrape a category with all its subcategories, walk the whole store, search by keyword, or paste product links. Every product comes back as one flat record with price, original price and discount, sold count, rating and review count, images, shop and category, plus, with details on, the full description, variations, rating breakdown, seller stats and recent reviews. Export to JSON, CSV or Excel, or read the results through the API.

### Why This Scraper?

- **Whole categories, not a first page.** Point it at any category and it walks every subcategory to its end, or pick the whole-store mode to cover every category.
- **Four ways in.** Category with subcategories, whole store, keyword search, or pasted product links.
- **Full product detail on request.** Description, variations, rating breakdown, seller rating and followers, and recent reviews with photos.
- **Category path on every record.** Each product carries the category trail it was found under.
- **Incremental monitoring built in.** Schedule it and get only new and changed products, unchanged ones are not billed.
- **Streams as it goes.** Results are saved category by category, so a long run keeps everything it collected.

### Use Cases

- **Ecommerce analysts:** track prices, discounts and sold counts across TikTok Shop categories on a schedule.
- **Sellers and brands:** monitor competitor catalogs, ratings and review volume in your niche.
- **Market researchers:** map a whole category tree or the full store with structured, comparable records.
- **Deal and arbitrage teams:** watch categories for new discounted products.
- **Product sourcing teams:** find fast sellers by sold count and rating before they trend elsewhere.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `productId` | `"1730000000000000001"` |
| `title` | `"Example Phone Stand, Adjustable"` |
| `url` | `"https://shop.tiktok.com/us/pdp/example-phone-stand/1730000000000000001"` |
| `price` | `13.99` |
| `currency` | `"USD"` |
| `originalPrice` | `19.99` (when discounted) |
| `discountPercent` | `30` (when discounted) |
| `soldCount` | `3500` |
| `rating` | `4.6` (empty when the product has no ratings yet) |
| `reviewCount` | `1208` |
| `images` | `["https://.../image.webp"]` |
| `shopName` | `"Example Store"` |
| `shopId` | `"7490000000000000001"` |
| `brand` | `"ExampleBrand"` (when listed) |
| `categoryPath` | `["Phones & Electronics", "Mobile Phone Accessories", "Phone Holders & Mounts"]` |
| `resultSource` | `"search"` or `"recommended"` (search mode) |
| `description` | full product description text (with details) |
| `variations` | `[{"name": "Color", "options": ["Black", "White"]}]` (with details) |
| `ratingHistogram` | `{"5": 412, "4": 88, "3": 20, "2": 6, "1": 9}` (with details) |
| `sellerRating` | `4.7` (with details) |
| `sellerProductCount` | `120` (with details) |
| `sellerFollowers` | `15400` (with details) |
| `reviews` | recent reviews: rating, date, text, photos, verified purchase (with details) |
| `searchMode` | `"browse"`, `"search"` or `"product"` |
| `scrapedAt` | `"2026-10-02T04:30:00Z"` |

When Incremental mode is on, records also carry `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`.

### How to Use

1. Pick a **mode**: `category` (a category and all its subcategories, the default), `all` (every category in the store), `search` (keywords) or `product` (pasted product links).
2. Fill in **Category links**, **Search keywords** or **Product links** for that mode. Whole-store mode needs no input.
3. Set **Max listings** and turn **Fetch product details** on or off.
4. Click **Start**, then download the dataset as JSON, CSV or Excel.

**Scrape a category with all its subcategories:**

```json
{
  "mode": "category",
  "categoryUrls": ["https://shop.tiktok.com/us/c/mobile-phone-accessories/909064"],
  "maxItems": 500
}
```

**Walk the whole store:**

```json
{
  "mode": "all",
  "maxItems": 5000,
  "fetchDetails": false
}
```

**Search by keyword:**

```json
{
  "mode": "search",
  "queries": ["phone case", "magsafe case", "iphone 17 case"],
  "maxItems": 75
}
```

**Paste product links:**

```json
{
  "mode": "product",
  "productUrls": ["https://shop.tiktok.com/us/pdp/cosrx/1731369897578565803"]
}
```

#### Run it from your code

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("abotapi/tiktok-shop-scraper").call(run_input={
    "mode": "category",
    "categoryUrls": ["https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925"],
    "maxItems": 20,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

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

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('abotapi/tiktok-shop-scraper').call({
    mode: 'category',
    categoryUrls: ['https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925'],
    maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Which mode returns how much

| Mode | Best for | How many products |
| --- | --- | --- |
| `category` | Everything in a niche | Every subcategory is walked to its end; one subcategory typically holds about 100 products |
| `all` | Store-wide catalog, trend research | Every category in the store; set **Max listings** to bound the run |
| `search` | Quick lookups by keyword | 25-40 per keyword, TikTok Shop's own limit for visitors: the keyword's own results first, then its recommendations, marked by `resultSource` |
| `product` | Known products | Exactly the links you paste |

**Max listings** is the run's cap across all categories, keywords and links (`0` = unlimited; in whole-store mode that walks the entire store and can take many hours). **Max pages** is an optional extra limit on how many result pages are loaded per category (about 10-20 products each); on its own it does not cap results.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one specific interrupted run: paste its run or dataset id and this run skips every product already in that dataset, so it returns only the remaining products and you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for a schedule against the same category, store walk or search. The first run returns everything as `NEW`; later runs return only `NEW`, `UPDATED` (with `changedFields`) and `REAPPEARED` products, while unchanged ones are suppressed and not billed unless `emitUnchanged` is on. `EXPIRED` products (no longer found) are only produced once a run has walked every tracked category to its end, never after a cap or Resume cut it short or after a keyword search (a ranked top slice is not a full scan), and only with `emitExpired` on. `stateKey` names or shares the stored state; leave it empty and the actor derives one from your setup.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `category` | `category` for a category and its subcategories, `all` for every category in the store, `search` for keywords, `product` for direct product links. |
| `categoryUrls` | array | prefill one example | Category pages to scrape (category mode). Links end in a number. |
| `includeSubcategories` | boolean | `true` | Category mode: walk every subcategory under a parent category. Off = only the linked page (about 80 products for a parent). |
| `queries` | array | prefill `["phone case"]` | Keywords to search (search mode). |
| `productUrls` | array | no default | Product links to scrape (product mode). |
| `maxItems` | integer | `20` | Maximum products returned across the run. `0` = unlimited. |
| `maxPages` | integer | `0` | Optional page limit per category; does not cap on its own. |
| `fetchDetails` | boolean | `true` | Read each product's page for description, variations, rating breakdown, seller stats and reviews. |
| `includeReviews` | boolean | `true` | Nest recent reviews under each product. Requires `fetchDetails`. |
| `resumeFromRunId` | string | no default | Continue one specific previous run, skipping products it already collected. |
| `incrementalMode` | boolean | `false` | Scheduled monitoring: return only new and changed products. |
| `stateKey` | string | no default | Name or share a monitoring campaign's saved state. |
| `emitUnchanged` | boolean | `false` | Incremental mode only: also return, and bill, unchanged products. |
| `emitExpired` | boolean | `false` | Incremental mode only: also return products no longer found after a complete scan. |
| `ignoreFieldsForChanges` | array | no default | Incremental mode only: output fields that should not mark a product as UPDATED. `images` is always ignored. |
| `proxy` | object | residential, US | Connection settings. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "productId": "1730000000000000001",
  "title": "Example Phone Stand, Adjustable",
  "url": "https://shop.tiktok.com/us/pdp/example-phone-stand/1730000000000000001",
  "price": 13.99,
  "currency": "USD",
  "originalPrice": 19.99,
  "discountPercent": 30,
  "soldCount": 3500,
  "rating": 4.6,
  "reviewCount": 1208,
  "ratingHistogram": {"5": 412, "4": 88, "3": 20, "2": 6, "1": 9},
  "images": ["https://example.com/image-1.webp"],
  "shopName": "Example Store",
  "shopId": "7490000000000000001",
  "categoryPath": ["Phones & Electronics", "Mobile Phone Accessories", "Phone Holders & Mounts"],
  "description": "Adjustable aluminium stand for phones and small tablets.",
  "variations": [{"name": "Color", "options": ["Black", "Silver"]}],
  "sellerRating": 4.7,
  "sellerProductCount": 120,
  "sellerFollowers": 15400,
  "reviews": [
    {
      "reviewId": "7600000000000000001",
      "reviewAuthor": "A**e",
      "reviewRating": 5,
      "reviewDate": "2026-09-20",
      "reviewBody": "Sturdy and easy to adjust.",
      "reviewPhotos": [],
      "verifiedPurchase": true
    }
  ],
  "searchMode": "browse",
  "scrapedAt": "2026-10-02T04:30:00Z"
}
```

### Plan Requirement

TikTok Shop serves its US storefront to US visitors. The default connection setting is already configured for that, so the actor works out of the box; keep it unless you have a reason to change it.

### FAQ

#### How much does it cost?

You pay per product returned, plus a detail charge per product when product details are on, plus a small per-run start fee. The **Pricing** tab shows the current rates. Use **Max listings** to cap the cost of any run, and turn off **Fetch product details** for the lightest runs.

#### Is it legal to scrape TikTok Shop?

This actor collects only publicly available marketplace data. You are responsible for how you use it: follow TikTok's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Reviewer names arrive masked by TikTok Shop itself.

#### Why did keyword search return fewer products than I asked for?

TikTok Shop shows visitors only its top 25-40 results per keyword, so that is the most any keyword can return. For more, add more specific keywords, or use `category` mode, which walks every subcategory to its end, or `all` mode for the whole store. The run log says so when a search ends early for this reason.

#### How many reviews do I get per product?

TikTok Shop product pages show their most recent reviews, typically the latest few rather than the full history, and that is what the actor returns under `reviews`, together with the aggregate rating, review count and rating breakdown. The dataset's **Reviews** view unwinds them to one row per review.

#### What is the difference between Resume and Incremental mode?

Resume continues one interrupted run from its run or dataset id and skips what it already collected. Incremental mode remembers products across scheduled runs by itself and returns only new and changed ones each time.

#### Can I get only new or changed products on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new, updated and reappeared products, and unchanged ones are not billed unless you turn that on too.

#### Why did my run fail instead of returning an empty dataset?

A run is marked failed only when TikTok Shop could not be loaded at all on any connection, when the Resume id you supplied is not a readable run or dataset from your account, or when a required input for the chosen mode is empty. Run it again shortly in the first case. A run that loads fine but finds nothing for your input completes normally with an empty dataset and a status message explaining why.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration, the Apify API or an MCP client that can run Apify actors.

### 🔗 Want more TikTok data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🧩 <a href="https://apify.com/abotapi/tiktok-comments-scraper"><b>TikTok Comments Scraper</b></a><br>Scrape comments from TikTok videos using one or more video URLs or IDs. Extract comment...</td><td>📱 <a href="https://apify.com/abotapi/tiktok-live-recorder"><b>Tiktok Live Recorder</b></a><br>Record TikTok live streams to MP4 with full metadata, all stream quality URLs, and...</td></tr>
<tr><td>📱 <a href="https://apify.com/abotapi/tiktok-scraper"><b>TikTok Profile, Hashtag, Search, Video &amp; Trending Scraper</b></a><br>Scrape TikTok without login. Extract profiles, bios, follower stats and videos; search by...</td><td>👗 <a href="https://apify.com/abotapi/24s-com-scraper"><b>24S Scraper</b></a><br>Scrape 24S luxury fashion by category, filters, or URL. Extract brand, name, price...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/2dehands-2ememain-scraper"><b>2dehands &amp; 2ememain</b></a><br>Scrape classifieds from 2dehands.be and 2ememain.be by keyword, filters, or URLs. Returns...</td><td>🛒 <a href="https://apify.com/abotapi/a101-product-scraper"><b>A101 Turkey Scraper</b></a><br>Scrape A101.com.tr products across A101 Ekstra and A101 Kapıda. Extract 80+ fields...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support & custom scrapers

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/tiktok-shop-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com> or message [Telegram @abotapi](https://t.me/abotapi).
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Choose 'category' to scrape a category and its subcategories, 'all' to walk every category in the store, 'search' for keyword search (top 25-40 results per keyword), or 'product' to scrape direct product links.

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

TikTok Shop category pages to scrape in category mode, for example https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925. Open a category on TikTok Shop and copy its link; it ends in a number. Paste one or more.

## `includeSubcategories` (type: `boolean`):

Category mode: when a link points to a parent category, walk every subcategory under it. A parent category page alone shows only about 80 listings; its subcategories together reach far more. Turn off to scrape only the listings shown on the linked page itself.

## `queries` (type: `array`):

One or more keywords to search on TikTok Shop, for example 'phone case' or 'yoga leggings'. Each keyword is searched separately and returns the top results TikTok Shop shows for it (25-40 per keyword).

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

TikTok Shop product links to scrape in product mode, for example https://shop.tiktok.com/us/pdp/cosrx/1731369897578565803. Paste one or more.

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

Maximum number of listings to return across the whole run. This is the run's cap. Use 0 for unlimited (in 'all' mode, unlimited walks the entire store, which can take many hours).

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

Optional extra limit on how many result pages are loaded per category (each page adds about 10-20 listings). Leave empty or 0 — it does not cap anything on its own and defers to Max listings.

## `fetchDetails` (type: `boolean`):

Open each listing's product page and extract the full detail: description, variations, rating breakdown and item-level reviews. Turn off for a faster, lighter run that returns only the listing-card fields.

## `includeReviews` (type: `boolean`):

Nest each listing's item-level reviews under its row, in addition to the aggregate rating and review count. Requires Fetch product details. Turn off if you only want the listing fields.

## `resumeFromRunId` (type: `string`):

Paste a previous run or dataset id to skip listings already collected there and return only the rest.

## `incrementalMode` (type: `boolean`):

Turn on for scheduled monitoring: later runs return only new and changed listings. Unchanged listings are suppressed and not billed.

## `stateKey` (type: `string`):

Optional name for this monitoring campaign's saved state. Use different keys to monitor different searches independently.

## `emitUnchanged` (type: `boolean`):

Incremental mode only. Also return, and bill, listings unchanged since the last run.

## `emitExpired` (type: `boolean`):

Incremental mode only. Also return, and bill, listings no longer found once a run has fully scanned the tracked search.

## `ignoreFieldsForChanges` (type: `array`):

Output field names that should NOT make a listing count as UPDATED. images is always ignored, because TikTok Shop re-renders listing image lists with unstable ordering between loads; added or removed photos still change the listing through other fields. Add fields such as description or soldCount here if changes to them are not relevant to you. Ignored fields are still returned in the output.

## `proxy` (type: `object`):

Apify Proxy connection settings. A residential exit in a supported market is used by default.

## Actor input object example

```json
{
  "mode": "category",
  "categoryUrls": [
    "https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925"
  ],
  "includeSubcategories": true,
  "queries": [
    "phone case"
  ],
  "productUrls": [],
  "maxItems": 20,
  "maxPages": 0,
  "fetchDetails": true,
  "includeReviews": true,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `reviews` (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": "category",
    "categoryUrls": [
        "https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925"
    ],
    "queries": [
        "phone case"
    ],
    "productUrls": [],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/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 = {
    "mode": "category",
    "categoryUrls": ["https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925"],
    "queries": ["phone case"],
    "productUrls": [],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/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 '{
  "mode": "category",
  "categoryUrls": [
    "https://shop.tiktok.com/us/c/cases-screen-protectors-stickers/601925"
  ],
  "queries": [
    "phone case"
  ],
  "productUrls": [],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call abotapi/tiktok-shop-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/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/xbOv0QqX8aP04peq2/builds/6kNAN65hYb2yfFJT3/openapi.json
