# Changelog of AliExpress Search API & Scraper (`sourcing-data-studio/aliexpress-search-api`) Actor

- **URL**: https://apify.com/sourcing-data-studio/aliexpress-search-api/changelog.md
- **Full Actor documentation**: https://apify.com/sourcing-data-studio/aliexpress-search-api.md

## Changelog

### 0.1.13 (2026-10-09)

- README: the Python example now works with version 3 of Apify's Python client, the version `pip install apify-client` installs today (it needs Python 3.11 or later). The example read the dataset id as `run["defaultDatasetId"]`. Client 3 returns a typed object, so that line stopped with `TypeError: 'Run' object is not subscriptable`, after the run had finished and been charged. It now reads `run.default_dataset_id`, and the line above it says `pip install "apify-client>=3"`.
- The faulty example was added on 2026-10-09 and had not been run before it was published. The corrected one was run against this Actor the same day with client 3.3.0.
- README: the two paired runs for "usb c cable" started 14 minutes 13 seconds apart. The text said fifteen minutes and now says fourteen.
- README, "Is the price the final price I would pay?": no longer says that a new-user deal price "applies once and only to a first order". That was our reading of the name of the offer and was not checked against AliExpress's own terms. The answer now says that AliExpress marks the price as a deal for new users.
- No change to the code, the input or the output.

### 0.1.12 (2026-10-09)

- README, input form and two example tasks: a sort order holds inside one result page, not across pages. Seen in multi-page test runs on 2026-10-09: 300 rows for "yoga mat" with most orders first came from 7 pages, each beginning again from a high sold count, and only 36 of the 60 highest sold counts among them were on the first page; 150 rows with each of the two price orders behaved the same way. The texts of 0.1.9 to 0.1.11 said "most orders first" and gave the share of rows in order (117 of 119 neighbouring pairs), which hid that the two breaks were the two page boundaries. The tips now say: read several pages and sort the rows yourself.
- README: the price type `unknown` can also occur on a full result card.
- No source change.

### 0.1.11 (2026-10-09)

- README: says what paired runs of 2026-10-09 showed. Two runs of the same search four minutes apart, both in USD, returned the same 60 products and 48 of them at a different price. Two other runs of one search, fifteen minutes apart, returned English titles, USD and item ids starting with 3256 in the first and German titles, EUR and ids starting with 1005 in the second, with no item id in common; setting a proxy country did not change that (and a run with the country set to Germany failed to connect). The page now tells the reader to compare prices inside one run only and not to join runs on the item id, and the answer about scheduling no longer suggests a weekly price check of single items.
- No source change.

### 0.1.10 (2026-10-09)

- README: the opening now states the price: what one saved row costs on the Free tier and what a typical query costs, from the same figures as the cost section. The sentence above the sample rows no longer names a "quick-test example".
- README, limits: the paragraph on full and short result cards said that a second page brings short cards only. It now gives the earlier tests and the platform runs of 2026-10-09, where the first three pages were all full cards.
- No source change.

### 0.1.9 (2026-10-09)

- New optional input **Sort order** (`sort`): `relevance` (the default, the order AliExpress uses), `orders` (most orders first), `priceAsc` (lowest price first) and `priceDesc` (highest price first). AliExpress does the sorting: the Actor adds the order to the address of the search page it already reads (`SortType=total_tranpro_desc`, `SortType=price_asc` or `SortType=price_desc`). Checked on the platform on 2026-10-09 through the default Apify Proxy with the search "usb c cable" (prices in USD): with `orders`, 120 rows came in falling order of sold count in 117 of 119 neighbouring pairs; with `priceAsc` and a range from 1 to 10, 60 rows rose from 1.09 to 1.44, all of them new-user deal prices; with `priceDesc`, 40 rows fell from 564.86 to 75.11. In the `orders` run 43 of 180 result cards were free-gift offers without a price and were not saved, so part of the top sellers was missing.
- New optional inputs **Minimum price** and **Maximum price** (`minPrice`, `maxPrice`), added to the same address. AliExpress reads them in the currency it shows for the request, which the Actor cannot choose. In the platform check a range from 2 to 5 for "led strip lights" gave 60 rows, all priced from 2.03 to 4.88 USD, 22 of them new-user deal prices. In a check from Bahrain the range was read in BHD: a range from 1 to 2 gave 20 rows priced between 1.01 and 1.95, and the first three result pages held 4, 9 and 8 results. The Actor does not check the prices of the rows against the range.
- New optional inputs **Minimum rating** (`minRating`, 0 to 5) and **Minimum sold count** (`minSoldCount`). The Actor applies them to the rows it has read: a row whose rating (or sold count) is missing or below the minimum is not saved and not charged. Empty or 0 switches a minimum off. In the platform check ("yoga mat thick", `orders`, minimum rating 4.5, minimum sold count 100) the Actor read 658 rows on 11 pages, saved 47, all of which meet both minimums, and left out 611; the term ended with `no-saved-row-in-3-pages`.
- While a minimum rating or sold count is set, a search term stops after 3 result pages in a row that add no saved row, or after 20 pages, whichever comes first. Without them, paging is as before.
- The run summary: `input` echoes the five new inputs, and each entry of `listings` now carries `rowsRead` and `leftOutByFilters` and, where a minimum ended the term, `stoppedBy` (`no-saved-row-in-3-pages` or `page-limit-20`). The existing keys and their meanings are unchanged. A run in which a minimum leaves out every readable result ends as succeeded, with a message that says so.
- A value that cannot be used (a sort order that does not exist, a negative or non-numeric price, a rating above 5, a sold count with decimals, a Maximum price below the Minimum price) stops the run before any page is loaded, with an error that names the field.
- With none of the new inputs set, the addresses requested, the rows saved and the charges are the same as in 0.1.7. The dataset fields are unchanged.
- README: describes the new inputs, and the statement that sorting and a price range are not supported is gone (the shipping country, free shipping and the other filters of the site still are not). The examples 02 and 03 show the new inputs. The statements about the short result card now say what the platform runs of 2026-10-09 showed: the first three pages of a search were all full cards, where earlier tests saw them on the first page only.

