Read TikTok Shop prices and per-SKU stock across ten markets
The TikTok Shop API returns product, seller, category and review data from TikTok Shop across ten markets — the US, UK, Malaysia, Singapore, Thailand, the Philippines, Vietnam, Indonesia, Japan and Mexico — as clean JSON.
13 active endpoints, on 1 and 2 credit tiers.
- POST/tiktok-shop/v1/product_detail
- POST/tiktok-shop/v1/products_by_ids
- POST/tiktok-shop/v1/category_tree
- POST/tiktok-shop/v1/category_products
- POST/tiktok-shop/v1/search
- POST/tiktok-shop/v1/recommended_shops
- POST/tiktok-shop/v1/seller_profile
- +6 more
What TikTok Shop endpoints does ReefAPI ship?
13 live read endpoints. Read-only data API: no writes, no account actions, no dashboard access on the target site.
TikTok Shop API
3 of 13 endpoints, ready to run
One product, everything, one call: title, description, every image, every SKU with its own price and its exact remaining stock as an integer, shipping weight and package size, the discount and list price, the delivery window, the category path, the seller's full profile and the complete star histogram.
{ "ok": true, "meta": { "api": "tiktok-shop", "endpoint": "product_detail", "mode": "live", "latency_ms": 2432.8, "record_count": 1, "cache_hit": false, "completeness_pct": 100 }, "data": { "product": { "product_id": "1729450858130215273", "title": "WateLves Unisex Barefoot Shoes, Minimalist Knit Fabric Slip-On Footwear, Lightweight Athletic Sneakers for Walking & Outdoors, Minimalist Shoes, Walking Casual Shoes#sohochic", "url": "https://shop.tiktok.com/us/pdp/product/1729450858130215273", "market": "us", "price": 32.99, "list_price": 42.99, "currency": "USD", "currency_symbol": "$", "discount_percent": 23, "is_promotional": true, "price_prefix": "From", "rating": 4.8, "review_count": 6237, "sold_count": 52943, "brand": null, "images": [ "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/c851aee5be57484ca7a726cad2140a95~tplv-fhlh96nyum-crop-webp:1600:1600.webp?dr=12190&t=555f072d&ps=933b5bde&shp=8dbd94bf&shcp=607f11de&idc=useast5&from=2378011839", "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/5993e121123147c2b343679f4111404a~tplv-fhlh96nyum-crop-webp:3840:3840.webp?dr=12190&t=555f072d&ps=933b5bde&shp=8dbd94bf&shcp=607f11de&idc=useast5&from=2378011839", "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/f97b50c9b8b04857a1fd2920fd2d515f~tplv-fhlh96nyum-crop-webp:1600:1600.webp?dr=12190&t=555f072d&ps=933b5bde&shp=8dbd94bf&shcp=607f11de&idc=useast5&from=2378011839" ], "description": "DESCRIPTION\nIf there are any logistics problems or other problems, please contact our customer service as soon as possible, we will perfectly solve all your problems.\n【Size Chart】\n【UNIQUE DESIGNS】\nThese slip-on shoes offer easy wear and a zero-drop design for a close-to-the-ground, barefoot feel. These walking casual shoes are an essential choice for anyone seeking comfortable and supportive footwear for daily walks or more extended outdoor adventures.\n【KNIT FABRIC】\nThese Knit shoes feature a comfortable, breathable, and lightweight fabric that keeps your feet sweat-free, ensuring a cozy and airy fit.\n【HIGH-QUALITY SOLES】\nSlip-resistant, durable sole for extra traction and longevity.\n【FASHION SHOES】\nThese sock-like shoes blend fashion seamlessly, elevating your style with versatile pairing options.", "description_images": [ "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/15a013e73da541feaa122ad28311718d~tplv-fhlh96nyum-origin-jpeg.jpeg?dr=12178&t=555f072d&ps=933b5bde&shp=a3510d86&shcp=6ce186a1&idc=useast5&from=2739998086", "https://p16-oec-general.ttcdn-us.com/tos-alisg-i-aphluv4xwc-sg/4a8a251116c6406fba7cf5c5cd8fcb4e~tplv-fhlh96nyum-origin-jpeg.jpeg?dr=12178&t=555f072d&ps=933b5bde&shp=a3510d86&shcp=6ce186a1&idc=useast5&from=2739998086", "https://p16-oec-general.ttcdn-us.com/tos-alisg-i-aphluv4xwc-sg/904f39db8fc64ae38ff19aa52b8eea7e~tplv-fhlh96nyum-origin-jpeg.jpeg?dr=12178&t=555f072d&ps=933b5bde&shp=a3510d86&shcp=6ce186a1&idc=useast5&from=2739998086" ], "categories": [ { "category_id": "601352", "name": "Shoes", "level": 1, "is_leaf": false, "parent_category_id": "0" }, { "category_id": "900616", "name": "Men's Shoes", "level": 2, "is_leaf": false, "parent_category_id": "601352" }, { "category_id": "601357", "name": "Casual Trainers", "level": 3, "is_leaf": true, "parent_category_id": "900616" } ], "specifications": [ { "name": "CA prop 65: repro. chems", "value": "No" }, { "name": "CA prop 65: carcinogens", "value": "No" }, { "name": "Dangerous goods or hazardous materials", "value": "No" } ], "variant_options": [ { "name": "Color", "values": [ "Apricot", "White", "Black" ] }, { "name": "Size", "values": [ "6 Women/5 Men", "7 Women/6 Men", "8 Women/7 Men" ] } ], "skus": [ { "sku_id": "1729537990794842473", "sku_name": "DB Walking Shoes-Apricot36", "available_quantity": 13, "in_stock": true, "properties": [ { "name": "Color", "value": "Apricot" }, { "name": "Size", "value": "6 Women/5 Men" } ], "image": null, "package_weight_g": 395, "package_length_cm": 25, "package_width_cm": 10, "package_height_cm": 6, "is_pre_order": false, "pre_order_ship_days": 0, "price": 32.99, "list_price": 42.99, "currency": "USD", "currency_symbol": "$", "discount_percent": 23, "is_promotional": true, "price_prefix": null }, { "sku_id": "1729450858130280809", "sku_name": "DB Walking Shoes-Apricot37", "available_quantity": 6, "in_stock": true, "properties": [ { "name": "Color", "value": "Apricot" }, { "name": "Size", "value": "7 Women/6 Men" } ], "image": null, "package_weight_g": 395, "package_length_cm": 25, "package_width_cm": 10, "package_height_cm": 6, "is_pre_order": false, "pre_order_ship_days": 0, "price": 32.99, "list_price": 42.99, "currency": "USD", "currency_symbol": "$", "discount_percent": 23, "is_promotional": true, "price_prefix": null }, { "sku_id": "1729450858130346345", "sku_name": "DB Walking Shoes-Apricot38", "available_quantity": 29, "in_stock": true, "properties": [ { "name": "Color", "value": "Apricot" }, { "name": "Size", "value": "8 Women/7 Men" } ], "image": null, "package_weight_g": 395, "package_length_cm": 25, "package_width_cm": 10, "package_height_cm": 6, "is_pre_order": false, "pre_order_ship_days": 0, "price": 32.99, "list_price": 42.99, "currency": "USD", "currency_symbol": "$", "discount_percent": 23, "is_promotional": true, "price_prefix": null } ], "sku_count": 98, "total_stock": 1198, "in_stock": true, "shipping": { "delivery_min_days": 3, "delivery_max_days": 5, "cod_available": false, "shipping_fee": 5.99, "currency": "USD", "service_name": "US-NEW-FBT-Standard-Real" } }, "reviews": [ { "review_id": "7536062640255649549", "rating": 5, "text": "LOVE! LOVE! LOVE them! They are so comfortable! Nice cushion on the inside. They are wide enough for my feet, but not too wide. I only wish there were more color options.\nShape and size: Nice shape. Size 37\nScent: They did not have a scent\nWearability: I could wear them all day, every day\nColor: Nice white color.", "sku_id": "1729450858130936169", "review_time_ms": 1754626324657, "verified_purchase": true, "incentivized": false, "image": "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/3c45dbc4cfac42aaa4c7a6084f733da9~tplv-fhlh96nyum-crop-webp:300:300.webp?dr=12190&t=555f072d&ps=933b5bde&shp=8dbd94bf&shcp=607f11de&idc=useast5&from=2378011839" }, { "review_id": "7660224790543763213", "rating": 4, "text": "Super cute and comfortable. Love the color options. Great for walking, will try riding my bike tomorrow. Love the wxtra padded insiles and the separation for your toes. Excellent fit, with or without socks.", "sku_id": "1729450858131788137", "review_time_ms": 1783535077982, "verified_purchase": true, "incentivized": false, "image": "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/0b818779212440518aad76694ee42451~tplv-fhlh96nyum-crop-webp:300:300.webp?dr=12190&t=555f072d&ps=933b5bde&shp=8dbd94bf&shcp=607f11de&idc=useast5&from=2378011839" }, { "review_id": "7426996900282550058", "rating": 5, "text": "Shape and size: Perfect/Sz:8 Fits Comfortably.\nScent: No Smell\nWearability: These Are So Comfy.\nColor: Black\nWhen I originally ordered my 1st pair on Amazon I got them in the color apricot, and once I tried them on I had knew then that these were going to be my forever go to shoes & I needed more & TikTok came through as always! Great buy!", "sku_id": "1729450858131657065", "review_time_ms": 1729232488603, "verified_purchase": true, "incentivized": false, "image": "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/522d64be6a8e406d9d779b526fd46acd~tplv-fhlh96nyum-crop-webp:300:300.webp?dr=12190&t=555f072d&ps=933b5bde&shp=8dbd94bf&shcp=607f11de&idc=useast5&from=2378011839" } ], "review_summary": { "total_reviews": 6237, "average_rating": 4.8, "rating_distribution": { "1": 150, "2": 54, "3": 144, "4": 451, "5": 5438 } } } }
How the TikTok Shop API works
TikTok Shop is a normal ReefAPI surface — the same four rules that hold for every other engine on the key.
No OAuth app, no request signing, no per-site account. One key covers all 185 engines.
Every route is a POST with a JSON body. Parameters are validated against the published schema before anything is charged.
Credits, not seats. Failed and blocked calls are never charged, and cache hits cost nothing.
One envelope everywhere. meta carries latency_ms, record_count and the endpoint that answered.
Walk a market you have no ids for
Nine of the ten markets have no keyword search, so a first integration cannot start from a query. It starts from the taxonomy, which needs nothing hard-coded.
{"market": "id"}28 top-level departments on every market. Pass a category_id back to walk down; each node says whether it is a leaf.
{"category_id": "<leaf id>", "market": "id", "max_results": 100}Around 100 products with price, rating and sold count on each. Pages are position-independent, so a throttled page is retried without losing your place.
{"product_id": "<id>", "market": "id"}Only for the ids you actually want: every SKU with its own price and an exact integer stock.
A whole category on a market you had no identifiers for, in three calls and with no id list to maintain. products_by_ids refreshes up to fifteen United States ids in a single call.
curl -X POST https://api.reefapi.com/tiktok-shop/v1/product_detail \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"product_id":"1729450858130215273","market":"us"}'{
"ok": true,
"data": { … },
"meta": {
"api": "tiktok-shop",
"endpoint": "product_detail",
"mode": "live",
"latency_ms": …,
"record_count": …
},
"error": null
}Id shapes, and the price fields that exist only on a promotion
Two things account for most of the surprises here. Every identifier is a long numeric string that must never be parsed as a number, and three of the price fields are populated only when the product is genuinely on promotion. They are not zeroed or nulled at random, they follow is_promotional. All values below came from measured calls on the US and Japanese storefronts.
| Field | Format | What was measured |
|---|---|---|
| product_id | 19-digit numeric string. The same value on every action. | 1729450858130215273. The identical id on the gb market returned NOT_FOUND, because a catalogue is per-market. |
| sku_id | 19-digit numeric string, one per variant. | That product carried 98 skus, each with its own available_quantity (13, 6 and 29 on the first three), summing to total_stock 1011. |
| seller_id and global_seller_id | 19-digit numeric strings, identical on the US shop measured. | 7495552880616442217, shop "WateLves Footwear", on_sell_product_count 38. |
| category_id | 6-digit numeric string. Three levels, and level 1 has parent_category_id "0". | 601352 Shoes, then 900616 Men's Shoes, then 601357 Casual Trainers with is_leaf true only on the last. |
| is_promotional | Boolean. It gates the two fields below. | true on one product and false on another inside the same bulk response. |
| list_price and discount_percent | Populated only when is_promotional is true, and null otherwise. | Promoted: price 32.99, list_price 42.99, discount_percent 23.0. Not promoted: price 84.97, list_price null, discount_percent null. |
| currency and currency_symbol | Set by market, not by your locale. | us returned USD with "$"; jp returned JPY with "円" and price 6113 as a whole number, since yen has no minor unit. |
| sold_count | An exact integer, not the banded string the storefront prints. | 52870 on a product whose page shows "52.9K sold". |
| review_time_ms | Unix milliseconds, 13 digits, not seconds. | 1754626324657 on a measured review. |
| not_found[] | Returned by products_by_ids only, and trustworthy there. | Three ids in, two products out, not_found ["1111111111111111111"], completeness_pct 66.67. |
price_prefix is a display hint, not a number. It came back "From" at the product level on a multi-SKU listing and null on each individual SKU, because the product-level price is the cheapest variant rather than one price for everything.
Ten markets, ten currencies, and the flag that says the price is a range
Measured on all ten markets on the same day, one category page each. Two of these go against us.
United States, United Kingdom, Malaysia, Singapore, Thailand, the Philippines, Vietnam, Indonesia, Japan and Mexico. 30 of 30 rows priced on each, each in that market's own currency — USD, GBP, MYR, SGD, THB, PHP, VND, IDR, JPY and MXN — stated on the row rather than inferred.
A row can carry a price_prefix reading From, and when it does the number is the cheapest SKU rather than the price of the thing you named. Measured on one category page per market: 13 of 30 rows on the United States, 9 of 30 on Mexico, 6 of 30 on the United Kingdom and zero on the other seven markets. Check that flag before you write the number into a price feed, or open product_detail, which gives every SKU its own price.
Checked against the two price numbers across a whole page: 11.68 from 14.79 reports 21, 4.59 from 7.29 reports 37, 8.23 from 13.49 reports 39. Every one matches to a rounding. Where a product is not discounted the list price is absent and no percentage is claimed.
In a ten-market sweep run back to back, Thailand, the Philippines and Mexico each answered a retryable block on the first attempt and returned normally afterwards — Thailand took three attempts, the other two took one retry each. The error is explicitly marked retryable. Build one retry into the loop for these markets and pace a sweep rather than firing it in parallel.
The search action is United States only and takes no market parameter, because TikTok serves keyword search nowhere else at any address. On the other nine, category_products is the route, and it reaches further into a category than search reaches into a query.
product_detail returns every SKU with available_quantity as an integer, plus its option values, its package weight in grams and its package dimensions in centimetres. A sold-out SKU comes back as zero rather than disappearing from the list.
products_by_ids takes up to fifteen United States ids and answers with a products list and a separate not_found list naming exactly which ids did not resolve. Asked for one real id and one invented one, it returned the real product and named the invented id as not found. That is the cheap pre-check before spending a full product fetch on a stale catalogue.
Three full review texts plus the complete one-to-five star distribution and the total count covering every review the product has — so an average is computed from the real distribution rather than from three samples. The published sample on this page drops the reviewer's photograph; TikTok masks the name itself.
What people build with TikTok Shop
The jobs this data is most often used for.
endpoints
credits per call
Pricing teams call search then product_detail to track a product's per-SKU price and stock against its list price.
Trend and merchandising tools use ranking_list and premium_offers to see what is selling and what is discounted right now.
Seller-intelligence products use seller_profile and seller_catalog to audit a competitor's whole TikTok Shop inventory.
Catalogue pipelines use products_by_ids to refresh 15 products per call instead of one request per id.
What TikTok Shop data costs
The cheapest call here is 1 credit, so $15/mo (Pro) buys 10,000 of them — $1.50 per 1,000 credits. Credits roll over and never expire, and failed or blocked calls are not charged.
Full pricing →- 1,000 free credits on signup, no card
- One key, all 185 APIs, one credit pool
- Failed and blocked calls are never charged
- Credits roll over and never expire
Call it in two lines
Sign up, get 1,000 credits and one key that works on every engine. Then this is the whole protocol.
curl -X POST https://api.reefapi.com/tiktok-shop/v1/product_detail \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"product_id":"1729450858130215273","market":"us"}'import requests
r = requests.post(
"https://api.reefapi.com/tiktok-shop/v1/product_detail",
headers={"x-api-key": REEF_KEY},
json={
"product_id": "1729450858130215273",
"market": "us"
},
)
print(r.json()["data"])Have a question? We got answers.
The questions people actually ask before wiring up TikTok Shop.
Get a free key →list_price and discount_percent are null on some products. Is data missing?▾
No. Read is_promotional first. Those two fields are populated when and only when the product is genuinely on promotion. In one measured bulk call a promoted product returned price 32.99, list_price 42.99 and discount_percent 23.0, while a second product in the same response returned price 84.97 with both other fields null and is_promotional false. There is no hidden pre-discount price on a product that is not discounted, so a null here means "not on sale", not "failed to parse".
How do I check whether a product id is still live without paying for a full fetch?▾
Use products_by_ids. It takes up to 15 US ids in one call and returns a not_found[] array you can trust. A measured call with two real ids and one made-up id returned two products, not_found ["1111111111111111111"], requested 3, returned 2 and completeness_pct 66.67. It gives you the listing card only, with no skus[], no per-SKU stock, no description and no reviews, so use it to validate and price a list and then spend a product_detail call on the ids that matter.
The same product id works on us but not on gb. Why?▾
Because market selects the storefront and the catalogue, not merely the currency. A measured product_detail for 1729450858130215273 on market=us returned the full record, while the identical id on market=gb returned NOT_FOUND with a message saying the product does not exist in the gb catalogue, confirmed on two independent observations. Sellers list per market, so treat the pair (product_id, market) as your key rather than the id on its own.
Is sold_count exact, or is it the rounded number from the page?▾
Exact. A measured product returned sold_count 52870 while its storefront page prints "52.9K sold", and the seller record returned sold_count 186348 against a shop blurb reading "186.3K sold". You get the underlying integer rather than the banded display string, which is the difference between being able to compute a weekly delta and not. review_count behaves the same way, returning 6227 rather than "6.2K".
How does per-SKU stock work, and does it add up?▾
Yes, and that is the point of it. skus[] carries available_quantity as a real integer per variant, alongside in_stock, properties[] (colour, size and so on), and its own price and package dimensions. A measured 98-SKU shoe listing had 13, 6 and 29 units on its first three variants and reported total_stock 1011 for the product, and total_stock is the sum. That lets you watch a single size run out instead of only seeing the whole product flip to unavailable.
How does the review histogram relate to average_rating?▾
review_summary is complete even though the review texts are not. A measured product returned rating_distribution with numeric string keys, {"1":149, "2":56, "3":144, "4":451, "5":5427}, summing exactly to total_reviews 6227, and the weighted mean of that distribution is 4.759, which is the average_rating 4.8 it reported. So you can recompute the rating, chart its shape, or track how the one-star bucket moves, all from a 7 KB call rather than re-fetching the product page.
Why do I only get three review texts?▾
Three is what the public review surface serves per product, and we ship what we can actually deliver rather than a number we cannot. Each of the three is a full record: review_id, rating, text, author, sku_id (so you know which variant was bought), verified_purchase, incentivized, and a photo where there is one. Note the author names arrive partially masked by TikTok itself, as "S**s" and "F**h C**l" in a measured response. For coverage of every review, read review_summary, which is complete.
Are category names localized for non-US markets?▾
Not reliably, so do not key on name. A measured category_tree on market=jp returned its 28 top-level departments with name in English, "Womenswear & Underwear" and "Phones & Electronics", identical to the US labels and carrying the same category_ids, 601152 and 601739. name_en holds the stable slug form, "womenswear-underwear". Key on category_id or name_en and use name for display only, because it is TikTok's own label and it varies by market.
What timestamp unit do reviews use?▾
Milliseconds. review_time_ms measured 1754626324657, which is 13 digits, and feeding that to a seconds-based parser puts the review roughly 53,000 years in the future. Divide by 1000 for anything expecting unix seconds. It is the only timestamp field on this API, so there is nothing to stay consistent with; just read the _ms suffix as the contract it is.
What is the TikTok Shop API?▾
TikTok Shop API is a ReefAPI endpoint group for products, per-sku stock, sellers, reviews and best-sellers across 10 markets. It returns live JSON through POST requests under /tiktok-shop/v1.
Is the TikTok Shop API free to try?▾
Yes. ReefAPI starts with 1,000 free credits, no card required. TikTok Shop calls use the same shared credit balance as every other ReefAPI engine.
Do I need a TikTok Shop login or account?▾
No login to TikTok Shop is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the TikTok Shop data?▾
The page example is captured from a live product_detail call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the TikTok Shop API use?▾
TikTok Shop actions currently cost 1-2 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
37 E-commerce & Marketplaces APIs on the same key
One key, one credit pool, one response envelope. If you are pulling TikTok Shop, you are one call away from the rest of the category — no second contract, no second integration.
Need something this API does not do?
Name the endpoint, the field, or a source we do not carry yet. We ship new APIs every week and you would be first to get the key. Real people read every message and reply the same day.
Try it on your own data before you pay anything
The call above is the real endpoint, not a recording. A free key gives you 1,000 credits, the other 184 APIs, and the same envelope everywhere.
Endpoints, parameters and credit costs on this page are read from the live catalog and cannot drift from what the API accepts. Field notes were captured on 2026-08-28.