TikTok Shop Scraper — Price, Sales, Rating by URL · $0.006/item avatar

TikTok Shop Scraper — Price, Sales, Rating by URL · $0.006/item

Pricing

from $4.20 / 1,000 detail-items

Go to Apify Store
TikTok Shop Scraper — Price, Sales, Rating by URL · $0.006/item

TikTok Shop Scraper — Price, Sales, Rating by URL · $0.006/item

TikTok Shop product data from product URLs: title, price, units sold, rating, reviews and shop at $0.006/item, plus a $0.01 run fee on success. Keyword search ($0.004/card) is temporarily unavailable and costs $0. Fresh US proxy session per product; failures are never charged.

Pricing

from $4.20 / 1,000 detail-items

Rating

0.0

(0)

Developer

Kyle Wang

Kyle Wang

Maintained by Community

Actor stats

0

Bookmarked

5

Total users

1

Monthly active users

4 days ago

Last modified

Share

TikTok Shop Scraper — Price, Units Sold, Rating & Shop by Product URL · $0.006/item

Turn TikTok Shop product links into price, units-sold, rating and shop data — then find the exact 1688 factory behind any winner.

Status update (October 2026): keyword search is temporarily unavailable. The search engine this Actor uses to discover TikTok Shop product pages now blocks automated queries, so keyword runs end immediately as SEARCH_UNAVAILABLE and cost $0.00. Product URL mode works normally: paste product links to get full detail rows. Keyword search will come back once a reliable discovery source is in place.

Paste up to 100 TikTok Shop product URLs and get one full detail row per product — title, price with currency, units sold, rating, review count and shop identity ($0.006/item) — plus a transparent $0.01 run fee on successful runs. No TikTok account. No login. Every product is fetched on its own fresh US proxy session. Failed, blocked and empty results are never charged, and a hard budget cap protects every run.

Keywords: TikTok Shop scraper, TikTok product search, TikTok Shop keyword search, viral products, product research, ecommerce data, sold count, social commerce, dropshipping, trend tracking, 1688 supplier lookup

📸 Have a product photo instead? Find the 1688 factory by image with the 1688 Image Search Scraper — snap a TikTok product, match its supplier.


🎯 What you can do with it

  • Competitor price monitoring — feed your competitors' product links, get current price + units sold on a schedule and catch price moves early
  • Sourcing in one workflow — take any winning product's photo (imageUrl) or title into the 1688 actors below and find the exact factory behind it (the moat: nobody else ships a paired 1688 lookup)
  • Trend batch research — paste a batch of links collected from TikTok videos/lives and turn them into a spreadsheet of prices, sales and ratings
  • Store audit at scale — up to 100 product URLs per run: your own store or a supplier's, one dataset out
  • AI-agent product lookups — one API call per batch, machine-readable failure semantics, hard budget cap — built to be called by software
  • Winning-product discovery by keyword (paused with keyword search — see the status note) — type a keyword like "pet grooming brush" and get the product cards going viral on TikTok Shop, relevance-ranked

⚡ Three points that matter

  1. Two entry points, one honest ledger — product URLs deliver full detail rows ($0.006 each); keyword search delivers type=searchResult cards ($0.004 each; temporarily unavailable, see the status note). Fill both and they run independently in the same run, billed separately, with a per-keyword and per-URL verdict ledger.
  2. Honest billing — you pay only for what is actually delivered, plus a transparent $0.01 run fee on runs that deliver anything. Zero-result keywords (NO_MATCH), risk-blocked searches (SEARCH_BLOCKED), truncated cards, captcha-blocked and not-found URLs all cost $0.00.
  3. A budget cap you control — set maxTotalChargeUsd and billing hard-stops at your ceiling. Items beyond the cap are still delivered but marked uncharged — never silently dropped, never over-billed.

🚀 One-click examples (Tasks)

  • Single product check — one product link, full detail row (cheapest trial, and the most reliable URL-mode shape)

The keyword-search Tasks (find trending products by keyword, TikTok product → 1688 supplier lookup, price scan by keyword, weekly trends monitor) are paused together with keyword search and will be republished when it returns.


🔎 Two ways to use it

1. Product URLs — full detail rows for links you already have (up to 100 per run): title, price with currency, units sold, rating, review count, shop identity, image and a data-quality marker.