### 0.1.8 (2026-10-09, test build, never live)

- The code of 0.1.9 with the texts as they stood before the platform checks. Built under the tag `candidate` for those checks; `latest` stayed on 0.1.7.

### 0.1.7 (2026-10-09)

- The title is now "AliExpress Search API & Scraper". The Store address and the technical name are unchanged. The Store description leads with what you get; the sentence that the Actor is unofficial and not affiliated with AliExpress or Alibaba Group stays.
- The note under the output example said the sample came from a connection in Bahrain, in BHD and Arabic. The sample has been a platform run in USD and English since 0.1.6; the note now says so.
- README: the output example now comes right after the feature list; the API section shows a curl, a JavaScript and a Python example; the cost section adds the price of a typical query. No change to the code, the input or the output.

### 0.1.6 (2026-10-07)

- The output example in the README comes from a platform run through the default Apify Proxy (USD prices, English titles) instead of a local run from Bahrain (BHD prices, Arabic titles). No code change.

### 0.1.5 (2026-10-06)

- A page that AliExpress answers with a block or a verification page (status 401, 403, 407, 429 or 503, or a verification or sign-in page) is no longer asked for again in the same run, and no other address is tried for it. It is listed as `blocked` in the run summary and nothing is charged. Before, it was loaded again, up to the number of retries, with a new session. **Retries per page** now applies to the other failures only.
- One browser session, and so one address, for the whole run. The library moved the browser to a new session after three failed pages (a blocked page counts as one, and so does a first result page without result cards that is loaded again), after 50 pages and after 50 minutes, and with the proxy a new address came with it. The crawler now keeps a single session for the run, so a block, a failed page or the passing of time no longer changes the address. A new session starts only when the connection through the proxy fails.
- The Store description and the first paragraph of the README no longer say that the new-user deal price is "kept apart" from the price. They now say what the rows carry (see the `price` entry below): `price` is the price as shown, and one-time new-user deals are flagged in `priceType`.
- `price` is now the price the result shows, as shown. On a result with a one-time new-user deal it is the deal price; before, it was the crossed-out list price next to the deal. The crossed-out price is in `originalPrice` on every row that shows one (before, it was empty next to a deal) and `discountPercent` stays with it. `newUserPrice` repeats the price and `isNewUserPrice` is true on every row with a deal (before, `isNewUserPrice` was true only when the deal was the only price shown). If you compare prices, compare rows with the same `priceType`.
- New field `priceType`: `new_user_deal` (the result is marked as a new-user deal and `price` is the deal price), `regular` (the result carries its campaign information and none of it is a new-user deal) or `unknown` (short result cards, which carry no such information).
- The output field `scrapedAt` is now called `retrievedAt`. The value is the same: the ISO 8601 UTC time when the search page was read. If you read `scrapedAt` in a script, a spreadsheet or an integration, change it to `retrievedAt`. No other field was renamed.
- A first result page without any result card is no longer recorded as `no-match`. AliExpress always returns cards, so the page is loaded again (up to **Retries per page**) and the term is listed as `failed`, with a message, if it stays empty; the run then ends as failed when nothing else was saved. A term is listed as `no-match` only when the page itself reports 0 results.
- Every entry of `listings` in the run summary now carries `retries` (how often a page of the term was loaded again) and, for a failed or blocked term, a `message`.

### 0.1 (2026-10-05)

- First version: keyword search on AliExpress, one row per search result with price, original price, discount, currency, rating, sold text, sold count, image and item link. No seller data. One charged event per saved result.
- Prices are the amount the result shows, no longer rounded from the number the page carries next to it.
- New fields newUserPrice and isNewUserPrice: one-time new-user deals are kept apart from the regular price where the result shows both, and flagged where it shows only the deal.
- discountPercent is filled whenever the result shows a discount figure, with or without a crossed-out price.
- Free-gift offers without a price are rejected under their own reason in the run summary.
- Page loads are about 10 seconds apart. Every search term appears in the run summary, also when the run stopped before reading it. A run in which every result was rejected ends as failed.
- The run summary records under `browser` what the browser reported about itself: its user agent, and that software controls it. The Actor reads AliExpress with a plain, unmodified, headless browser and does not start one that hides this.
