Skip to main content
Blog

How to scrape TikTok Shop product data

Use the tiktok shop scraper to extract product pages, prices, variants, store details, and images from TikTok Shop URLs.

ScrapersTikTokMay 28, 2026
How to scrape TikTok Shop product data

TikTok Shop product pages contain pricing, seller details, reviews, shipping info, and product variants. The ScrapeNow TikTok Shop scraper extracts all of these from a product URL.

The the TikTok Shop Extract by URL scraper scraper extracts product data from a TikTok Shop product detail page. It returns title, price, availability, variants, store details, images, product specs, review counts, and sold counts.

Use it after you already have product page URLs. For discovery, run category or shop search first, collect product URLs, dedupe them, then pass those URLs into this scraper.

Data teams use this flow for catalog tracking, price monitoring, seller analysis, and product enrichment. The output is built for product tables, price history tables, seller tables, and spec child tables.

How to use this scraper

TikTok Shop Scraper job pipeline
The TikTok Shop Scraper job pipeline, from input to stored output.

The scraper slug is the TikTok Shop Extract by URL scraper. The required input is a TikTok Shop product URL that starts with https://www.tiktok.com/.

Run one known product URL first. Check the JSON shape, map the fields to your schema, then scale the batch.

Step 1. Find the product URL

Input variable:

  • url, URL to the product page on TikTok Shop. It must start with https://www.tiktok.com/.
TikTok Explore page with Shop link highlighted in sidebar
Open the TikTok Shop tab to reach product listings
Open `tiktok.com`.
TikTok Shop search bar typing guitar with autocomplete suggestions
Open TikTok and click the Shop tab to access product listings
In the left side panel, click the `Shop` option.

Search for the product in the search bar. For example, search for guitar.

TikTok Shop guitar search results with acoustic guitar listings
Search for a product keyword in TikTok Shop to find listings
Click the product from the search result page.
TikTok Shop GLARRY electric guitar product page with URL highlighted
Click the product listing to open its detail page on TikTok Shop
Copy the URL from the address bar. Keep the full URL for the first run, including query parameters.

That first run should match the page you opened in the browser. After you confirm the output, normalize URLs for deduplication and joins.

Step 2. Run the API request

Use this Python script. Replace YOUR_API_KEY with your ScrapeNow API key.

"""
Configuration:
    - Set SCRAPER_SLUG to the scraper you want to run.
    - Set SCRAPER_INPUTS to the list of input dicts matching that scraper's schema.
    - Set API_KEY to your scraper API key.
"""

import sys
import time
import json
import requests
import os

API_KEY = "YOUR_API_KEY"

SCRAPER_SLUG = "tiktok-shop-extract-by-url"

SCRAPER_INPUTS = [
    {
        "url": "https://www.tiktok.com/shop/pdp/glarry-6-string-acoustic-electric-guitar-starter-kit-amp-amp/1729495699318543725?source=ecommerce_searchresult&enter_method=feed_list_search_word&first_entrance=explore_page&first_entrance_position=navigation_bar&first_entrance_tt_scene=seo"
    }
]



BASE_URL = "https://api.scrapenow.io/api/v1/scraping"
TIMEOUT_SECONDS = 3600
POLL_INTERVAL = 5
SPINNER = "|/-\\"


def build_headers(api_key: str, content_type: str | None = None) -> dict:
    headers = {"Authorization": f"Bearer {api_key}"}
    if content_type:
        headers["Content-Type"] = content_type
    return headers


def trigger_scrape(slug: str, inputs: list[dict]) -> str:
    url = f"{BASE_URL}/scrape?scraper={slug}"
    response = requests.post(
        url,
        headers=build_headers(API_KEY, "application/json"),
        json={"inputs": inputs},
    )
    response.raise_for_status()
    return response.json()["data"]["job_id"]


