Scrape TikTok Shop products with Python
Search the US TikTok Shop by keyword and get every product card as JSON: title, price, discount, rating, sold count and seller. Then rank the products by sold count and save them to a CSV file. About 5 requests cover 250 products.
You can scrape the TikTok Shop storefront yourself, but then you maintain a headless browser or TikTok's internal web requests, a pool of US IP addresses and retry logic, and you fix them each time TikTok changes. This guide uses the TikTok Shop API instead: one GET request returns up to 50 products, and the JSON has the same fields every time.
Setup
You need Python 3, the requests package (pip install requests) and a key from the API's RapidAPI listing. The free plan's 100 requests are enough to run this guide. Put the key in an environment variable called RAPIDAPI_KEY.
import csv
import os
import time
import requests
BASE = "https://tiktok-shop-api5.p.rapidapi.com"
HEADERS = {
"X-RapidAPI-Key": os.environ["RAPIDAPI_KEY"],
"X-RapidAPI-Host": "tiktok-shop-api5.p.rapidapi.com",
}
def get(path, **params):
"""GET a path and return the JSON. Retries rate limits and upstream errors."""
for attempt in range(4):
response = requests.get(BASE + path, headers=HEADERS, params=params, timeout=30)
if response.status_code in (429, 502, 503) and attempt < 3:
time.sleep(2 ** attempt)
continue
response.raise_for_status()
return response.json()
Search one page
GET /v1/search takes a keyword in q and returns up to count product cards (maximum 50). The same request with curl:
curl --request GET \
--url 'https://tiktok-shop-api5.p.rapidapi.com/v1/search?q=adjustable%20dumbbell&count=2' \
--header "X-RapidAPI-Key: $RAPIDAPI_KEY" \
--header 'X-RapidAPI-Host: tiktok-shop-api5.p.rapidapi.com'
Real response, shortened to one product card, image links shortened
{
"products": [
{
"id": "1732630664915882459",
"title": "5-in-1 Adjustable Dumbbell Set 20/40/60 lbs – Multi-Functional Home Gym Equipment for Muscle Building, Squats & Strength Training",
"url": "https://shop.tiktok.com/us/pdp/5-in-1-adjustable-dumbbell-set-20-40-60-lbs-for-home-gym/1732630664915882459",
"image": "https://p16-oec-general-useast5.ttcdn-us.com/…",
"price": {
"currency": "USD",
"symbol": "$",
"value": "38.38",
"formatted": "38.38",
"original": "53.29",
"discount": "28%"
},
"rating": { "score": 4.8, "count": "9" },
"sold": { "count": "246" },
"seller": {
"id": "7496210586501679579",
"name": "Floft",
"logo": "https://p16-oec-general-useast8.ttcdn-us.com/…"
}
}
],
"has_more": true,
"next_offset": 2
}
price.original and price.discount are there only when the product is on sale. sold can be missing when TikTok hides it.
Get more pages
While has_more is true, pass next_offset as the next request's offset. Cards are keyed by product ID, so a product seen on two pages is kept once.
def search(term, max_pages=5):
cards, offset = {}, 0
for _ in range(max_pages):
data = get("/v1/search", q=term, count=50, offset=offset)
for card in data.get("products") or []:
cards[card["id"]] = card
if not data.get("has_more"):
break
offset = data["next_offset"]
return list(cards.values())
products = search("adjustable dumbbell")
print(len(products), "products")
Each page is one request, so max_pages=5 costs at most 5 requests for up to 250 products.
Rank by sold count
sold.count and rating.count are strings, formatted as TikTok shows them: "246", or "1.2K" for larger numbers. Convert them to numbers before sorting.
def to_number(text):
"""'246' -> 246, '1.2K' -> 1200, None -> 0"""
if not text:
return 0
text = text.strip().upper().replace(",", "")
multiplier = {"K": 1_000, "M": 1_000_000}.get(text[-1], 1)
if multiplier > 1:
text = text[:-1]
try:
return int(float(text) * multiplier)
except ValueError:
return 0
def sold(card):
return to_number((card.get("sold") or {}).get("count"))
ranked = sorted(products, key=sold, reverse=True)
for card in ranked[:10]:
print(f'{sold(card):>7} ${card["price"]["value"]:>7} {card["title"][:60]}')
For the card above, the printed line reads:
246 $ 38.38 5-in-1 Adjustable Dumbbell Set 20/40/60 lbs – Multi-Function
Save a CSV
Flatten each card into one row. Numbers are converted, so the CSV sorts correctly in a spreadsheet.
FIELDS = ["id", "title", "price", "original_price", "discount", "rating",
"rating_count", "sold", "seller_id", "seller", "url"]
def row(card):
price = card.get("price") or {}
rating = card.get("rating") or {}
seller = card.get("seller") or {}
return {
"id": card["id"],
"title": card["title"],
"price": price.get("value", ""),
"original_price": price.get("original", ""),
"discount": price.get("discount", ""),
"rating": rating.get("score", ""),
"rating_count": to_number(rating.get("count")),
"sold": sold(card),
"seller_id": seller.get("id", ""),
"seller": seller.get("name", ""),
"url": card["url"],
}
with open("tiktok_shop_products.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=FIELDS)
writer.writeheader()
writer.writerows(row(card) for card in ranked)
The row for the card above
1732630664915882459,"5-in-1 Adjustable Dumbbell Set 20/40/60 lbs – Multi-Functional Home Gym Equipment for Muscle Building, Squats & Strength Training",38.38,53.29,28%,4.8,9,246,7496210586501679579,Floft,https://shop.tiktok.com/us/pdp/5-in-1-adjustable-dumbbell-set-20-40-60-lbs-for-home-gym/1732630664915882459
Find more keywords
GET /v1/suggest returns the autocomplete terms TikTok Shop shows for a partial query. They are phrases shoppers type, so they make good search terms for a niche.
terms = get("/v1/suggest", q="office", count=5)["suggestions"]
all_products = {}
for term in terms:
for card in search(term, max_pages=2):
all_products[card["id"]] = card
Real response for q=office&count=5
{
"suggestions": [
"office accessories",
"office chair",
"office outfits women",
"office decor at work",
"office desk chair"
]
}
That loop costs 1 request for the suggestions plus up to 2 per term: at most 11 requests for 5 terms.
Things to know
- US storefront only. Prices are in USD.
- Sold count is the total TikTok displays, rounded like
"1.2K". It is not sales per day. - Responses are cached for up to 7 days. To see which products are picking up, save the CSV and compare it with a run a week or more later.
- Product detail (
GET /v1/products/{id}) adds the description and the full image gallery but has no price or rating. Keep those from the search card. - Errors:
400for a bad parameter,429when you pass your plan's rate limit,502when TikTok fails after our retries. Theget()helper above retries429and502. - This API is unofficial. It reads public storefront data and isn't affiliated with TikTok.
Next steps
- Export a product's reviews to CSV, with a 1★–5★ breakdown.
- List every product a seller offers, using the
seller_idcolumn. - TikTok Shop's official API vs public product data: which one fits your project.
- Full API reference for all five endpoints.
Plans
A free plan covers testing. Plans, monthly quotas and rate limits are listed on the RapidAPI listing. For more volume or a custom plan, use Contact provider on the listing.