How to collect TikTok Shop product reviews by keyword
Three endpoints, in this order: TikTok Shop product search, product detail, then product reviews page by page. The worked example below is 10 calls and costs $0.022; a failed call is not charged.
Updated
The job: you are about to sell, source or compare a product on TikTok Shop and want to know what buyers say about the listings already there. Star averages hide the useful part. The review text tells you which size runs small, which strap breaks in a week and what people were pleasantly surprised by. Here you search the shop by keyword, read the details of the listings that have the most reviews, and page through their reviews. You end with two CSVs: the products (price, units sold, rating, shop) and the reviews (stars, text, variant bought, photos, verified purchase).
TikTok's own Shop API is for sellers reading their own shop, so it does not help with other listings; TikTok Shop product data without a seller account explains the difference. These endpoints read the public listings buyers see, with an API key and no TikTok account.
Step by step
All three endpoints take a country parameter: the shop's market as an ISO country code. It defaults to US, which is also the market with the most complete data. Other markets work with their own code, with thinner coverage, so try one call first. Use the same country for all three calls, and read currency before comparing prices across markets.
Step 1: search products
TikTok Shop product search takes query and country. One call returns about 20 products for $0.004. Each has id, url, title, price, original_price, currency, sold, rating, reviews (how many reviews it has), one image and shop (ID and name). reviews is how you pick listings worth reading.
curl -G "https://api.okdata.online/v1/tiktok/shop/products/search" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'query=posture corrector' \
--data-urlencode 'country=US'
Step 2: read each listing
TikTok Shop product detail takes the product id, or the product page link as url (for example https://www.tiktok.com/shop/pdp/1731859027465331722), plus country. For $0.002 it returns the current price and discount, units sold, rating, review count and every image. With view=full, ext adds shop_rating, shop_followers and the listing's specifications, where details such as material and size are listed.
curl -G "https://api.okdata.online/v1/tiktok/shop/products/get" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'id=1731859027465331722' \
--data-urlencode 'country=US' \
--data-urlencode 'view=full'
Step 3: page through the reviews
TikTok Shop product reviews takes the product id (required) and country. One call returns about 20 reviews for $0.002. Each has rating (1 to 5 stars), text, variant (the size or colour bought), images (buyer photos), verified_purchase, published_at and author_name, which the shop usually shows masked. For the next page, send page.next_cursor back as cursor; stop when page.has_more is false.
curl -G "https://api.okdata.online/v1/tiktok/shop/products/reviews/list" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'id=1731859027465331722' \
--data-urlencode 'country=US'
Step 4: put it together in Python
The script runs the three steps for one keyword and writes shop_products.csv and shop_reviews.csv. REVIEW_PAGES caps the review pages per product, and with it the bill.
import csv
import os
import requests
API = "https://api.okdata.online"
HEADERS = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
QUERY, COUNTRY = "posture corrector", "US"
PRODUCTS = 3 # listings to read
REVIEW_PAGES = 2 # review pages per listing; each page is one call
def get(path, **params):
body = requests.get(API + path, headers=HEADERS, params={**params, "country": COUNTRY}, timeout=60).json()
if body["code"] == "not_found":
return None # delisted: not charged
if body["code"] != "ok":
raise RuntimeError(f"{body['code']}: {body['message']} (charged: {body['charged']})")
return body
found = get("/v1/tiktok/shop/products/search", query=QUERY)["data"]["items"]
found = sorted(found, key=lambda p: p["reviews"] or 0, reverse=True)[:PRODUCTS]
products, reviews = [], []
for p in found:
body = get("/v1/tiktok/shop/products/get", id=p["id"], view="full")
if not body:
continue
products.append(body["data"]["item"])
cursor = None
for _ in range(REVIEW_PAGES):
page = get("/v1/tiktok/shop/products/reviews/list", id=p["id"], **({"cursor": cursor} if cursor else {}))
if not page:
break
reviews.extend(page["data"]["items"])
if not page["page"]["has_more"]:
break
cursor = page["page"]["next_cursor"]
with open("shop_products.csv", "w", newline="", encoding="utf-8-sig") as f:
out = csv.writer(f)
out.writerow(["product_id", "title", "price", "original_price", "currency", "sold", "rating", "reviews", "shop", "shop_rating"])
for p in products:
out.writerow([p["id"], p["title"], p["price"], p["original_price"], p["currency"], p["sold"],
p["rating"], p["reviews"], p["shop"]["name"], p["ext"].get("shop_rating")])
with open("shop_reviews.csv", "w", newline="", encoding="utf-8-sig") as f:
out = csv.writer(f)
out.writerow(["product_id", "review_id", "rating", "text", "variant", "photos", "verified", "published_at"])
for r in reviews:
out.writerow([r["product_id"], r["id"], r["rating"], r["text"], r["variant"],
len(r["images"] or []), r["verified_purchase"], r["published_at"]])
With the defaults that is the worked example: 10 calls and about 120 reviews. Ten listings at 5 review pages each would be $0.124 in total.
Good to know
- Stars without words. Some buyers leave only a rating; then
textis an empty string. Filter those out before you summarise text, but keep them for the star distribution. - No review likes. TikTok Shop shows no likes on reviews, so
likesis always null andmeta.unavailablesaysplatform. - Order. Reviews come in the shop's own order and there is no sort parameter. A few pages are a sample, not the whole picture; the product's
ratingandreviewscount cover all of them. - Units sold.
soldis the number the listing displays. Compare it day to day for a rough sales trend; it is not an order count. - Dates. Where the shop shows only a date,
published_atis that day. - Public data only. GMV, traffic sources and ad spend live in the seller's back office; none of it is here.
- Delisted products. A product that is gone answers
not_found, which is free, as is every other error. Send anIdempotency-Keyheader to make retries safe: the same request with the same key within 24 hours returns the stored result withmeta.replayed: trueand no new charge.
To turn the review text into product decisions, see mining competitors' reviews. For tracking prices and units sold across a category every day, see product research.
What the example costs
One search for posture corrector in the US shop, the detail of the 3 listings with the most reviews, then 2 pages of reviews (about 40) for each
| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| TikTok Shop product search | 1 | $0.004 | $0.004 |
| TikTok Shop product detail | 3 | $0.002 | $0.006 |
| TikTok Shop product reviews | 6 | $0.002 | $0.012 |
| Total | 10 | $0.022 |
Prices in force when this page was built. A failed call costs nothing.
In your AI (MCP)
Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.
SaySearch the US TikTok Shop for posture corrector. Take the 3 listings with the most reviews, show me price, units sold, rating and shop for each, then read about 40 reviews per listing and sum up what buyers praise and complain about.
What the agent sends to the call tool first
{
"endpoint": "tiktok.shop.products.search",
"params": {
"query": "portable coffee maker"
},
"idempotency_key": "<a new UUID>"
}