def poll_until_done(job_id: str) -> str:
    start = time.time()
    i = 0
    while True:
        elapsed = time.time() - start
        if elapsed > TIMEOUT_SECONDS:
            print(f"\nTimeout after {TIMEOUT_SECONDS}s")
            sys.exit(1)
        response = requests.get(
            f"{BASE_URL}/jobs/{job_id}",
            headers=build_headers(API_KEY),
        )
        response.raise_for_status()
        data = response.json()
        status = data["data"]["status"]
        mins, secs = divmod(int(elapsed), 60)
        sys.stdout.write(
            f"\r[{SPINNER[i % 4]}] Waiting... {status} ({mins}m {secs:02d}s)  "
        )
        sys.stdout.flush()
        if status in ("completed", "failed"):
            print()
            return status
        time.sleep(POLL_INTERVAL)
        i += 1


def fetch_results(job_id: str) -> dict:
    response = requests.get(
        f"{BASE_URL}/jobs/{job_id}/results?format=json",
        headers=build_headers(API_KEY),
    )
    response.raise_for_status()
    return response.json()


def save_results(data: dict, slug: str) -> str:
    os.makedirs("output", exist_ok=True)
    filename = os.path.join("output", f"{slug}.json")
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
    return filename


def main() -> None:
    print(f"Triggering scraper: {SCRAPER_SLUG}")
    job_id = trigger_scrape(SCRAPER_SLUG, SCRAPER_INPUTS)
    print(f"Job started: {job_id}")
    final_status = poll_until_done(job_id)
    if final_status != "completed":
        print(f"Job failed with status: {final_status}")
        sys.exit(1)
    print("Fetching results...")
    results = fetch_results(job_id)
    output_file = save_results(results, SCRAPER_SLUG)
    print(f"Results saved to: {output_file}")


if __name__ == "__main__":
    main()

The script starts a job, polls every 5 seconds, waits up to 3600 seconds, downloads JSON, and writes it to disk. The save function creates the output directory before it writes the file.

The same request flow works for the other TikTok Shop scrapers. For Search TikTok Shop by category and Search TikTok shops, change the scraper slug and SCRAPER_INPUTS.

Keep the polling interval conservative during development. A 5-second interval gives you fast feedback without hammering the job endpoint.

For production jobs, log job_id, input count, start time, end time, and final status. Those fields save time when a scheduled run fails at 3 a.m.

Step 3. Check the JSON output

This trimmed response shows the shape returned by the TikTok Shop Extract by URL scraper.

