# Changelog of 1688 Pet Supplies Scraper — $0.0045/row + $0.01/run (`crawleast/china-pet-supplies-scraper`) Actor

- **URL**: https://apify.com/crawleast/china-pet-supplies-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/crawleast/china-pet-supplies-scraper.md

## Changelog — 1688 Pet Supplies Scraper

Version history and upgrade guidance. For current usage, see [README.md](./README.md).

***

#### 0.4.18 — Metadata only (2026-09-16)

No input, output, billing or pipeline changes.

- **`meta.categories` realigned to `["ECOMMERCE","AGENTS"]`** to match the live Store listing. The local file declared only `ECOMMERCE`, so a future push would have silently dropped the AI-agents category placement.
- One source comment lost its competitor reference (comment text only).

#### 0.4.14 — Task #236 Flywheel 2.0: cookie-jar hygiene (2026-08-26)

Warm-token flywheel hardening; no input, output or billing changes.
Task #234 proved cross-run warm-jar replay works ONLY with a clean,
deduplicated jar (duplicate/stale `_m_h5_tk` copies were the historical
replay killer — clean deduped jar = 7/7).

- New `src/jar-hygiene.js`: `dedupeJar` (most-specific-domain-wins) +
  `_m_h5_tk` re-parse from the deduped jar (+ unit tests).
- Every KV write-back (mint-on-miss / detail-dedicated mint / sliding
  renewal) now publishes the DEDUPLICATED full jar with the token pair
  RE-PARSED from it.
- Browser-channel jar seeding (detail fallback addCookies) now seeds the
  deduplicated jar. Canary / rejection fallback semantics unchanged.

#### 0.4.12 — README trust elements (Task #229) (2026-08-26)

Documentation-only release; no code, input, output or billing changes.

- **Trust at a glance table**: seven verifiable commitments (pay only for
  delivered results, no hidden fees, honest degradation disclosed in
  SUMMARY, flat fair pricing, schema-declared MCP/agentic-payment
  readiness, fast Issue response, live Store-page reliability stats).
- **One-click examples**: direct links to the five public quick-start
  tasks (10-product quick start, 50-item deep research, price-ladder
  scan, offer-ID lookup, fast catalog scan).
- **New "Copy to your AI assistant" section**: API-first narrative with
  the `run-sync-get-dataset-items` endpoint, `?timeout=900` guidance for
  larger orders and 408 polling recovery — same shape as the image-search
  Actor's section.
- **Reviews section**: compliance-safe, non-incentivized review request
  plus Issue-first guidance.

***

#### 0.4.11 — Shared warm-token cache (Task #228) (2026-08-26)

Token-cache plumbing only; no input, output or pipeline-behaviour changes.

- **2c — shared-cache REST read**: when the injected KV env vars
  (`MTOP_KV_API_TOKEN` + `MTOP_KV_STORE_ID`) are present, `get()` reads the
  minter-published warm token from the shared KV store via the Apify REST
  API BEFORE the SDK path. A fresh (<25 min) entry is used as-is
  (`shared-cache: hit`); any failure silently degrades to the existing
  SDK/bootstrap path (`shared-cache: miss`). Works for external users too,
  whose SDK cannot see the owner's named store.
- **2d — flywheel write-back**: after a self-bootstrapped token is cached
  locally, it is also PUT back to the shared store (non-fatal on failure).
- **2e — pool_health unchanged**: circuit-breaker entries stay on the
  run-local/default store; they are never read from or written to the
  shared store.
- **Secrets hygiene**: the KV token value never reaches logs, datasets or
  error messages (`shared-cache: hit/miss` only; `scrubSecret` helper).
- New tests: `tests/shared-cache-228.test.js` (hit / miss / degrade /
  flywheel / pool_health isolation / token-never-leaks).

***

#### 0.4.10 — Pricing v2: flat per-row price + run-base-fee deferred billing (2026-08-26)

Billing-model change (platform pricing update + settlement code); no input,
output or pipeline-behaviour changes.

- **Flat dataset-item price $0.0045/row** (was $0.00399 FREE + tiered
  discounts): tiered subscription discounts removed — one honest price for
  every delivered product row, billed by the platform's default-dataset-item
  synthetic event. Failed / filtered rows are never pushed to the default
  dataset and are never billed (verified: single push path, filter rows
  `return false` before buffering).
