Back to Blog

TikTok Shop Price Tracking with Product Snapshots in Python

Daniel Kim
Daniel Kim

Lead Scraping Automation Engineer

31-Aug-2026

TL;DR:

  • Price tracking is a snapshot pipeline. Call scraper.tiktok.shop.page on a schedule, timestamp each response, and compare like with like.
  • The comparison key includes market context. Product ID, returned region, currency, and SKU identity prevent false matches.
  • Product and SKU changes are different events. Track displayed product price and stock separately from variant availability or price.
  • Missing is not zero. An absent field should not trigger an out-of-stock or free-price alert.
  • The actor does not create history or alerts. SQLite storage, comparison rules, scheduling, and delivery live in the calling application.
  • Free to start. New Scrapeless accounts include free credit through the Scrapeless Dashboard.

Introduction: a tracker is more than a repeated request

A single TikTok Shop product response answers what the page exposes now. A price tracker answers a different question: what changed between two comparable observations, and is the change meaningful enough to notify someone?

That distinction affects the entire design. The Scrapeless Shop actor supplies structured product snapshots. The application adds a timestamp, stores product and SKU rows, compares each row with its prior observation, and emits an alert-ready event. This guide builds that pipeline with Python and SQLite.

For the request pattern behind the product snapshots, read the live Scrapeless data actor guide.

Pipeline at a Glance

Stage Action Durable output
Fetch Request a known product by ID and region Raw JSON snapshot
Normalize Separate product and SKU fields Comparable rows
Store Insert timestamped observations Product and SKU history
Compare Find the prior matching record Change events
Deliver Apply business rules Alert-ready JSON

This pipeline monitors a product list supplied by the application. It does not discover every product in a shop or expose a private order ledger.

Prerequisites

  • A Scrapeless account and API token from the Scrapeless Dashboard
  • Python with the standard library and SQLite support
  • Known public TikTok Shop product IDs
  • A region rule for every product
  • An alert policy that defines which changes matter

The code requires a live token in SCRAPELESS_API_KEY. The product list is supplied by your application.

Stage 1 — Fetch a Product Snapshot

Call POST https://api.scrapeless.com/api/v1/scraper/request with actor scraper.tiktok.shop.page. Its input contains product_id as a string and region. The TikTok Shop page actor documentation shows the request and response fields.

The result can include product identity, returned region, seller, name, sold count, price, currency, stock, rating, review count, images, options, SKUs, categories, and shipping details. Store the raw response before normalization so later schema changes can be handled without recollecting the same observation.

Stage 2 — Normalize Product and SKU Rows

Use a product key made from product ID and returned region. For variants, add the SKU identifier. Currency belongs in the comparison record; a numeric price has no safe cross-market meaning on its own.

The ISO currency code reference explains the role of currency identifiers. Keep the source value, even when a downstream reporting layer also converts prices.

Product and variant inventory should remain separate. TikTok Shop's inventory documentation treats product and SKU identifiers as distinct query dimensions, which matches a clean snapshot schema.

Start Scraping with Scrapeless

Power up your web scraping and automation workflow with Scrapeless!
Sign up today and get $5 in free creditno credit card required.

Claim your free credit now in the Scrapeless Dashboard.

Stage 3 — Store Snapshots in SQLite

SQLite works well for a small monitor because the database and time functions are available from Python without a separate service. The SQLite CREATE TABLE documentation defines the table constraints used in the schema.

python Copy
import json
import os
import sqlite3
from datetime import datetime, timezone
from urllib.request import Request, urlopen

ENDPOINT = "https://api.scrapeless.com/api/v1/scraper/request"


def fetch_product(product_id, region):
    body = json.dumps({
        "actor": "scraper.tiktok.shop.page",
        "input": {"product_id": str(product_id), "region": region},
    }).encode()
    request = Request(
        ENDPOINT,
        data=body,
        headers={
            "x-api-token": os.environ["SCRAPELESS_API_KEY"],
            "content-type": "application/json",
        },
        method="POST",
    )
    with urlopen(request, timeout=60) as response:
        return json.load(response)


def open_database(path="tiktok_shop_history.sqlite3"):
    database = sqlite3.connect(path)
    database.execute("""
        CREATE TABLE IF NOT EXISTS product_snapshots (
            observed_at TEXT NOT NULL,
            product_id TEXT NOT NULL,
            region TEXT NOT NULL,
            currency TEXT,
            price TEXT,
            stock INTEGER,
            sold_count INTEGER,
            raw_json TEXT NOT NULL,
            PRIMARY KEY (observed_at, product_id, region)
        )
    """)
    return database


def store_product(database, raw):
    observed_at = datetime.now(timezone.utc).isoformat()
    row = (
        observed_at,
        str(raw.get("product_id", "")),
        str(raw.get("region", "")),
        raw.get("currency"),
        None if raw.get("price") is None else str(raw.get("price")),
        raw.get("stock"),
        raw.get("sold_count"),
        json.dumps(raw, ensure_ascii=False),
    )
    database.execute(
        "INSERT INTO product_snapshots VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
        row,
    )
    database.commit()
    return row