[
  {
    "inputs": {
      "url": "https://www.tiktok.com/shop/pdp/glarry-6-string-acoustic-electric-guitar-starter-kit-amp-amp/1729495699318543725?source=ecommerce_searchresult&enter_method=feed_list_search_word&first_entrance=explore_page&first_entrance_position=navigation_bar&first_entrance_tt_scene=seo"
    },
    "scrape_status": "success",
    "url": "https://www.tiktok.com/shop/pdp/glarry-6-string-acoustic-electric-guitar-starter-kit-amp-amp/1729495699318543725?source=ecommerce_searchresult&enter_method=feed_list_search_word&first_entrance=explore_page&first_entrance_position=navigation_bar&first_entrance_tt_scene=seo",
    "title": "[Summervibes] GLARRY Full Size 6 Strings Acoustic Electric Guitar Beginner Kit w/ 15W Amp, Cutaway 41 Inch Electric Acoustic Guitar w/Inbuilt Tuner, Bag, Strap, Picks, Strings, Basswood Guitarra",
    "available": true,
    "description": "Low-Battery Display\nThe on-board 4 band EQ with low-battery display can go electric when it is plug-into an AMP.\nAdjustable Neck\nThe neck can be adjusted by using the simple wrench (included) to keep straight in different seasons and temperature.\nHigh-Precision Screws\nThe 18:1 high-precision tuning screws helps you tune efficiently and accurately, keeping your guitar's tone stable for a long time.",
    "currency": "USD",
    "initial_price": 1,
    "final_price": 1,
    "discount_percent": 35,
    "initial_price_low": 1,
    "initial_price_high": 1,
    "final_price_low": 1,
    "final_price_high": 1,
    "sold": 55,
    "colors": [
      "Distressed Black",
      "Distressed Green",
      "Matte Natural"
    ],
    "sizes": null,
    "shipping_fee": null,
    "specifications": [
      {
        "title": "CA Prop 65: Repro. Chems",
        "value": "No"
      },
      {
        "title": "CA Prop 65: Carcinogens",
        "value": "No"
      },
      {
        "title": "Contains Batteries or Cells?",
        "value": "None"
      }
    ],
    "reviews_count": 11,
    "reviews": null,
    "store_details": {
      "name": "Glarry",
      "url": "https://www.tiktok.com/shop/store/glarry/7494313903714501997",
      "num_of_items": 64,
      "rating": 4.4,
      "num_sold": 64,
      "followers": 228,
      "badge": "https://p19-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/129551baaf7d4cdda6220a5928f6a63c~tplv-fhlh96nyum-resize-png:300:300.png?dr=12184&t=555f072d&ps=933b5bde&shp=905da467&shcp=6ce186a1&idc=useast5&from=2422056039"
    },
    "images": [
      "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-omjb5zjo8w-tx/2d1cebb22ed949289a943f6dfa941b26~tplv-fhlh96nyum-crop-webp:1453:1453.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/7336c21fb75744bfb4ea14c55bc09766~tplv-fhlh96nyum-crop-webp:1500:1500.webp?dr=12190&t=555f072d&ps=933b5bde&shp=8dbd94bf&shcp=607f11de&idc=useast5&from=2378011839",
      "https://p16-oec-genera
... (truncated)

A successful row has scrape_status set to success. Check that field before you load the row into your product table.

Treat the response as an array, even when you submit one URL. That keeps your code path identical for single runs and batches.

How to get category and shop inputs

TikTok Shop Scraper discovery paths
How the TikTok Shop Scraper routes each input type to the right scraper.

Use product URL extraction when you already have PDP URLs. Use category or shop search when you need product discovery before extraction.

Scraper Input Use it for
the TikTok Shop Extract by URL scraper Product page URL Pull full data from known product pages
tiktok-shop-search-by-category Category page URL Find products inside a TikTok Shop category
tiktok-shop-search-by-shop Shop page URL Find products from one seller

The search scrapers return product URLs that you can feed into the TikTok Shop Extract by URL scraper. In production, run discovery first, normalize product URLs, remove duplicates, then run extraction on the remaining PDPs.

This staged flow gives you a clean checkpoint. Discovery finds candidates, extraction spends credits on the pages you choose to keep.

Get a category URL

Input variable:

  • url, URL to the category page on TikTok Shop. It must start with https://www.tiktok.com/.
TikTok Explore sidebar with Shop selected to browse categories
Open TikTok Shop, then browse to a product category
Open `tiktok.com`.
TikTok Shop categories row highlighting Books, Magazines section
Open TikTok Shop and click the Shop tab in the side panel
In the left side panel, click the `Shop` option.

On the Home page, choose a category from the category bar. For example, select Books, Magazines & Audio.

TikTok Shop Books Magazines Audio subcategories with Video Music highlighted
Copy the category page URL from the address bar
In the category bar at the top of the page, choose a subcategory. For example, select `Video & Music`.
TikTok Shop Video Music category page with vinyl records and URL
Select a subcategory to narrow the product set before copying the URL
Copy the URL from the address bar. Store the source category with the discovered product URLs.

That source field helps during audits. If the same product appears in multiple categories, you can trace every discovery path.

Get a shop URL

Use shop URLs when you want all products from one seller. This path works well for seller monitoring, competitive tracking, and brand catalog audits.

TikTok Explore page Shop link highlighted to find a seller
Open TikTok Shop to find the seller you want
Open `tiktok.com`.
TikTok Shop search bar typing yamaha guitar with suggestions
Search for a shop or product name in TikTok Shop to find the seller
Find a shop through TikTok Shop search or from a product page.
TikTok Shop yamaha guitars search results listing acoustic guitars
Find the shop through search results or from a product detail page
Open the seller page.
TikTok Shop Segawe acoustic guitar product page with seller name
Open the seller page to view their full product catalog
Copy the shop URL from the address bar.
TikTok Shop Segawe seller storefront page with URL highlighted
Copy the shop URL from the address bar for scraper input
If your workflow includes TikTok content around products, run the [Extract TikTok post data](/scraper/tiktok-posts-extract-by-url) scraper for post metadata. Use the [Extract TikTok comments](/scraper/tiktok-comments-extract-by-url) scraper for comment threads tied to product videos.

What data you get back

TikTok Shop Scraper post processing
The TikTok Shop Scraper post processing that shapes raw results into warehouse tables.

Each result includes the original input, scrape status, product fields, seller fields, and media URLs. Treat scrape_status as the first field to check before you write the row to your database.

The scraper returns nullable fields where TikTok Shop does not show a value. Code for null, empty arrays, and missing optional attributes.

Product identity fields

url is the product page that was scraped. Store it as the canonical key if your system does not already assign a product ID.

title is the product listing title. TikTok Shop titles often combine brand names, bundle details, sizes, model numbers, and promo text in one string.

description is the product description with line breaks preserved. Keep it as raw text, then create a cleaned copy for search indexing or keyword matching.

For catalog matching, avoid title-only joins. Combine url, store_details.url, image hashes, and selected specs when you match products across sellers or refresh runs.

Title strings change often. Merchants add discount text, seasonal tags, and shipping claims without changing the underlying product.

Price fields

currency tells you the money unit, such as USD.

initial_price and final_price give the current displayed price values. For range pricing, read initial_price_low, initial_price_high, final_price_low, and final_price_high.

discount_percent is a number, so store it as an integer or decimal. In the sample response, the value is 35.

For price monitoring, keep a separate price history table. Write the normalized product URL, observed price fields, currency, scrape timestamp, and scrape status for every successful run.

Use the current product table for the latest price. Use the history table for trend lines, alerting, and rollback checks.

Availability and sales fields

available returns true or false. Use it for stock monitoring, product availability alerts, and catalog suppression rules.

sold gives the product-level sold count. In the sample response, the product has 55 sold.

reviews_count gives the count of reviews on the product page. In the sample, that value is 11.

Sales and review counts are page-level counters. Store them as observed values rather than permanent product attributes.

If you refresh daily, these counters become time-series data. That makes velocity checks possible without extra scraping.

Variant fields

colors returns an array of color options. The sample includes Distressed Black, Distressed Green, and Matte Natural.

sizes can return an array or null. Code against both shapes.

shipping_fee can return a value or null. Store it as nullable.

For variants, preserve the original array order. Merchants often place the default or promoted option first, and that order matters for storefront audits.

If you need variant-level inventory, store each variant value in a child table. Product-level rows cannot represent every combination cleanly.

Specification fields

specifications is an array of objects with title and value.

This format works well in a child table:

product_url title value
TikTok Shop product URL CA Prop 65: Repro. Chems No
TikTok Shop product URL CA Prop 65: Carcinogens No
TikTok Shop product URL Contains Batteries or Cells? None

Avoid flattening every spec into a product column unless your category set is small. TikTok Shop categories use different spec names, and a wide table becomes hard to maintain.

A child table also handles repeated spec names. If a merchant changes casing or punctuation, your ingest job can store the raw value first.

Store fields

store_details gives you seller metadata. The sample includes the store name, store URL, item count, rating, sold count, follower count, and badge image.

Useful fields include:

  • store_details.name
  • store_details.url
  • store_details.num_of_items
  • store_details.rating
  • store_details.num_sold
  • store_details.followers
  • store_details.badge

Use store_details.url as the seller key when you build seller-level tables. Store names can change, while the seller URL gives you a steadier join key.

If you enrich sellers separately, the Get TikTok profile data scraper can pull profile data when your dataset includes creator or brand profile URLs.

Keep seller metrics in their own table. Product refreshes can update seller follower counts, ratings, and item counts without overwriting product facts.

Image fields

images returns an array of image URLs. Store the array as JSON if you need the raw response.

If your app needs one primary image, use images[0] and keep the full array in a secondary field. This keeps the product table small while preserving every image URL for media checks.

For image pipelines, download media outside the scraper job. Queue image fetches separately so product extraction does not block on media storage, resizing, or hash generation.

Store image hashes if you track duplicate products. Image similarity catches catalog copies that use different titles or seller names.

Ready to get this data? Get TikTok Shop product data.

Production tips for clean TikTok Shop data

Validate inputs before starting jobs

Reject empty strings and non-TikTok URLs before you call the API.

from urllib.parse import urlparse

def validate_tiktok_url(url: str) -> None:
    parsed = urlparse(url)

    if parsed.scheme != "https":
        raise ValueError(f"Invalid scheme: {url}")

    if parsed.netloc != "www.tiktok.com":
        raise ValueError(f"Invalid host: {url}")

    if not parsed.path:
        raise ValueError(f"Missing path: {url}")


urls = [
    "https://www.tiktok.com/shop/pdp/glarry-6-string-acoustic-electric-guitar-starter-kit-amp-amp/1729495699318543725"
]

for url in urls:
    validate_tiktok_url(url)

Deduplicate by normalized product URL

Strip query parameters before deduplication.

from urllib.parse import urlparse, urlunparse

def normalize_product_url(url: str) -> str:
    parsed = urlparse(url)
    return urlunparse((parsed.scheme, parsed.netloc, parsed.path.rstrip("/"), "", "", ""))


raw_urls = [
    "https://www.tiktok.com/shop/pdp/glarry-6-string-acoustic-electric-guitar-starter-kit-amp-amp/1729495699318543725?source=ecommerce_searchresult",
    "https://www.tiktok.com/shop/pdp/glarry-6-string-acoustic-electric-guitar-starter-kit-amp-amp/1729495699318543725"
]

deduped = sorted({normalize_product_url(url) for url in raw_urls})
print(deduped)

Expected output:

[
  "https://www.tiktok.com/shop/pdp/glarry-6-string-acoustic-electric-guitar-starter-kit-amp-amp/1729495699318543725"
]

Keep both the raw URL and the normalized URL for debugging and joins.

Store raw JSON and typed tables

A practical schema uses 3 tables:

Table Key Purpose
tiktok_shop_products normalized_url Product title, prices, availability, sold count, review count
tiktok_shop_stores store_url Seller name, rating, followers, item count
tiktok_shop_product_specs normalized_url, title Product specification rows

Add a raw table with job_id, input_url, normalized_url, scrape_status, scraped_at, and the full JSON payload so you can replay old payloads during schema changes.

Handle failed rows without dropping the batch

Check scrape_status per row.

def split_results(rows: list[dict]) -> tuple[list[dict], list[dict]]:
    good = []
    bad = []

    for row in rows:
        if row.get("scrape_status") == "success":
            good.append(row)
        else:
            bad.append({
                "input": row.get("inputs"),
                "status": row.get("scrape_status"),
                "url": row.get("url")
            })

    return good, bad

Cap retries at 2-3 attempts. Separate transient failures from permanent input failures so timeouts get retried and invalid URLs go to a rejection report.

Where this fits in a TikTok scraping workflow

TikTok Shop product extraction covers the commerce page. TikTok post and profile data cover the content around the product.

Use this split:

Data needed Scraper to run
Product title, price, variants, specs, store details the TikTok Shop Extract by URL scraper
Product discovery by category tiktok-shop-search-by-category
Product discovery by seller tiktok-shop-search-by-shop
Video metadata from known post URLs Extract TikTok post data
Videos from a profile URL Pull posts from a TikTok profile
Videos from search terms Search TikTok posts by keyword

A common workflow starts with category search, then extracts every discovered product page. After that, a scheduled job refreshes known PDPs daily and records price, availability, sold count, and review count changes.

For seller monitoring, start with shop search. Extract every product URL from the seller, join rows on store_details.url, and keep one seller table plus one product table.

The full ScrapeNow scraper catalog lives under Browse all 86+ scrapers. It includes TikTok, Amazon, Google, LinkedIn, Instagram, YouTube, and other platforms.

Pricing

ScrapeNow charges per returned row. One row costs one credit, starting at $0.04 per credit for small runs and dropping with volume. No monthly contracts, no proxy fees, no charges for failed rows. See the pricing page for current rates.

Your next step is specific. Open Get TikTok Shop product data, paste one PDP URL into the script, run it, and inspect scrape_status, title, final_price, store_details.url, and images[0] before you send a batch.

Related articles

View all

Start collecting data in under five minutes.

Free credits included - no credit card required.

Free credits included - no credit card required