Find profitable pet products to dropship from China's largest wholesale platform. Get English titles, dropship-ready scores, estimated landed costs, and compliance hints — all in one API call.
Version history and upgrade guidance. For current usage, see README.md.
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.
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).
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).
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.
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.
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 (>, &, …) 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).