How to use TikTok Shop Scraper
Step by step: in the Apify Store, over the API, from JavaScript or Python, in n8n, and from AI agents through MCP or LangChain.
$22.50 per 1,000 products on the Free plan, down to $13.50 on higher plans (live Apify price on 2026-11-02).
1. In the Apify Store (no code)
- Open arcadium/tiktok-shop-products and sign in to Apify (a free account works).
- Fill in the input form. The fields below are the ones the example uses.
- Set a maximum cost per run in the run options if you want a hard cap.
- Start the run. Rows appear in the Output tab as they arrive.
- Export the dataset as JSON, CSV or Excel, or connect an integration.
| Input field | Type | Description |
|---|---|---|
startUrls | array | Product URLs or ids (https://www.tiktok.com/shop/pdp/1729541517648171786 or 1729541517648171786) give full product details. Store URLs (https://www.tiktok.com/shop/store/asterwood/7495384600901552906) read the store's products; category URLs (https://shop.tiktok.com/us/c/beauty-personal-care/601450) a sample of the category. Give at least one; no keyword search. Also accepted as url (pratikdani), productUrls / shopUrls (unseenuser), productIds, storeUrls or urls. |
maxProductsPerUrl | integer | Products read per store or category URL, e.g. 20 (the default). A store page lists its first ~20 products; a category page shows a changing sample, which is reloaded until it stops adding new products. Also accepted as maxResults (0 = 500, the maximum) or maxProducts. |
maxItems | integer | Optional cap on charged products for the whole run, e.g. 500. Empty means no cap beyond the per-URL limit. Also accepted as limit. |
Example input:
{
"startUrls": [
"https://shop.tiktok.com/us/pdp/ghk-cu-peptide-serum-by-asterwood-collagen-boosting-hydrating/1729541517648171786"
],
"maxProductsPerUrl": 20,
"maxItems": 50
}
2. Over the Apify API
The synchronous endpoint runs the actor and returns its rows in one call (for runs up to about five minutes; use the asynchronous run endpoint for longer ones).
curl -X POST "https://api.apify.com/v2/acts/arcadium~tiktok-shop-products/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"startUrls":["https://shop.tiktok.com/us/pdp/ghk-cu-peptide-serum-by-asterwood-collagen-boosting-hydrating/1729541517648171786"],"maxProductsPerUrl":20,"maxItems":50}'
3. From JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/tiktok-shop-products').call({
"startUrls": [
"https://shop.tiktok.com/us/pdp/ghk-cu-peptide-serum-by-asterwood-collagen-boosting-hydrating/1729541517648171786"
],
"maxProductsPerUrl": 20,
"maxItems": 50
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
4. From Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/tiktok-shop-products").call(run_input={
"startUrls": ["https://shop.tiktok.com/us/pdp/ghk-cu-peptide-serum-by-asterwood-collagen-boosting-hydrating/1729541517648171786"],
"maxProductsPerUrl": 20,
"maxItems": 50,
})
items = client.dataset(run.default_dataset_id).list_items().items # apify-client 3.x returns a Run object
print(items)
5. In n8n
Paste this node into a workflow (it is a plain HTTP Request node). Create a Header Auth credential with name Authorization and value Bearer <APIFY_TOKEN>; each returned row becomes one item for the next node.
{
"nodes": [
{
"name": "Run arcadium/tiktok-shop-products",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~tiktok-shop-products/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"startUrls\":[\"https://shop.tiktok.com/us/pdp/ghk-cu-peptide-serum-by-asterwood-collagen-boosting-hydrating/1729541517648171786\"],\"maxProductsPerUrl\":20,\"maxItems\":50}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}
6. From an AI assistant (MCP)
Add Apify’s MCP server to Claude Desktop, Cursor, VS Code or any MCP client, limited to this actor:
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/tiktok-shop-products",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}
More on the MCP page.
7. From LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/tiktok-shop-products")
rows = tool.invoke({"run_input": {
"startUrls": ["https://shop.tiktok.com/us/pdp/ghk-cu-peptide-serum-by-asterwood-collagen-boosting-hydrating/1729541517648171786"],
"maxProductsPerUrl": 20,
"maxItems": 50,
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])
Sample output
One row, from the example in the actor’s README. Long text is shortened and lists are cut to two items.
{
"rowType": "product",
"platform": "tiktok-shop",
"productId": "1729397565453996295",
"detailLevel": "full",
"region": "US",
"url": "https://shop.tiktok.com/us/pdp/tongue-scraper-2-pack-by-gurunanda-u-shaped-stainless-steel-with-case/1729397565453996295",
"title": "GuruNanda Tongue Scraper (2 Pack) with Travel Case, 420 Medical-grade Stainless Steel Tongue Cleaner",
"brand": "Gurunanda",
"currency": "USD",
"price": 19.49,
"priceText": "$19.49",
"soldCount": 54204,
"rating": 4.6,
"reviewCount": 3715,
"ratingCounts": {
"1": 134,
"2": 56,
"3": 175,
"4": 503,
"5": 2847
},
"categories": [
"Beauty & Personal Care",
"Nasal & Oral Care"
],
"promotions": [
"Free shipping"
],
"variants": [
{
"skuId": "1729397565454061831",
"options": {
"Specifications": "Default"
},
"price": 19.49,
"stock": 826,
"purchaseLimit": 200,
"available": true
}
],
"totalStock": 826,
"shipping": {
"freeShipping": true,
"fee": 0,
"originalFee": 5.99,
"deliveryMinDays": 4,
"deliveryMaxDays": 7,
"fulfilledByTikTok": true,
"shipFrom": "local"
},
"seller": {
"sellerId": "7495134783373216007",
"name": "GuruNanda LLC",
"rating": 4.5,
"soldCount": 6413926,
"productCount": 129,
"followers": 278606,
"badge": "OFFICIAL SHOP",
"businessName": "GuruNanda LLC"
},
"aiKeywords": [
"tongue scraper",
"stainless steel tongue cleaner"
],
"topReviews": [
{
"rating": 5,
"text": "It's really good; it removes all the dirt, I give it a 10/10.",
"createdAt": "2026-07-24T21:15:52.471Z",
"variant": "Default",
"country": "US",
"verifiedPurchase": true,
"incentivized": false,
"imageCount": 6
}
],
"source": "product",
"scrapedAt": "2026-10-22T10:34:19.179Z"
}
| Field | What it holds |
|---|---|
rowType | Kind of row. Filter on this first. 'product': one TikTok Shop product (charged; see detailLevel). 'error': an input could not be read; see code and message (free). |
platform | Always 'tiktok-shop'. |
productId | TikTok Shop product id (the long number at the end of the product URL). |
detailLevel | 'full': read from the product page (charged as 'product'). 'card': TikTok did not serve the product page to an anonymous visitor, so the row holds only what the store or category card shows: title, price, originalPrice, discountPercent, soldCount, rating, reviewCount, seller id and name, one image (charged as 'product-card'; the other fields are null or empty). |
region | Market of the page: US or GB. Prices, stock and shipping are for this market. |
url | Canonical product page URL on shop.tiktok.com. |
title | Product title as the seller wrote it. |
description | Product description (first 300 characters by default, full or none on request). Emails and phone numbers removed. |
brand | Brand from the product attributes; null when the seller set none. |
currency | Currency code of every price in the row, e.g. USD or GBP. |
price | Lowest current variant price (after discounts), in currency. |
maxPrice | Highest current variant price; equal to price for single-variant products. |
originalPrice | Lowest variant price before discounts; null when nothing is discounted. |
discountPercent | Discount of the lowest price against originalPrice, in percent; null without a discount. |
priceText | Price as displayed on the page, e.g. "$21.99 - 82.99". |
soldCount | Items sold since the product was listed, all markets (TikTok's own counter, excluding returns). |
rating | Average product rating, 1 to 5; null without reviews. |
reviewCount | Number of product reviews. |
ratingCounts | Reviews per star, e.g. {"5": 5366, "4": 664, "3": 294, "2": 101, "1": 326}. |
categories | Category path from top level to leaf, e.g. ["Beauty & Personal Care", "Skincare", "Serums & Essences"]. |
categoryId | Id of the leaf category (usable in shop.tiktok.com/us/c/<name>/<id> URLs). |
promotions | Promotion labels shown on the page, e.g. "Flash sale", "Free shipping". |
images | Product image URLs (first URL of each image). |
hasVideo | True when the product page has a product video. |
videoDurationSec | Length of the product video in seconds; null without video. |
specifications | Product attributes, e.g. [{"name": "Brand", "value": "Asterwood"}]. |
variants | Purchasable variants (SKUs): skuId, options (e.g. {"Size": "1oz"}), price, originalPrice, discountPercent, stock, purchaseLimit, available. |
totalStock | Sum of variant stock as the page states it; null when no variant states stock. |
inStock | True when at least one variant can be bought now; null on card rows. |
shipping | Default delivery option for the market: freeShipping, fee, originalFee, currency, deliveryName, deliveryMinDays, deliveryMaxDays, deliveryWindow, fulfilledByTikTok, shipFrom. |
seller | The shop as a business: sellerId, name, url, rating, soldCount, productCount, reviewCount, followers, videoCount, badge (e.g. "OFFICIAL SHOP"), businessName (legal company name, only when it is clearly a company). No owner or creator account. |
aiTitle | Short product title generated by TikTok for search. |
aiKeywords | Search keywords generated by TikTok for the product. |
topReviews | The reviews shown on the product page (up to 5): reviewId, rating, text (emails and phones masked), createdAt, skuId, variant, country, verifiedPurchase, incentivized, imageCount. Never reviewer names or photos. |
source | How the product was found: 'product' (given directly), 'store' or 'category'. |
input | The URL or id from the input that led to this row. |
sourceRank | Position on the store or category page (1-based); null for products given directly. |
scrapedAt | ISO datetime when the row was read. |
code | Error rows only: invalid_input, not_found, blocked, unavailable or no_match. |
message | Error rows only: what went wrong, in plain words. |
Schedule it
In Apify Console, create a Task from your input and add a Schedule (for example daily). Each run writes a new dataset; integrations can push the rows to Google Sheets, Slack, a webhook or your database.