- **New `run-base-fee` event ($0.01)**: charged once at run completion, and
  only when the run delivered at least one row. Zero-row runs stay $0.
  Settled after ALL flushes (`settleRunBaseFee`, both keyword and offerIds
  paths), guarded by try/catch — a charge failure logs a warning and never
  fails the run.
- **SUMMARY honesty**: `pricePer1000` now `$4.5` and `estimatedCost` uses
  the real v2 arithmetic (`max($0.04, rows × $0.0045 + $0.01)`); new
  `runBaseFee` block reports the delivered count and settlement result.
- **README**: pricing section rewritten (flat price + run fee, no tier
  tables), worked examples recomputed ($0.04 floor / $0.06 / $0.24 / $0.46 /
  $2.26).

***

#### 💰 Pricing update — unified tiered subscription discounts (2026-08-24)

Billing + docs-only change (no code, input or output changes):

- **Unified tiered PPE pricing** (aligned with 1688 Image Search Scraper):
  FREE $0.00399/product (unchanged), BRONZE $0.00359 (−10%),
  SILVER $0.00319 (−20%), GOLD/PLATINUM/DIAMOND $0.00279 (−30%).
- Pure price reduction for subscribers → effective immediately under
  Apify policy (no 14-day waiting period). Appended as a new
  `pricingInfos` record; price history preserved (append-only).
- Unchanged: $0.04 minimum charge, zero-result runs cost $0.00, no
  startup fee (`apify-actor-start` never charged).
- README Pricing section now lists the per-tier price table with the
  discount line; FAQ answer updated accordingly.

***

#### ⬆️ Upgrading to 0.4.8 / Related Actors cross-link

Docs-only change, no input/output/billing changes:

- README gains a **Related Actors** section cross-linking the new
  **1688 Image Search Scraper** (`crawleast/1688-image-search-scraper`)
  with the recommended pipeline: discover by keyword (this Actor) →
  source by photo (Image Search Scraper) → re-check finalists via its
  `offerIds` detail-only mode. The Store link resolves once Actor 2
  passes its W4 publication gate.

***

#### ⬆️ Upgrading to 0.4.7 / Billing wording alignment + zero-result honesty

No input or output data changes. Operations-review fix batch on top of 0.4.6:

- **Pricing wording corrected.** The README Pricing section now states the actual billing precisely: per-product billing ($3.99/1,000 delivered) applies only to runs that deliver at least one product (floored at $0.04); **a run that returns nothing costs $0.00** — the earlier wording wrongly implied a $0.04 charge on zero-result runs. Added a `0 results → $0.00` row to the price table and a "Run outcomes" section documenting the three run endings (products delivered / zero results / empty input NO_INPUT) and where `errorCode` lives (SUMMARY and OUTPUT key-value records).
- **SUMMARY estimatedCost honesty.** Zero-result runs now report `estimatedCost: "$0.00"` instead of "$0.04" — the minimum-charge floor only fires when ≥1 product is delivered (cloud-verified in 0.4.6: zero-event runs charge exactly $0). Aligned with the NO_INPUT path.
- **OUTPUT record declared in the key-value store schema** alongside SUMMARY, so both run-report keys are typed collections.
- **Title consistency.** The input schema title now matches the Actor title: "1688 Pet Supplies Scraper — China Wholesale Market Research".

***

#### ⬆️ Upgrading to 0.4.6 / Billing fairness alignment

No input or output data changes for normal runs. Billing-fairness batch:

- **Startup fee removed.** The $0.005 per-run startup fee is gone — pricing is now $3.99 per 1,000 products with the $0.04 per-run minimum charge as the only floor. SUMMARY `estimatedCost` arithmetic updated to match.
- **Empty input no longer fails the run.** When both `keywords` and `offerIds` are empty/blank, the run now finishes SUCCEEDED with `errorCode: "NO_INPUT"` written to the `SUMMARY` and `OUTPUT` key-value records (previously a FAILED run). Nothing is researched and no per-product events are charged, so a misfired run costs nothing beyond the platform's treatment of zero-event runs.
- **Golden example input.** The Store example run input is now the small golden case (`{"keywords": ["dog leash"], "maxResults": 5}`) instead of the template placeholder.

***