{
"productUrls": ["https://www.tiktok.com/view/product/1729493620818874839"],
"region": "US"
}
{
"productUrls": ["https://shop.tiktok.com/us/pdp/crop-tops-by-automet/1729493620818874839"],
"enrichShop": true
}
{
"productUrls": ["https://www.tiktok.com/view/product/1729493620818874839",
"https://www.tiktok.com/view/product/1729570313535393936"],
"maxTotalChargeUsd": 0.5
}

2. Keyword search — find what's going viral. (Temporarily unavailable: keywords currently end as SEARCH_UNAVAILABLE at $0; see the status note above.) Give one or more keywords; every TikTok Shop product card we find comes back as one dataset row (type=searchResult) with title, price, relevance rank and the canonical product URL.

{
"searchKeywords": ["pet grooming brush"],
"region": "US",
"maxResultsPerKeyword": 10
}
{
"searchKeywords": ["tiktok made me buy it", "dog vacuum brush"],
"maxResultsPerKeyword": 30,
"includeDetails": true
}
{
"searchKeywords": ["LED face mask"],
"sort": "sales",
"maxResultsPerKeyword": 50
}

Fill both in one run? They are processed independently and billed separately — the SUMMARY ledger splits the two columns.


📦 Sample output

Product URLs deliver one detail row per delivered product:

{
"productId": "1729493620818874839",
"title": "AUTOMET Women's 3-Piece Long Sleeve Crop Tops",
"price": { "value": 39.99, "currency": "USD" },
"soldCount": 140700,
"rating": 4.8,
"reviewCount": 10104,
"shop": { "name": "AUTOMET", "id": null },
"productUrl": "https://www.tiktok.com/view/product/1729493620818874839",
"imageUrl": "https://p16-oec-general-useast5.ttcdn-us.com/tos-useast5-i-…webp",
"region": "US",
"dataQuality": "FULL",
"scrapedAt": "2026-09-01T03:23:06.308Z"
}

Fields the page doesn't expose are null — and dataQuality (FULL / PARTIAL / MINIMAL) tells you exactly how complete each row is. The SUMMARY key-value record additionally accounts for every input URL (delivered / captcha / 404 / deadline, plus INVALID_URL for non-TikTok links and INVALID_ID for obvious placeholder IDs — nothing is dropped silently) with the billing ledger attached.

Any product-link shape works: www.tiktok.com/view/product/<id>, shop.tiktok.com/view/product/<id>, regional …/pdp/<slug>/<id> links, even partner links full of tracking parameters — the numeric product ID is extracted automatically and duplicates are removed.