Store price as source text or an exact decimal type in systems that support it. Binary floating-point is a poor storage choice for money because some decimal values cannot be represented exactly.

Stage 4 — Compare With the Prior Observation

Comparison should occur only within the same product ID, returned region, and currency. Query the most recent earlier row, then produce an event for fields that are present in both observations.

python Copy
from decimal import Decimal


def previous_snapshot(database, product_id, region, observed_at):
    return database.execute(
        """
        SELECT observed_at, currency, price, stock
        FROM product_snapshots
        WHERE product_id = ? AND region = ? AND observed_at < ?
        ORDER BY observed_at DESC
        LIMIT 1
        """,
        (str(product_id), region, observed_at),
    ).fetchone()


def compare(current, previous):
    if previous is None:
        return {"state": "baseline", "changes": []}

    _, old_currency, old_price, old_stock = previous
    changes = []
    if current[3] == old_currency and current[4] is not None and old_price is not None:
        new_price = Decimal(current[4])
        prior_price = Decimal(old_price)
        if new_price != prior_price:
            changes.append({
                "field": "price",
                "before": str(prior_price),
                "after": str(new_price),
                "currency": current[3],
            })
    if current[5] is not None and old_stock is not None and current[5] != old_stock:
        changes.append({"field": "stock", "before": old_stock, "after": current[5]})
    return {"state": "compared", "changes": changes}


database = open_database()
raw = fetch_product(os.environ["TIKTOK_SHOP_PRODUCT_ID"], "GB")
current = store_product(database, raw)
prior = previous_snapshot(database, current[1], current[2], current[0])
print(json.dumps(compare(current, prior), indent=2))

The first observation establishes a baseline and should not produce a price-change alert. A missing value also should not become zero or an out-of-stock event.

Stage 5 — Track SKU Changes

Create a second history table keyed by observation time, product ID, returned region, and SKU ID. Store the option labels, SKU price when exposed, currency, and availability. Compare a SKU only with its own prior row.

Variant lists can change. A newly observed SKU is a discovery event; a missing SKU is an absence that needs confirmation under the application's collection policy. Do not declare a variant discontinued from one missing response.

Stage 6 — Produce Alert-Ready Events

Keep collection separate from notification. The comparison job can write events such as:

  • product price changed in the same currency and region
  • aggregate stock crossed a business threshold
  • a known SKU's availability changed
  • a new SKU appeared

Apply thresholds after exact changes are calculated. Include the prior and current observation times, product ID, region, currency, field, and before/after values. A downstream worker can route the event to email, chat, a webhook, or an internal dashboard.

Read sold_count Carefully

Preserve sold_count as returned with the product snapshot. It does not identify a reporting window, individual orders, refunds, or GMV. A change between observations is a page-label change, not a complete sales ledger.

Scrapeless provides the product actor through Scraping API. Use the current pricing page to estimate the cost of the product set and chosen schedule.

Conclusion: keep snapshots comparable

TikTok Shop price tracking needs a stable key, a collection timestamp, separate product and SKU history, and cautious change rules. The actor supplies each public product snapshot; the surrounding Python job turns those observations into a history and alert-ready events.

Ready to Build Your Price Monitor?

Join the Scrapeless Discord or Telegram community to discuss monitoring schemas. Create an account in the Scrapeless Dashboard when the product list and alert policy are ready.

FAQ

Q: Does Scrapeless provide automatic TikTok Shop price history?

No. The actor returns product snapshots; the calling application schedules requests, stores observations, compares rows, and delivers alerts.

Q: What key should a TikTok Shop price tracker use?

Use product ID plus returned region for product records, and add SKU ID for variant records. Keep currency in every price comparison.

Q: Should a missing price or stock value be stored as zero?

No. Missing means unknown, while zero is a definite value with a different business meaning.

Q: Can sold count be used as daily sales or GMV?

No. The returned label does not establish a reporting window or provide a complete order and revenue ledger.

Q: How often should the tracker run?

Choose a cadence based on the business decision, product volatility, permitted use, and budget. Record the schedule so gaps in the history remain visible.

Q: Is monitoring public Shop prices legal?

Legality depends on jurisdiction, market, purpose, access method, and applicable terms. Monitor public product data for a permitted purpose and obtain legal advice for the intended use.

Q: Do I need to manage proxies, page defenses, or DOM selectors?

Scrapeless handles the collection surface. The application manages the product list, timestamps, storage, comparisons, and notifications.

Q: Can this run without an AI agent?

Yes. The workflow uses direct HTTP requests, SQLite, and ordinary Python code.

At Scrapeless, we only access publicly available data while strictly complying with applicable laws, regulations, and website privacy policies. The content in this blog is for demonstration purposes only and does not involve any illegal or infringing activities. We make no guarantees and disclaim all liability for the use of information from this blog or third-party links. Before engaging in any scraping activities, consult your legal advisor and review the target website's terms of service or obtain the necessary permissions.

Most Popular Articles

Catalogue