Find the factory behind any product photo: reverse image search on 1688.com with supplier prices, MOQ ladders, landed cost and dropship scores — pay only for successful matches.
0.3.8 — 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, subscriber discounts, schema-declared MCP/agentic-payment
readiness, fast Issue response, live Store-page reliability stats).
One-click examples: direct links to the three public quick-start
tasks (quick start, full details & landed cost, offer-ID lookup).
Review call-to-action: compliance-safe, non-incentivized review
request in the Reviews section.
Verified complete: the existing "Copy to your AI assistant" section
(run-sync endpoint, ?timeout=900, 408 polling recovery).
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.
New run-base-fee event ($0.01): charged once at run completion, and
only when the run delivered at least one match. Runs that deliver no
matches are not charged. Settled after ALL flushes (settleRunBaseFee),
guarded by try/catch — a charge failure (e.g. the event still in its
14-day pending window) logs a warning and never fails the run.
Pricing update (effective 2026-09-09, 14-day notice for existing
paying users): image-search tiers flattened to $0.005 (FREE/BRONZE) /
$0.0048 (SILVER) / $0.0045 (GOLD+) — discount depth capped at 10%;
detail-item unchanged; $0.04 minimum per run retained.
No functional changes — content is identical to the 0.3.4 line plus the
docs-only pricing update that was shipped as 0.2.12/0.2.13.
Version-line restoration: the 0.2.12/0.2.13 pushes were traced to
this repository (CLI origin, timestamps match the 0.2.0 snapshot and
pricing-docs pushes); they were legitimate but retired the latest
tag onto the 0.2 series. Version files restored to the 0.3 line
(package.json 0.3.5, actor.json 0.3) so latest points back at the
current code line.
Default build pinning: Actor defaultRunOptions.build pinned to
the exact 0.3.5 build number, so Console default runs and Store
"Try" no longer follow a bare latest tag. The daily canary
schedule intentionally keeps latest as a hijack early-warning.
Pure price reduction → effective immediately under Apify policy (no
14-day waiting period). Appended as a new pricingInfos record;
price history preserved (append-only). minimalMaxTotalChargeUsd
stays $0.04.
Unchanged: no start fee, failed/undelivered work never charged.
README discount line updated to −10/−20/−30; worked example now
states it is computed at FREE-tier prices.
Fixes against the independent 20-case test report (build 0.3.2). Billing
semantics untouched: pay-per-success events unchanged, pricingInfos
append-only.
IS-1 [High] titleEn Chinese residue (34/34 dictionary-only): root
cause was translateTitle(..., { useApi: false }) in enrichProduct plus a
dead LibreTranslate endpoint. Fix: keyless machine-translation chain
(Google gtx → MyMemory) with 5–6s timeouts and a per-run 3-strike circuit
breaker per endpoint, wired with useApi: true. API outputs still
containing CJK are rejected and fall back to the domain dictionary;
titleCn stays authoritative. README discloses machine-translation
best-effort semantics. New tests/w2-translation.test.js (7 cases,
fetch-mocked).
IS-2 [Med] cold-start canary NETWORK got only 1 of 2 configured
attempts: canary retry loop now treats NETWORK like RISK_CONTROL
(retry once on a fresh identity). Intermediate retryable attempts are
marked diagnostic (no dataset row); only the FINAL outcome is delivered,
preserving the one-item-per-input-image contract.
IS-3 [Low] card-only runs wrote 2 rows per image (null vs 0
detailDelivered): the final settlement flush re-pushed every imageResult
after the per-image flush. Card-only runs now skip the final re-flush and
settle detailDelivered: 0 at per-image flush time — exactly one row per
input image. Enrich-mode snapshot semantics unchanged.
IS-4 [Low] post-cap in-flight details delivered free: intentional
graceful-stop design; now documented in README Limits ("Budget cap stops
gracefully, never mid-delivery").
Cloud verification (build 0.3.3): card-only run yIyhIgzReUSCrD4lT —
1 dataset row for 1 input image (IS-3 closed), detailDelivered=0,
charged {image-search:1} = $0.005, ledger matches. Enrich retry run
SvE6e7QuLD2gqAm02 — 3/3 details delivered, charged {image-search:1,
detail-item:3} = $0.014, translationMethod=api 3/3 with zero Chinese
residue in titleEn (IS-1 closed). First enrich attempt B1iehpMi3l34Kxyh7
hit transient RGV587 risk control on the detail layer (0/3, correctly
UNCHARGED) and self-healed on retry — same pattern as the 0.3.1 staging
evidence.
P1-1 (self-provable competitor claims): README ✅/❌ table — dev00 raw
run counts removed (not reproducible), replaced with "~80–85% run success
rate per Apify Store stats (as of Aug 2026)"; devcake per-row price kept
with an "as of Aug 2026" date anchor.
P1-2 (G-09 zero-result billing evidence): staging build 0.3.2, both
runs SUCCEEDED with ZERO actor charges:
404 image run Raac0FXlP4EWJlgNd (31s): dataset status
IMAGE_FETCH_FAILED, chargeable=false; platform chargedEventCounts
{image-search:0, detail-item:0}; SUMMARY chargedEvents={}, chargedUsd=0.
NO_INPUT empty-input run SlxbcY0LSiETJgzEB (31s): OUTPUT errorCode
NO_INPUT ("nothing to do, nothing charged"); chargedEventCounts
{image-search:0, detail-item:0}; SUMMARY chargedEvents={}, chargedUsd=0.
Both runs bill only platform compute (~$0.002). No apify-actor-start
synthesis event exists in pricingInfos — nothing to disable.
P1-3 (minimal alias mapping restored, contract G-04):
input-validation R4 now MAPS imageUrl→imageUrls (scalar wrapped in
array) and images→imageUrls; imageFile routed by value shape
(data:URI / pure base64 → imagesBase64, otherwise → imageUrls); query
still DROPPED with a warning (this Actor searches by image, not keywords).
NO_INPUT OUTPUT message now reports "Detected unsupported fields: …".
Plan deviation 留痕: W2 had dropped ALL four aliases; the ops audit ruled
the minimal mapping is the G-04 original intent. Tests rewritten + new
P1-4 cap tests (49/49 green). README migration note updated accordingly.
P1-4 (schema/code/README boundary parity): code clamps tightened to
maxImages ≤20 (was 400) and maxResultsPerImage ≤20 (was 60);
DEFAULT_MAX_IMAGES 20 → 10, matching input schema (default 10, cap 20)
and the README input table. README "Up to 20 images × 20 matches" stays
true.
P1-5 (snapshot semantics promoted): dataset schema description +
Overview view description now state "runs write progressive snapshots;
consumers take the last imageResult block"; README Sample output carries
the same note up front.
P2-1: PLATINUM/DIAMOND tiers verified via API — both events carry
explicit tier keys at the −25% price (0.00375 / 0.00225); README wording
("GOLD/PLATINUM/DIAMOND −25%") stays.
P2-4: Copy-to-assistant section adds ?timeout=900 / client-timeout
guidance for large enriched orders (gateway 408 recovery via run polling).
P2-5: dedup/benchmark numbers unified on the recorded source of truth
(20-image benchmark run MrWX8o2ZcMFXC2ZQ0: 371 unique targets,
371/371 delivered & billed; compensation retry 50 offered / 50 delivered).
0.3.0 — W3 productization batch (2026-08-24)
Schemas (all four apify validate-schema classes green):
Input schema v2: 8 buyer fields (imageUrls / imagesBase64 / offerIds /
maxImages [default 10, cap 20] / maxResultsPerImage [default=cap 20] /
enrichDetails / targetMarket [US,EU,UK,AU,global] / maxTotalChargeUsd
[default 2, min 0.04]); NO required fields; diagnostic switches
(useMintedToken, maxRunTimeSecs) removed from the Store form (runtime
still honours them); wide aliases stay runtime-mapped, documented.
Dataset schema (draft-07, exact $schema): one row per query image
(imageResult) or one summary row (offerIdsResult); search-card fields
enriched-detail fields incl. two supplier shapes (card vs detail-page),
riskFlags as {flag,message} objects; views.overview buyer table. NO
always-null fields: reviewCount/rating verified absent from W2 data and
omitted entirely; repurchaseRate kept nullable (can land when a genuine
product-level source exists — 846/846 null in W2, disclosed).
Reverse validation gate (tests/w3-schema.test.js, AJV allErrors):
233 real dataset items / 4514 listing cards / 846 enriched details from
ALL W2 smoke captures pass; corrupted items are rejected (batch-reject
semantics).
Listing: README twelve-section marketplace rewrite (hook with hard
numbers, ✅/❌ comparison naming dev00's 85.3% success rate and
devcake-style per-row billing, real sample output, worked-example pricing,
honest limits incl. snapshot semantics / dedup billing / compensation
retry, FAQ, "Copy to your AI assistant", Related cross-link, review CTA).
Store description 265 chars; SEO description 150 chars; SEO name 43 chars.
Prefill / example run: golden image
https://cbu01.alicdn.com/img/ibank/15418323391_1847764777.jpg validated
on cloud build 0.2.11 (run A9JTov8FNAu9WyPu5: SUCCEEDED in 11s, 1 image ×
5 cards, billed 1 image-search) and set as input prefill + Actor-level
exampleRunInput (drives Apify's daily default-input health check).
Staging: build 0.3.0 pushed; 48h staging window opened with a default
input run + a small enriched order; no Store publication until W4 gate.
ONE serial compensation round after the parallel pool pass: retries only the
pool pass's failed offerIds (no parallel risk-control stacking), gated on
deadline slack = conservative estimate (180s/group) + 60s safety margin.
When the gate closes, the run stays honest-partial and says so
(compensationRetry.reason = 'deadline_slack_insufficient').
Billing semantics unchanged: the compensation round charges only what it
actually delivers; charged = delivered ledger parity is preserved.
Pure gate exported as planCompensationRetry + unit-tested (42/42 green).
README W3 backlog: dataset snapshot semantics + cross-image dedup calibre
(billed detail-item counts unique offerIds; card-level enriched count may
exceed it when one offer appears under several query images).
0.2.10 — SM-04 retest batch (2026-08-24, leader decision A)
Frozen-plan revision (留痕): the big-order deadline budget is re-planned.
DETAIL_SLICE_BIG_MS raised 780s → 2200s; new big-order shape is
bootstrap 48s + image slice 90–300s + detail slice ≤2200s + finish 48s,
kept inside the new 2400s actor timeout. Small orders (≤100 detail targets)
stay on the 330s standard slice — unchanged.
.actor/actor.json defaultRunOptions timeoutSecs raised 1200 → 2400
(min/memory unchanged; the cap is a bounded protection, small runs still
finish in ~45–550s and are billed on actual usage).
Detail identity groups now run on a BOUNDED PARALLEL POOL of ≤3
(DETAIL_PARALLEL_IDENTITIES, mirroring the image-phase precedent) instead
of strictly serial — previous serial throughput (~55 offers/100s) made a
378-offer order impossible inside any platform timeout. Snapshot flush is
serialised through a promise-chain lock; final settlement recomputes
failed = requested − delivered.
effectiveDetailSliceMs (0.2.9) keeps the slice honest when the platform
timeout leaves less room than the plan.
cloud-tests/run-case.mjs: run timeout now env-driven (RUN_TIMEOUT_SECS,
default 1200) with poll wall = timeout + 5 min.
0.2.9 — SM-04 fix batch (2026-08-23)
enrich-mode flush leak fixed: raw image cards no longer settle to the
dataset before enrichment (persistState used to splice the queue empty;
details were charged but never delivered). Progressive flushes now re-queue
a full snapshot each time; consumers take the LAST imageResult block
(append-only dataset). summary.imageFlushCount records snapshot count.
Platform-timeout-aware detail slice: the detail slice shrinks to the real
remaining window (ACTOR_TIMEOUT_AT) and logs a WARNING when clipped.
0.1.0 — Scaffold (2026-08-23)
Repository initialized: package.json (deps mirrored from Actor 1), .actor/actor.json
(timeoutSecs 1200, defaultRunOptions 1024 MB, categories ECOMMERCE), .gitignore.