#### ⬆️ Upgrading to 0.4.5 / Actor-level Output schema (Store publishing requirement)

No input or output data changes. Added the Actor-level output schema (`.actor/output_schema.json`, mounted via the `output` key in `actor.json`) that the Apify Store publishing gate requires on top of the dataset and key-value store schemas: it exposes the product dataset (`results`) and the SUMMARY report (`runSummary`) as named run outputs with direct API links. Delivered rows and SUMMARY content are unchanged from 0.4.4.

***

#### ⬆️ Upgrading to 0.4.4 / Key-value store schema completes Output schema

No input or output data changes. Added a key-value store schema (`.actor/key_value_store_schema.json`) declaring the run's `SUMMARY` record as a typed collection (application/json), completing the Apify Store publishing "Output schema" requirement alongside the dataset schema added in 0.4.3. Delivered rows and the SUMMARY content are unchanged from 0.4.3.

***

#### ⬆️ Upgrading to 0.4.3 / Output schema for Store publishing

No input or output data changes. Added a dataset output schema (`.actor/dataset_schema.json`) describing every delivered field — with English descriptions, types and examples — plus an "Overview" table view for the Console Output tab. This satisfies the Apify Store publishing requirement that an Actor ship an Output schema; the actual rows delivered are byte-for-byte identical to 0.4.2.

***

#### ⬆️ Upgrading to 0.4.2 / Listing & keyword refresh, version alignment

No input or output changes. This release refreshes the Actor's store listing around its real data source — 1688.com, Alibaba's domestic wholesale marketplace — so buyers searching for 1688 scraping, wholesale sourcing and dropshipping research find us accurately, and aligns the actor metadata version with the package version (0.4.2) for consistency. Release notes like this one move out of the README into this CHANGELOG; behaviour is unchanged from 0.4.1.

***

#### ⬆️ Upgrading to 0.4.1 / Real sorting, standalone offerId lookups, safer delivery