Keyword search (temporarily unavailable) delivers type=searchResult cards (fields the source doesn't expose are omitted, never null):

{
"type": "searchResult",
"queryKeyword": "pet grooming brush",
"productId": "1729493620818874839",
"title": "Geoorood Dog Grooming All-in-One Kit with Vacuum Brush 2.5L",
"price": { "value": 5.99, "currency": "USD" },
"productUrl": "https://www.tiktok.com/view/product/1729493620818874839",
"region": "US",
"similarityRank": 1,
"scrapedAt": "2026-09-12T03:23:06.308Z"
}

Pricing (pay-per-event)

Three events, one honest promise: failures are never charged. Every charge is itemized in the run's SUMMARY ledger.

EventPriceWhen it's charged
search-item$0.004Per delivered search card (keyword mode) — only cards actually pushed to the dataset. Keyword search is temporarily unavailable, so this event is not charged until it returns
detail-item$0.006Per delivered product row (URL mode, or includeDetails: true enrichment of a search card)
run-base-fee$0.01Once per run, at completion — only when ≥1 card/row was delivered

Fail-not-billed, in full: zero-result keywords (NO_MATCH), risk-blocked searches (SEARCH_BLOCKED), keywords skipped while search is paused (SEARCH_UNAVAILABLE), cards truncated beyond maxResultsPerKeyword, captcha-blocked URLs, not-found URLs and invalid inputs all cost $0.00 — the per-keyword and per-URL SUMMARY ledgers show the verdict for every input.

Subscription discounts apply to the per-item events: BRONZE −10%, SILVER −20%, GOLD / PLATINUM / DIAMOND −30%. The $0.01 run fee is the same on every tier.

Tiersearch-itemdetail-itemrun-base-fee
FREE$0.004$0.006$0.01
BRONZE$0.0036$0.0054$0.01
SILVER$0.0032$0.0048$0.01
GOLD+$0.0028$0.0042$0.01

There is no minimum charge. Small orders bill strictly per event — one delivered product URL costs $0.016 ($0.006 + the $0.01 run fee). The platform price list's minimalMaxTotalChargeUsd ($0.04) is a budget-protection parameter, not a minimum spend. Runs that deliver zero rows/cards are never charged at all (fail-not-billed).

Worked examples (FREE tier, base fee included):

OrderCalculationTotal
1 product URL$0.01 base + 1 × $0.006$0.016
10 product URLs$0.01 base + 10 × $0.006$0.07
100 product URLs$0.01 base + 100 × $0.006$0.61
1 search card$0.01 base + 1 × $0.004$0.014
10 search cards$0.01 base + 10 × $0.004$0.05
30 search cards$0.01 base + 30 × $0.004$0.13
30 cards + detail enrichment$0.01 + 30 × $0.004 + 30 × $0.006$0.31

How this compares to alternatives

This ActorOther TikTok Shop scrapersManual checking
Entry pointsProduct URLs today; keyword search temporarily unavailableUsually search only or URL onlyOne page at a time
Paired 1688 factory lookupYes — same-store workflowNoYou build it
BillingPer delivered item; failures cost $0; hard budget capPer search / per item, failed searches often billedYour hours
Failure honestyPer-input verdict ledger (NO_MATCH / SEARCH_BLOCKED / CAPTCHA / …)——

Limits & caveats (read once, trust us more)

  • Keyword search is temporarily unavailable. Its product-discovery source now blocks automated queries for datacenter and residential addresses alike, so keywords are not searched: each ends as SEARCH_UNAVAILABLE ($0) while product URLs in the same run are processed normally.
  • US-first. The captcha-free detail route is proven for US-marketplace products. Other regions (UK/ID/TH/VN/MY/PH) are accepted and labelled; if a regional page serves a captcha, the row is marked CAPTCHA and not charged.
  • Search coverage follows the index (keyword search is paused — see the status note). When available, keyword search surfaces products that TikTok Shop pages have made discoverable for that term — hot, well-indexed keywords return the richest card sets; very long-tail terms can honestly return NO_MATCH (never charged). Product-intent keywords ("pet grooming brush", "dog vacuum brush") work best.
  • Visible-data only. What the storefront page shows, we extract; private backend data (exact inventory, ad metrics) is not available to any scraper.
  • Pacing by design. Keyword searches are spaced 6–8s per request; URL batches run 12–15s between URLs with a 30s cooldown after any CAPTCHA wall, all on rotating sessions from a dedicated US proxy pool — fast enough for research, gentle enough to stay under the radar. Batch tip: for best results, use 1–2 URLs per run; batch mode is beta — success rate decreases with batch size. If a batch marks a live-looking URL REGION_UNAVAILABLE, retry it alone — batch-level soft blocks clear on single-URL runs ($0 either way).
  • Typical delivery rate is ~1 in 3 under the current CAPTCHA wall (URL mode). TikTok Shop serves an intermittent slider puzzle on the product route; our in-page solver clears most of them, but in real batch runs roughly a third of live URLs come back delivered while the rest are marked CAPTCHA. This is disclosed on purpose: every blocked URL costs $0.00 (fail-not-billed), and the per-URL SUMMARY ledger shows the exact verdict for each link, so you only ever pay for what you receive. Paste more URLs than you strictly need, or re-run the blocked ones — you are never charged for a link we could not open.

FAQ

Do I need a TikTok account? No. Which keywords work best? Keyword search is paused right now — every keyword ends as SEARCH_UNAVAILABLE at $0 (see the status note). When it returns, product-intent keywords ("pet grooming brush", "tiktok made me buy it") give the richest card sets, and a keyword with zero results ends as NO_MATCH at $0. What does similarityRank mean? The relevance position of the card within the keyword's result stream — 1 is the strongest match. Can I get full details for search results? Yes — set includeDetails: true; every card is then enriched through the full product-page pipeline and billed as detail-item on top of search-item. What if a URL is dead or delisted? It's classified (ROUTE_404), listed in the SUMMARY ledger and never charged. What if a page hits a captcha? Same: CAPTCHA verdict, honest ledger, no charge for that URL. Can I schedule this? Yes — run a Task on a schedule (e.g. daily) for price/sold-count tracking; each scheduled run pays only for what it delivers. How do I get product URLs? Collect links from TikTok video product anchors, live-shopping carts, your store backend export, or any shared product link — every shape is accepted. (Running a keyword search first will work again once keyword search returns.)

For AI agents

Built to be called by software, not just clicked by people:

  • Complete output schema — every field typed and documented (input, dataset, key-value and output schemas), so tool-calling loops parse results without guesswork.
  • API-first — one POST to run-sync-get-dataset-items returns the final dataset in a single call; no browser session, login or captcha step.
  • Agentic payments ready — maxTotalChargeUsd sets a hard per-run budget the platform enforces, fail-not-billed (zero-delivery runs cost $0), machine-readable per-input verdict semantics in SUMMARY (keywords: OK / NO_MATCH / SEARCH_BLOCKED / SEARCH_UNAVAILABLE / INVALID_KEYWORD / DEADLINE; URLs: OK / CAPTCHA / ROUTE_404 / PARSE_EMPTY / RATE_LIMITED / IP_BLOCK / DEADLINE / INVALID_URL / INVALID_ID), and a machine-readable run-level errorCode (NO_INPUT / NO_MATCH / SEARCH_BLOCKED / SEARCH_UNAVAILABLE / CAPTCHA / ROUTE_404 / PARSE_EMPTY / PARTIAL_DEADLINE). Safe to hand an autonomous agent a budget and let it decide.

Copy to your AI assistant

Use the Apify Actor crawleast/tiktok-shop-product-scraper to fetch TikTok
Shop product details from product URLs. POST this JSON to
https://api.apify.com/v2/acts/crawleast~tiktok-shop-product-scraper/run-sync-get-dataset-items?token=<APIFY_TOKEN>
body: {"productUrls": ["https://www.tiktok.com/view/product/1729493620818874839"],
"region": "US", "maxTotalChargeUsd": 2}.
Each dataset row is one product with title, price, soldCount, rating,
reviewCount, shop, imageUrl and productUrl; dataQuality says how complete
the row is. Keyword search (searchKeywords) is temporarily unavailable and
returns SEARCH_UNAVAILABLE at $0. The SUMMARY key-value record accounts for
every input and the billing ledger. Report title, price, soldCount and
rating per product.

Keyword requests sent to the same endpoint come back as SEARCH_UNAVAILABLE at $0 until keyword search returns.

For large batches (100 URLs ≈ 15 minutes), append ?timeout=1500 (seconds) to the sync endpoint or poll runs/{runId} — otherwise the gateway may return 408 while the run keeps working.


Input reference

FieldTypeDefaultDescription
productUrlsarray of strings or a single string—Up to 100 TikTok Shop product URLs; any link shape, duplicates removed. A lone string is normalized to a one-item array, and single-value aliases productUrl / url are accepted too. For best results, use 1–2 URLs per run; batch mode is beta — success rate decreases with batch size (anti-bot pressure accumulates across consecutive URLs, so batches pace 12–15s per URL with a 30s cooldown after any wall). In batch runs a REGION_UNAVAILABLE verdict can also reflect a batch-level soft block — retrying that URL in its own single-URL run usually delivers it
regionselectUSMarketplace label attached to each row (US/UK/ID/TH/VN/MY/PH)
enrichShopbooleanfalseBest-effort shop identity from on-page signals (shop storefronts themselves are captcha-walled)
maxTotalChargeUsdnumber2Hard per-run billing ceiling; overflow delivered uncharged and declared
searchKeywordsarray of strings or a single string—Temporarily unavailable: keywords are not searched and end as SEARCH_UNAVAILABLE ($0). When available: keywords to search on TikTok Shop (1–64 chars each, e.g. "pet grooming brush"). Each keyword delivers up to maxResultsPerKeyword cards as type=searchResult rows ($0.004/card). Keywords over 64 chars are rejected per-entry (INVALID_KEYWORD, $0) while the rest run. Combine with productUrls — both are processed independently and billed separately. Leaving both entries empty ends the run honestly as NO_INPUT at $0
maxResultsPerKeywordinteger30Ceiling on search cards per keyword (1–100). Results beyond the cap are truncated and recorded in SUMMARY (truncatedItems) — truncation is never charged
sortselectdefaultSearch result ordering: default (relevance position), sales, priceAsc, priceDesc. Sales/price sorts need includeDetails data; without it the relevance order is kept and noted
includeDetailsbooleanfalseEnrich every search card through the full product-page pipeline (title, price, sold count, rating, reviews, shop) — billed as detail-item ($0.006) on top of search-item
filterByRegionbooleantrueLabeled mode, not a strict filter: biases the keyword's TikTok search stream toward the selected region's marketplace and tags every delivered card with that region — cross-region products are not hard-excluded (product discovery itself is region-agnostic). SUMMARY discloses this explicitly as regionFilterMode: "labeled" on every search run. Turn off for the default US stream
ActorUse it forThen…
TikTok Shop Product Scraper (this one)Price/sold/rating detail from TikTok Shop product URLs—
1688 Product ScraperKeyword research across ANY 1688 category: factory prices, MOQ ladders, landed costFind the China source behind a TikTok winner
1688 Image Search ScraperReverse image search on 1688 — find the factory from a product photoScreenshot a TikTok product → match its supplier

Pipeline: search a keyword with this Actor → spot the winning card → find the factory behind it with the Image Search Scraper (photo) or the Product Scraper (keyword) → validate landed cost before you source. While keyword search is unavailable, start from product links you already have.

Data is collected from publicly available pages for legal use — no login, no private data, no video/creator fields.

Reviews & changelog

Found a bug or have a wishlist? Open an Issue — a fixed bug helps you more than a one-star rating, and we answer fast.

If this run saved you time, a quick review on the Store page helps other buyers find it — 10 seconds, hugely appreciated.

Changelog

  • 0.2.18 (2026-10-17) — Product URLs lead the input form. The Store's Try form, its API code samples and Apify's daily test all start from the form's prefilled values, which in 0.2.17 still opened on the paused keyword field (0.2.17 changed only the example input that API callers read). Product URLs now come first; the keyword fields sit in a separate "temporarily unavailable" section with nothing prefilled. Billing ledger: SUMMARY now books the events the platform reports as settled (chargedCount) rather than every event requested, so it can never show a charge the platform declined at its own per-run limit. Removed the link to the retired 1688 Pet Supplies Scraper. The proxy wording in the 0.2.8 and 0.2.10 entries below is kept as written, with a correction note. Prices unchanged.
  • 0.2.17 (2026-10-16) — Docs-only, product URLs first. The Store title, this README's headline and the example input now lead with product-URL mode (keyword search is still paused); the API example and the copy-to-assistant prompt use a single product URL that delivers a full row, instead of a keyword that ends as SEARCH_UNAVAILABLE. Pricing-copy corrections (prices unchanged): the $0.01 run fee is the same on every tier — the tier table had shown it discounted to $0.009/$0.008/$0.007 — and there is no $0.04 minimum charge: minimalMaxTotalChargeUsd is a budget-protection parameter, so one delivered product URL costs $0.016, not $0.04. No code changes.
  • 0.2.16 (2026-10-16) — Keyword search paused, honestly. The product-discovery source behind keyword search (Brave's search results page) now answers HTTP 429 to every automated request, from datacenter and residential addresses alike, so every keyword run since then delivered nothing. Keywords are no longer sent anywhere: each ends at once as SEARCH_UNAVAILABLE ($0, listed in the SUMMARY ledger, with a run status message), and product URLs in the same run are processed normally. The keyword-search Tasks are unpublished until search returns. Correction: earlier versions of this README described the proxy as residential; it is a dedicated US datacenter proxy pool, and the copy now says so. Also, runs that did not name a build had been served an old 0.1.x build; the Actor's default build is now latest.
  • 0.2.15 (2026-09-16) — Docs-only. Every one-click Task link in this README was dead: three tasks had never been created and five existed but were private, so all eight links led nowhere. The list is now five live tasks, all keyword-mode plus the single-URL check. The URL-batch tasks (10/30/100 URLs) stay unpublished on purpose — this README discloses that batch mode is beta with roughly 1-in-3 delivery, so promoting a 100-URL batch as a one-click starting point would contradict our own guidance. Also added a cross-link to the 1688 Image Search Scraper for readers who have a product photo rather than a link, and the comparison table now describes capability differences instead of naming rival Actors. No code changes.
  • 0.2.14 (2026-09-14) — Docs-only: disclosed the batch soft-block nuance found in self-test (runs ZScq9uL0z5FAbNDHz / sjw6Oxi4BQYssRLFF): a URL that delivers FULL in its own run can come back REGION_UNAVAILABLE inside a batch — TikTok's batch-level soft block serves the same empty shells a dead-region id gets, and no in-page signal separates the two. README and the productUrls input description now tell batch users to retry such URLs in a single-URL run (both verdicts stay $0.00). No code changes.
  • 0.2.13 (2026-09-14) — Soft-shell rotation (self-test run 0lz3il5aKhpfWq6ED): after a solved wall, TikTok can serve a content-less-but-not-blank 200 shell — a hydration blob and footer text with NO product data and NO refusal phrase. 0.2.12 rotated only on fully-blank shells, so dead-region ids died as PARSE_EMPTY after a single session (and the region-shell hit sometimes captured on session 1 was forgotten). A PARSE_EMPTY verdict now rotates through the same 3-session budget: soft-blocked LIVE products get fresh exit IPs (batch delivery-rate recovery), while an id that stays empty through ALL sessions escalates from PARSE_EMPTY to REGION_UNAVAILABLE ($0). includeDetails enrichment rotates on soft shells too. Worst-case batch runtime grows accordingly (each empty URL now spends up to 3 sessions ≈ +2 min/URL) — still well inside the default 3600s timeout; failed URLs stay $0.00.
  • 0.2.12 (2026-09-14) — Blank-shell rotation (self-test evidence, runs WgY3ZteeugTpsGBQk / xIotlq8OiY8xU7DBh): a solved wall on a dead-region URL serves a completely EMPTY 200 shell — no hydration, no refusal text (the refusal phrase only appears on the no-wall SSR path), so 0.2.11 still classified those ids PARSE_EMPTY on a single session. An empty shell is now a burn signal too — it is genuinely ambiguous (dead ids stay blank forever; live soft-blocked products recover on a fresh exit IP), so the URL rotates through the same 3-session budget: a live product gets fresh chances, while an id that stays blank through ALL sessions escalates honestly from PARSE_EMPTY to REGION_UNAVAILABLE ($0). A single-observation blank (no rotations spent) keeps the legacy PARSE_EMPTY verdict. Region-refusal shells keep their own, stronger burn path — never double-counted as blank. Search channel unchanged.
  • 0.2.11 (2026-09-14) — Dead-URL region detection + batch pacing (guardkeeper round-3 evidence, runs 7AMuQC8LX8lhzsuXS / KcaHxoHcUNc2ukB5F / XQhRP9mDKb9syEhpM): (1) D-3 fix — region-refusal dead URLs all collapsed to PARSE_EMPTY in 0.2.10 because the refusal text was matched only against rendered innerText, which appears only after client hydration (the SSR markup carries the refusal earlier); the refusal is now matched against BOTH the rendered text and the raw SSR markup, a solved wall buys the page a 3s settle plus one bounded re-harvest before classification, and PARSE_EMPTY carries an internal sub-classification — a subjectless refusal phrase ("Not available in this country or region") flips it to REGION_UNAVAILABLE ($0, and a hint can never mislabel a page that has real product content). (2) D-6 batch pacing — URL batches (and includeDetails enrichment) now space 12–15s per URL (up from 6–8s) and any wall hit on a URL buys the next one a 30s cooldown; evidence: the SAME URL delivered FULL in a 2-URL batch but died in a 3-URL batch. Worst-case cost for a fully-walled 3-URL batch is ≈+3 min, well inside the default timeout. Keyword-search pacing stays 6–8s — search channel unchanged. For best results, use 1–2 URLs per run; batch mode is beta — success rate decreases with batch size.
  • 0.2.10 (2026-09-14) — Region-shell rotation (evidence-driven, run vEJBYFiAVrULNi1oF): the SAME live product serves REGION_UNAVAILABLE on one residential exit IP and a FULL product page on another — TikTok geo-misjudges part of the proxy pool. The region shell is now a burn signal too: the URL retries on fresh one-shot sessions (same 3-session budget), so IP-geo misjudgments get a chance to clear while truly dead ids still land on the honest REGION_UNAVAILABLE verdict ($0). includeDetails enrichment now uses the exact same burn-driven rotation (and the regionUnavailable classification) as the URL loop — previously it had a single CAPTCHA-only retry, which is why 4/5 cards failed to enrich; rotations are counted in SUMMARY sessionRotations for both channels. (Correction, 0.2.16: the proxy pool is a dedicated US datacenter pool, not residential.)
  • 0.2.9 (2026-09-14) — 0.2.8 hotfix (found in self-test, never shipped to buyers): (1) post-solve transition wait — after TikTok's verifyV2 endpoint confirms a solve (code 200), the page redirects via JS routing, so harvesting immediately read the stale Security-Check shell and misjudged cleared walls as CAPTCHA (burning the rotation budget for nothing); the page is now polled until it actually leaves the captcha shell; (2) SUMMARY crash — sessionRotations was declared inside the URL loop's scope but read after it, FATALing pure-URL runs before OUTPUT was written. Search channel unaffected.
  • 0.2.8 (2026-09-14) — URL-channel wall hardening. D-1: a CAPTCHA only counts as solved when TikTok's own verifyV2 endpoint confirms it — captchaSolved is now a 3-state enum (verifyv2_ok / verifyv2_fail / dom_fallback), so a wall is never reported as "solved" again. D-2: every URL runs on one-shot random residential sessions with burn-driven rotation (verifyV2 reject / webid 503×3 / unsolved CAPTCHA), up to 3 sessions per URL before failing honestly as SESSION_ROTATION_EXHAUSTED ($0); SUMMARY reports the run-wide sessionRotations count. D-3: "Product not available in this country or region" pages are classified REGION_UNAVAILABLE ($0, totals.regionUnavailable) instead of CAPTCHA — dead-region ids get a truthful verdict. D-4: includeDetails enrichment inherits the same fixes; when cards fail to enrich, SUMMARY warns enrichment_channel_degraded and the keyword ledger flags enrichmentDegraded: true. D-5: SUMMARY discloses regionFilterMode: "labeled" (see filterByRegion above). Search-channel behavior is byte-identical to 0.2.7; fail-not-billed untouched. (Correction, 0.2.16: the proxy pool is a dedicated US datacenter pool, not residential.)
  • 0.2.7 (2026-09-12) — Truncation honesty fix: SUMMARY's truncatedItems now counts the real SERP hits beyond maxResultsPerKeyword (Brave serves ~20/keyword; at the 0.2.6 build the count was always 0 because discovery pre-truncated to the cap).
  • 0.2.6 (2026-09-12) — Discovery engine switch DDG → Brave Search: DDG's HTML endpoints began 403-walling every proxy exit (cloud-verified), while Brave serves the same site:tiktok.com/view/product query wall-free with clean listing titles — search cards are back to delivering. filterByRegion now biases the TikTok video stream's region params (US default unchanged, byte-identical). No input/output/price changes.
  • 0.2.5 (2026-09-12) — Keyword-search mode: searchKeywords → type=searchResult cards at $0.004/card (relevance-ranked, optional includeDetails enrichment at $0.006/card), maxResultsPerKeyword 1–100 with free truncation, sort (default/sales/priceAsc/priceDesc), filterByRegion; new failure semantics NO_MATCH / SEARCH_BLOCKED / INVALID_KEYWORD; dataset schema anyOf (detail + search rows, 1688 monitor-mode pattern); search-item pricing event registered. URL mode byte-identical (zero-regression).
  • 0.1.0 (2026-09-01) — Narrow-gate v1: productUrls direct-detail scraper (up to 100/run), www-domain SSR channel (proven captcha-free), 13-field rows with dataQuality markers, per-URL honest ledger, $0.006/item + $0.01 run base fee on success, hard budget cap. Keyword search deferred to v1.5.

Roadmap / post-launch backlog (tracked here for transparency — not yet shipped):

  • Bring keyword search back on a reliable product-discovery source; until then keywords are skipped at $0.
  • Raise the delivery rate above the ~1-in-3 CAPTCHA baseline. Both queued follow-ups landed in 0.2.8: (1) solver hardening — a solve only counts when TikTok's verifyV2 endpoint confirms it (captchaSolved: "verifyv2_ok"; DOM transients are demoted to dom_fallback), and (2) automatic session rotation — wall-hit URLs retry on fresh one-shot proxy sessions (up to 3 per URL) before failing honestly as SESSION_ROTATION_EXHAUSTED ($0). Blocked URLs stay $0.00 and fully auditable in the ledger.