# Changelog of AliExpress Products Scraper & Price Tracker (`myth84/aliexpress-search-scraper`) Actor

- **URL**: https://apify.com/myth84/aliexpress-search-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/myth84/aliexpress-search-scraper.md

### Changelog

History of `aliexpress-search-scraper` (Apify id `cuPvsrxB0bpmqhmV0`, folder `actors/aliexpress-search`). Versions are the Apify build numbers; entries before this file existed were reconstructed from the project journal on 2026-10-19.

#### Unreleased (quality score: input documented for AI agents)

- Brief `docs/cloud-tasks/2026-10-20_studio-quality-score-insights.md` (2026-10-21): the input schema passes the shared checks of `@scraper-studio/core/testing` (`runInputQualityChecks`, `runSearchTextChecks`), the rules of Apify's quality score and of what an AI agent receives through the Apify MCP server.
- Descriptions rewritten: `priceMin`, `priceMax` (whole number in the selected currency with an example, sent as AliExpress' own price filter, `priceMax` at least `priceMin`), `maxResults` (says the form prefill).
- Bounds added to the form: `maximum` 1,000,000,000 on `priceMin`, `priceMax` and `minSold` (the code accepts any non-negative whole number; no realistic value reaches the bound).
- `maxResults` is prefilled with 20 in the form (the default stays 100): the prefilled input Apify tests daily is exactly `inputs/daily-check.json` (20 products, wireless earbuds, US / USD / en), which is unchanged.
- `store.json` gains `searchSynonyms` (AliExpress scraper, AliExpress price tracker, dropshipping, product research), all within the first 3,000 README characters; README input table: prefill and example values for `maxResults`, `priceMin`, `priceMax`.
- No code change: rows, logs and charges are identical.

#### Unreleased (`monitorKey` renamed `monitorStoreName`)

- **`monitorKey` is now `monitorStoreName`** (brief `docs/cloud-tasks/2026-10-20_aliexpress-search-repair-monitor-key.md`, 2026-10-20). Apify's quality score (78/100) asked to "Mark sensitive input fields as secret" because of the word "key"; the field is not a secret, it names the monitoring store. Same meaning, pattern (1 to 44 letters, digits and dashes, lower-cased) and absence of default as before, same name as in the other Actors of the studio; still required when `monitorMode` is on. The store stays `aliexpress-monitor-<monitorStoreName>`.
- **Compatibility**: an input, task or schedule that still sends `monitorKey` gets the same store, hence its previous prices, and a warning in the log asking to rename the field; when `monitorStoreName` is set too, it wins (warning) and an empty `monitorKey` is dropped silently. `monitorKey` is no longer in the input schema.
- Example task `aliexpress-monitor-earbuds-prices` and README (input table, *Price monitoring*, FAQ, AI-agent section) use the new name; the README input table says the former name is still accepted.
- Store checks of every Actor of the studio now refuse an input field whose name contains key, token, cookie, secret, password, session or auth (`SENSITIVE_INPUT_NAME` in `@scraper-studio/core/testing`).
- Rows, `RUN_STATS`, store names and charges are unchanged for the same input.

#### Unreleased (README visibility)

- **README first screen and links** (brief `docs/cloud-tasks/2026-10-20_studio-readme-visibility.md`, 2026-10-20). The Store search indexes only the first ~3,000 README characters, so they now hold: an opening sentence with the phrase agents search and its synonyms, a "No login, no cookies, no personal data" line, the Free-plan price in figures in *How much* ($3.00 per 1,000 products; Apify's $5 monthly credit covers about 1,666), computed from `pricing/2026-10-19_pay-per-event-v2.json` (Free tier of the primary event, one `apify-actor-start` event deducted), and a real output snippet (12 key columns of the README example row). Further down: *Use it from your AI assistant* (MCP URL `https://mcp.apify.com?tools=myth84/<name>` and a ready-to-copy prompt with a minimal input), *Alternatives and switching* (official API, home-made scraper, input-name mapping table, no competitor named), *More scrapers by Flatrow Data Studio* (Store links to the other Actors of the studio) with an invitation to rate the Actor (no counterpart), and the non-affiliation line at the very bottom. The cost section and the MCP cost bullet quote the same figures; the line 1 heading is unchanged. `store.json` now names its studio (`ecommerce`), read by the link check. Checked by `runStoreChecks` (`readmeVisibility`): keyword in the opening prose, snippet equal to the example row and within 3,000 characters, figures equal to the pricing file in force (a new grid fails the test until the README follows), MCP block with a valid input, links to existing Actors of the same studio only, no `fpr=`. No code change: rows, logs and charges are identical.

#### Unreleased (version 0.2, deployed first as a `beta` build)

- **Agent signals** (brief `docs/cloud-tasks/2026-10-20_aliexpress-search-extend-agent-signals.md`, agent panel of 2026-10-20: agents picked competitors for exact sales, price-drop labels and the delivery country):
  - **Exact units sold**: `soldCount` now takes the exact figure of the card data (`pdp_cdi.order`) when it agrees with the label (equal to an exact label, at or above a rounded one): 339 of 339 products with a label in the captures of 2026-10-19, e.g. 981 behind "900+ sold"; otherwise the label's lower bound as before. The label as displayed goes to `attributes.soldCountText`; `RUN_STATS.extra.soldCountsRounded` counts the rows left with a lower bound, with a warning above 20 %. `minSold` therefore compares exact figures.
  - **Direction of a price change**: `attributes.priceDirection` = `drop`, `rise` or `same` on compared rows (absent on new products and currency switches); `changeType` keeps its three values. New input `onlyPriceDrops` (default `false`, needs `monitorMode` on): only the drops are written and charged, every compared price is still recorded, the first run writes nothing and says so. `RUN_STATS.monitor` gains `priceDrops`, `priceRises`, `onlyPriceDrops`; `RUN_STATS.extra` gains `monitorPriceDrops`, `monitorPriceRises`. The monitor record key and format are unchanged: existing schedules keep their reference.
  - **Shipping by country**: `attributes.shipsTo` = the delivery country (as `viewCountryCode`) on every row; `attributes.freeShippingThreshold` from the "Free shipping over 10€" badge (32 of 60 French cards; `shippingFree` stays null on them). README section *Shipping, delivery and units sold by country*: what the cards show for US and FR (no delivery date on French cards).
  - Texts written for AI agents: every input description under 500 characters with its default justified, column descriptions updated (`soldCount`, `shippingFree`, `deliveryEstimate`, `attributes`, `viewCountryCode`, `changeType`), FAQ *How do I get alerted when prices drop?* (the URL question moved to the *Input* section to keep 10 questions), Store description mentions exact units sold and price-drop monitoring.
  - Output schema unchanged (`ecommerce-product/1.1`): the new signals live in `attributes` because the unified schema is shared by the e-commerce Actors. Without the new option, rows differ from the previous build only by `soldCount` (exact) and the new `attributes` keys (replayed traces).

- **Store texts** (brief `docs/cloud-tasks/2026-10-20_studio-promote-store-texts.md`, 2026-10-20): README opens with what you get / for whom / how much, shows the real input of the validation run, a pricing table that points to the Pricing tab (no figure in the text), a *For AI agents (MCP)* section with a minimal input, the key output fields, the cost per 1,000 results and the typical time; FAQ kept to 8–10 questions; no proxy or anti-bot detail left in the texts visible on the Store. Tests on the Store lengths (`seoTitle` ≤ 60, `seoDescription` ≤ 155, `description` ≤ 300), the README structure and the forbidden mentions. No code change.

- **Filters** (brief n°6, 2026-10-19): `minRating`, `minSold`, `shipsFrom`, `choiceOnly`, `listedWithinDays`, applied after each page is read and before charging (a rejected product is never saved, never charged, does not count in `maxResults`); counters in `RUN_STATS.extra` (`filteredOut` and one per filter); a search stops after 5 consecutive pages without a kept product (`searchesStoppedByFilters`).

- **Price monitoring**: `monitorMode` (`off` / `changes` / `all`) and `monitorKey`; previous prices kept in the customer's named key-value store `aliexpress-monitor-<monitorKey>`, one record per search (keywords or URL, country, currency, sort, filters); `changes` returns and charges only new products and price changes, `all` every compared product with its `changeType`; first run = reference (`RUN_STATS.monitorBaseline`); `RUN_STATS.monitor` summary; records written only after a search that ended cleanly (fail-closed); a monitoring run without any change succeeds with an empty dataset; `maxResults` = products compared per search.

- **Output schema `ecommerce-product/1.1`** (additive): `isChoice` (AliExpress Choice badge), `listedAt` (listing date), `changeType`, `previousPrice`, `priceDelta`, `priceDeltaPercent`; new dataset view *Changes*; Overview and Ranking views show the Choice flag and the listing date. Without the new options the rows are identical to 0.1 apart from the new columns (replayed traces).

- README: sections *Filters* and *Price monitoring*, FAQ entries; two more example tasks (`aliexpress-monitor-earbuds-prices`, `aliexpress-choice-top-rated`), 10 in total.

- Internal: the crawl runs on the studio's generic paged-search crawler and Actor runner (`@scraper-studio/core/crawl`, `/actor-runner`); platform code is reduced to AliExpress hooks (search URL, preference cookie, parsing, end of results, sponsored and no-price cards). Requests, rows and log lines are unchanged (replayed traces).

- `RUN_STATS`: `productsSeen` / `productsPushed` become `itemsSeen` / `itemsPushed`; `sponsoredSeen` and `productsWithoutPrice` move under `extra`; new `bytesTransferred`, `bytesTransferredEstimates`; `bytesSource` can be `gzip-estimate`.

- Dataset schema now generated from the unified `ecommerce-product` schema and `.actor/dataset-fields.json` / `.actor/dataset-views.json` (`scripts/gen-dataset-schema.mjs`); generated JSON unchanged.

- Store readiness tests moved to the shared `@scraper-studio/core/testing` checks (same coverage); this changelog referenced from `actor.json`.

#### 0.1.5 — 2026-10-19

- Default memory 256 MB (`defaultMemoryMbytes` in `actor.json`, also set on the Actor card): runs started through the API took the platform default (1,024 MB) and compute was 57 % of the cost. Measured cost ≈ 0.005 $ per 1,000 products at 256 MB.
- Pay-per-event pricing set through the API: `result` « Product » 0.008 / 0.005 / 0.004 / 0.0035 $ (FREE / BRONZE / SILVER / GOLD; PLATINUM and DIAMOND aligned on GOLD by Apify), `apify-actor-start` 0.00005 $, minimum budget 0.05 $ per run. Billing trial: 20 products, 20 `result` events charged (0.0003 $).
- Store metadata set through the API: category ECOMMERCE, SEO title and description. Daily control run: task `aliexpress-search-daily-check` (« wireless earbuds », US / USD / en, 20 products, 256 MB, 300 s), schedule every day at 06:30 Europe/Paris, e-mail alert through `apify/send-mail`.
- Published on the Apify Store (`https://apify.com/myth84/aliexpress-search-scraper`); indexed in the default search about 10 minutes later. Logo still to be set in the Console (`pictureUrl` empty).

#### 0.1.1 – 0.1.4 — 2026-10-19

- First deployment; validation run: 60 products in 1 request (0.0005 $). Open points measured with one input file per question: price sorting is exact within a page and approximate across pages (documented, not corrected), 486 unique products over 10 pages (19 % repeats), FR / EUR pages fine (no delivery badge).
- Card variant `itemcard_new` parsed; promo cards without a price (free gift for new shoppers) skipped and counted in `RUN_STATS.productsWithoutPrice` instead of being saved with a null price.
- « Welcome deal » prices (62 % of US rows: strike-through price minus saving = displayed price) kept as displayed and flagged in `attributes.newShopperDeal` / `newShopperSaving`.
- Scale control run: 2 searches × 300 products in 14 requests and 41 s, 0.0040 $ at 1,024 MB.

#### 0.1 — 2026-10-19

- First version, built offline on committed fixtures by cloud task n°3 (PR #2) on the model of `airbnb-listings`: embedded `_init_data_` JSON parsing (balanced-brace extraction), search URLs with sort and filters, country / currency / language through the `aep_usuc_f` preference cookie, normalisation to `ecommerce-product/1.0` (displayed currency wins over the requested one), one sticky session per search, `page=N` pagination, global deduplication, session rotation on an unusable page, pay-per-event `result` charging, `RUN_STATS`, explicit failure on empty output.
- Store readiness: README, input schema, dataset schema with Overview / Prices / Ranking views, output schema, consistency tests (README ⇔ schemas ⇔ code, Ajv replay of 351 fixture rows).