No input changes. Three improvements for existing users: you can now look up specific products by `offerIds` alone — no search keywords needed; `sortBy=priceAsc` / `priceDesc` / `dropshipScore` now genuinely re-order your ENTIRE delivered dataset (previously the platform's raw order could leak through); and data safety is stronger — if a run is interrupted or the platform moves it to another server, everything collected up to that point is still delivered instead of being lost. Note: the three sorting modes deliver all results in one ordered batch at the end of the run, so the dataset stays empty until then (expected).

***

#### ⬆️ Upgrading to 0.4.0 / First stable public release

No input changes. 0.4.0 is the first stable, generally available release: every order size (from 10 to 100 items) is verified end-to-end on the Apify cloud with full item counts and full detail coverage, backed by a repeatable cloud test-case suite. Billing stays honest and transparent — you only ever pay for the rows actually delivered. Nothing changes for existing users; this milestone simply marks a release we are confident to stand behind publicly.

***

#### ⬆️ Upgrading to 0.3.41 / Completed orders no longer reported as incomplete

No input changes. Fixed a reporting bug: when your order was fully filled (e.g. 60/60 items delivered) but an earlier search pass had ended early, the summary could still claim the run was incomplete (`partial: true`). A completed order now always reports `shardStats.stopReason: "filled"` with no `partial` flag — you will never rerun an order that was already finished. Also: cloud runs started without `proxyConfiguration` no longer go out on a bare datacenter connection (which 1688 blocks outright) — the Actor now automatically falls back to the RESIDENTIAL/CN proxy preset and logs a warning; an explicitly provided `proxyConfiguration` is still used exactly as given.

***

#### ⬆️ Upgrading to 0.3.40 / Big orders wait out platform blocks instead of giving up

No input changes. When a large order hits the platform's temporary session-blocking wall, the Actor now waits briefly (~90 seconds) and tries once more before deciding to end early — leftover run time is spent on completing your order instead of being abandoned, and runs that genuinely cannot recover still stop honestly with `partialReason: "identity_mint_fuse"` (now only after the wait-and-retry has been spent). `SUMMARY.shardStats` gains two observability fields: `mintCooldownRetries` and `mintCooldownRecovered`.

***

#### ⬆️ Upgrading to 0.3.39 / Cleaner data, no breaking changes

No required input changes and no delivery-mechanics changes — this build is a data-quality pass over everything you receive. A few output values change shape for the better (flagged below and in "Field-semantics notes").

- **Cleaner text:** HTML entities (`&gt;`, `&amp;`, …) and inline markup (`<span style=…>`) are decoded/stripped from all output text fields (SKU specs, guarantee descriptions, titles, tags).
- **Numeric hygiene:** `weightKg` is rounded to 3 decimals; `quantityPrices` tiers are deduplicated and strictly ascending by `minQuantity`; impossible stock sentinels (>10,000,000) are reported as `null` (unknown) instead of misleading mega-numbers.
- **Trimmed supplier ratings:** `supplier.rateInfo` now carries only `positiveRate` and `score`; other sub-fields previously present there have been removed.
- **Tighter price ladders:** in `quantityPrices`, tiers missing a numeric `minQuantity` are dropped — they are unusable for range logic.
- **Clean URLs:** `detailUrl` is now always the canonical desktop link `https://detail.1688.com/offer/{id}.html` — no tracking parameters, no mobile/ad URL forms.
- **Consistent flags:** `isSuperFactory` is derived from `supplier.sellerType`, `isP4P` also detects paid placements from the ad URL markers, and `dropshipGrade` now keeps just the letter (`D+`) with the caveat moved to a separate `scoreConfidence` field (`ok` / `insufficient_data`).
- **English logs:** all run-log wording is neutral English (internal codenames removed).
- **Input convenience:** `keywords` also accepts the common aliases `query` / `search` when `keywords` is absent.

**If your code parses output fields, note three shape changes:** `dropshipGrade` is now a bare letter (`"B"` — previously it could carry a confidence caveat in the same string, which now lives in the new `scoreConfidence` field); `stock.total` / `stock.skus[].count` can now be `null` (previously always numeric); `detailUrl` strings change form (canonical, parameter-free), so URL-based dedup against older exports will not match.

***

#### ⬆️ Upgrading to 0.3.38 / Large orders deliver more reliably

No input changes — this build makes large orders more reliable and gives mid-size orders a bigger detail budget:

- **Big orders (56+ items) find more candidates:** the Actor now scans roughly 4× more search results before filtering (up to ~420 candidates for a 100-item order, previously ~250), so 100-item orders are far less likely to end early with "not enough products found".
- **Page-turning fixed:** large runs that need extra search pages mid-run no longer stall; the follow-up search path that had been degraded since 0.3.7 is restored as the primary route.
- **Early exit instead of wasted spend:** if the platform starts rejecting the Actor's browsing sessions faster than it can recover, the run stops early, delivers everything collected so far (every row still full-detail), and reports `partialReason: "identity_mint_fuse"` — you pay only for what was delivered. In practice: simply rerun; the next run starts with fresh sessions.
- **Mid-size orders (11–55 items) get a bigger detail budget:** raised from 55 to 85 detail calls, so 30–50 item orders keep full details even when some detail requests are blocked.
- **New keyword:** "pet toy" / "pet toys" now work out of the box.

**One honest caveat:** on large orders, part of the work depends on freshly created browsing sessions, and the platform sometimes blocks them. This never reduces the quality of what you receive — every delivered row keeps full details — it can only reduce how many rows arrive in a single run. If SUMMARY shows `partial: true`, rerun the Actor; billing is per delivered row either way.

***

#### ⬆️ Upgrading to 0.3.36 / Behaviour change

**Orders above 55 items change default behaviour.** Starting with this build, an order with `maxResults > 55` no longer returns the legacy mixed shape (full details up to the budget, search-card rows for the surplus). It runs **full-detail sharding (SFD)** by default:

- **Shape:** every delivered row carries a fetched detail layer — no minimal-card padding, ever.
- **Runtime:** typically ~7–9 minutes instead of ~3 minutes (each shard mints a fresh browser identity with its own ~55-call detail budget).
- **Count honesty:** when the post-detail survival rate is pathological (below ~20%), the delivered COUNT may fall short of `maxResults`. The SUMMARY states exactly why (`partial` + `partialReason` + `shardStats.stopReason`).
- **Billing:** this is a pay-per-result Actor — you are charged for the rows actually delivered, not for what you ordered, so a capped run costs proportionally less.

**Opt out:** set `fullDetailSharding: false` to restore the legacy behaviour (surplus delivered at search-card level within the ~3-minute budget).
