# AliExpress Products Scraper (`nice_dev/aliexpress-products-scraper`) Actor

Scrape AliExpress products by keyword, category or URL, in 51 countries: price, discount, units sold, rating, delivery, seller and reviews. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/aliexpress-products-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** E-commerce, Marketing, MCP servers
- **Stats:** 3 total users, 1 monthly users, 90.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.19 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### 🔎 What is AliExpress Products Scraper?

**AliExpress Products Scraper** extracts **products from [AliExpress](https://www.aliexpress.com)**: **price, discount, rating, units sold, delivery dates, category, store and reviews**, for any keyword, category or product link, in 51 delivery countries. It works as an **AliExpress API alternative**: run it on a schedule, call it from your code, or plug it into Make, Zapier or n8n.

Type a **keyword** (`wireless earbuds`) or paste an AliExpress link, click **Start**, and download the products in JSON, CSV or Excel. No login, nothing to set up. Optional extras: the full product page, buyer reviews, and price monitoring that tells you which products are new, cheaper or dearer since your last run.

### 📋 What data can you extract from AliExpress?

One item per product and delivery country, 94 fields:

| Category | What you get |
| --- | --- |
| 🏷️ **Product** | title, link, AliExpress product number, photos, video — `2026 New Air 3 Pro Bluetooth Earbuds` |
| 💰 **Price and promotions** | current price, crossed-out price, discount, the seller's own price, currency, import tax rate, new-shopper offers, promotion type and end date, coupons — `15.31 USD` |
| 📈 **Sales and ratings** | exact units sold, rating, number of reviews, wishlist count — `4,031 sold` |
| 🚚 **Delivery** | estimated delivery dates and days, ships-from country, cheapest shipping, every shipping option |
| 🏪 **Seller and store** | store name, number, link, country, opening date, positive feedback, followers, regular buyers, top-rated seller |
| 🗂️ **Category** | category number, path and readable name |
| 📄 **Product page** | variants with price and stock, total stock, specifications, brand, description, return policy, buyer protection, size feedback, compliance notes |
| ⭐ **Reviews** | rating breakdown, review topics, each review with its text, translation, date, buyer country and photos |
| 🔔 **Price monitoring** | new products, price drops and rises, previous price, change in %, price history |

Every field, with an example, is listed in the **Output** section below.

Fields of the **Product page** and **Reviews** rows are filled only when you tick **Full product details** or **Include buyer reviews** (both are options, off by default, see the price section). The product page also fills, in the other rows: the store details (all but the store name), the ships-from country, the shipping cost and options, the number of reviews, the wishlist count, the coupons, the brand and the video. Everything else comes with the search itself.

### ✅ Why use AliExpress Products Scraper?

- 🌍 **Any country, any currency**: the same search in several countries in one run, to compare prices. `globalProductId` is the same product number in every country.
- 🔢 **Exact units sold**: the real count, not the rounded « 1,000+ sold » of the page.
- 📦 **More than 3,600 products per search**: AliExpress stops at 60 pages; large searches are read again by price range.
- 🧩 **Nothing missed, nothing twice**: each product is returned once per country.
- 🔔 **Monitoring built in**: tick **Only new products**, schedule the Actor, and each run returns only what it has never delivered; add **price changes** to hear when a seller moves a price.
- ⭐ **Reviews with filters**: by stars, by word, by date, with photos, from buyers in your country.

### 🚀 How to scrape AliExpress

1. Create a free Apify account.
2. Open **AliExpress Products Scraper** and type a **Search keyword** (e.g. `wireless earbuds`).
3. Or paste your own AliExpress links into **Start URLs**: search, category or product pages.
4. Set **Max products** (100 by default, 0 = no limit) — and **Max products per search** when you run several searches — then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape AliExpress?

This Actor uses **pay per event** pricing: you pay for each product returned, plus a small fee per run start. Everything else is an option, off by default, charged apart and only for what it delivers. See the **Pricing** tab for every amount.

- **Product page** (**Full product details**, and every product you give by link or number in **Product URLs or IDs**): one charge for each product page read. A product AliExpress does not sell in the delivery country still costs it (its row says `available` is false), because the page was asked for.
- **Residential fallback**: product pages are protected harder than search pages. When AliExpress turns away every normal connection for a page, the Actor reads it through a premium connection and charges this event for that one page. A page read normally, a page nobody could read, and any run with your own proxies never pay it.
- **Buyer reviews** (**Include buyer reviews**): one charge for each review delivered. Reviews read and then dropped by one of your review filters, or cut by **Max reviews per product**, cost nothing.
- **Price changes** (**Also products whose price changed**): one charge for each product returned because the seller's price moved by at least **Minimum price change (%)**. A new product costs its normal product price only; so does a removed product.
- **Filter check** and **Detail filter check**: the filters the site does not offer are applied by the Actor on every product it reads. A product that passes them costs its normal price; a product a filter drops costs a small filter fee instead, so filtering never costs more than taking everything. With `onlyNew`, a product your filters drop is checked (and the fee charged) once; the next runs with the same filters skip it for free, except a product that may pass later (see below). Change a filter, or tick `resetState`, to check them again. Filter check covers launch date, minimum rating, minimum units sold, minimum discount, sponsored products and excluded words. Detail filter check covers the two filters that need the product page (**Promotion ends within** and **Only products with a promotion end date**): each product dropped after its page was read pays it. The filters AliExpress itself offers (price range, free shipping, Choice, on sale, local warehouse, ships from, and 4 stars and up) cost nothing extra.

Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

```json
{
    "query": "wireless earbuds",
    "maxItems": 200,
    "country": "GB",
    "currency": "GBP"
}
```

Several keywords in several countries, a cap per search, only the products not delivered before:

```json
{
    "searchQueries": ["wireless earbuds", "usb cable"],
    "country": "DE",
    "countries": ["FR"],
    "maxItemsPerQuery": 50,
    "onlyNew": true,
    "stateKey": "earbuds-eu"
}
```

Or with your own links, and a filter:

```json
{
    "startUrls": [
        { "url": "https://www.aliexpress.com/w/wholesale-phone-case.html" },
        { "url": "https://www.aliexpress.com/category/100003109/women-clothing.html" }
    ],
    "freeShippingOnly": true,
    "maxItems": 500
}
```

Two products by link, with their reviews:

```json
{
    "productUrls": ["https://www.aliexpress.com/item/1005006123456789.html", "3256806491667018"],
    "includeReviews": true,
    "maxReviewsPerProduct": 10
}
```

Monitoring a search: new products, and the ones whose price moved:

```json
{
    "query": "wireless earbuds",
    "onlyNew": true,
    "includePriceChanges": true,
    "stateKey": "earbuds-us"
}
```

| Field | Notes |
| --- | --- |
| `startUrls` | Search pages (`https://www.aliexpress.com/w/wholesale-phone-case.html`), category pages (`https://www.aliexpress.com/category/100003109/women-clothing.html`) or product pages (`https://www.aliexpress.com/item/1005006123456789.html`). Filters set on the site are kept. Max 1 000 URLs × delivery countries (500 URLs in 2 countries). |
| `query`, `searchQueries` | Free-text search, exactly as typed on AliExpress (`wireless earbuds`); `searchQueries` adds more keywords, one search each. |
| `categoryId` | A category number (`100003109`): alone it reads the whole category, with a keyword the keyword inside it. |
| `productUrls` | Single products, as links or numbers (`1005006123456789`). Each one is read from its product page and always delivered, even with `onlyNew`. Max 1 000. |
| `maxItems` | Stop after this many products for the whole run (`0` = unlimited). |
| `maxItemsPerQuery` | Cap for EACH keyword, category or search URL (keyword × country). `0` = no per-search cap. |
| `splitByPrice` | Read a search that fills AliExpress's 60 pages again by price range, to go past 3,600 products. Not used when the search is sorted by price: it then stops at 3,600 products, in the order of the sort. |
| `sortBy` | Order of the results: `orders` for the best-selling first, or by price. |
| `minPrice`, `maxPrice` | Price range in the chosen currency (`5`, `20`). AliExpress applies it. |
| `freeShippingOnly` | Only products with free shipping. AliExpress applies it. |
| `choiceOnly` | Only Choice products. AliExpress applies it. |
| `saleOnly` | Only products on sale. AliExpress applies it. |
| `localWarehouseOnly` | Only products shipped from a warehouse in the delivery country. AliExpress applies it to searches. |
| `shipFromCountry` | Only products shipped from this country (`CN`). AliExpress applies it to searches. |
| `country`, `countries` | Delivery country (default `US`), and more of them, added to it: every keyword is searched in every country (max 500 searches per run). Example: `GB`. |
| `currency` | Currency of the prices, for example `EUR`. |
| `language` | Language of the titles, for example `fr_FR`. |
| `extractDetails` | Product page for each product: variants, stock, shipping, specifications, seller. A paid option, off by default. |
| `includeDescription` | With the product page: the seller's description, at no extra charge. |
| `includeReviews` | Buyer reviews of each product, with or without the product page. A paid option, off by default. |
| `maxReviewsPerProduct` | Reviews kept for each product, `20` by default. |
| `reviewFilter` | Which reviews to keep, for example `image` for those with photos, or `local` for buyers in the delivery country. |
| `reviewMinRating`, `reviewMaxRating` | Keep reviews between these stars (`1`, `5`). The Actor reads more pages to find enough of them. |
| `reviewKeyword` | Keep the reviews that say this word or phrase, in their text or its English translation (`battery`). |
| `reviewPostedAfter` | Keep the reviews written on or after this date (`2026-03-01`) or within a period (`30 days`). |
| `postedAfter`, `postedBefore` | Launch date range: `2026-09-01`, or a period before now (`7 days`, `2 weeks`, `1 month`). |
| `minRating` | Only products rated at least this (`4`). From 4, AliExpress's own "4 stars and up" filter is used too. |
| `minOrders` | Only products with at least this many units sold (`1000`). |
| `minDiscountPercent` | Only products whose card shows at least this discount (`50`). |
| `expiringWithinHours` | Only products whose promotion ends within this many hours (`48`). Needs the product page. |
| `onlyWithExpiry` | Only products whose page gives the end date of their promotion. Needs the product page. |
| `excludeSponsored` | Drop the products marked as ads. |
| `excludeKeywords` | Drop the products whose title contains one of these words (`broken`). Case and accents ignored. |
| `onlyNew` | Monitoring: only the products never delivered under this memory key. |
| `includePriceChanges` | With monitoring: also the products whose seller's price changed. A paid option, off by default. |
| `minPriceChangePercent` | Smallest change of the seller's price that counts, in % of the price on the last row delivered to you (`3` by default, `0` = any change, to the cent). |
| `includeRemoved` | With price changes: also the products that left the search results. |
| `stateKey` | Name of the memory (`earbuds-us`): one per schedule. |
| `resetState` | Forget the memory before the run. |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

One real item, shortened:

```json
{
    "id": "3256811621288203",
    "url": "https://www.aliexpress.com/item/3256811621288203.html",
    "title": "2026 New Air 3 Pro 3 Bluetooth Wireless Earbuds with Heart Rate Monitoring",
    "price": 15.31,
    "originalPrice": 64.01,
    "discountPercent": 76,
    "currency": "USD",
    "images": ["https://ae-pic-a1.aliexpress-media.com/kf/Sa57a4580c1274b01988d5df3fa8e76cbx.jpg"],
    "rating": 4.9,
    "soldCount": 4031,
    "publishedAt": "2026-03-09",
    "tags": ["Sale", "Lowest price in 90 days"],
    "isSponsored": false,
    "estimatedDeliveryFrom": "2026-09-22",
    "estimatedDeliveryTo": "2026-09-29",
    "coupons": [],
    "categoryId": "63705",
    "categoryPath": ["44", "100000306", "63705"],
    "categoryName": "Headphones & Earphones",
    "variants": [],
    "deliveryDaysMin": 4,
    "deliveryDaysMax": 11,
    "shippingOptions": [],
    "specifications": [],
    "descriptionImages": [],
    "reviewTopics": [],
    "reviews": [],
    "priceHistory": [],
    "country": "US",
    "scrapedAt": "2026-10-01T12:00:00.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 94 fields

| Fields | What you get |
| --- | --- |
| `id`, `url`, `title`, `images`, `imageUrl`, `videoUrl` | product number, link, title, photos |
| `price`, `originalPrice`, `sellerOfferPrice`, `discountPercent`, `currency`, `priceText`, `taxRate` | prices and discount |
| `isNewShopperPrice`, `promotionType`, `promotionEndsAt` | the promotion behind the price |
| `rating`, `soldCount`, `soldCountIsLowerBound`, `soldText`, `reviewCount`, `wishlistCount` | rating and sales |
| `globalProductId`, `spuId`, `skuId` | AliExpress's other numbers for the product |
| `publishedAt`, `tags`, `isChoice`, `isTopBrand`, `freeShipping`, `isSponsored`, `coupons` | launch day, labels and offers |
| `estimatedDeliveryFrom`, `estimatedDeliveryTo`, `deliveryDaysMin`, `deliveryDaysMax`, `shipFrom`, `shippingCost`, `shippingOptions` | delivery |
| `storeName`, `storeId`, `storeUrl`, `sellerId`, `storeCountry`, `storeOpenedAt`, `storePositiveRate`, `storeRating`, `isTopRatedSeller`, `sellerLevel`, `storeFeedbackCount`, `storeFollowers`, `storeServiceRating`, `storeCommunicationScore`, `storeSoldLast180Days`, `storeRegularBuyers`, `storeOnVacation`, `storeLogo`, `isLocalSeller`, `storeSalesText` | the store |
| `categoryId`, `categoryPath`, `categoryName`, `topCategoryName` | category |
| `brand`, `available`, `totalStock`, `variantCount`, `isLot`, `variants`, `specifications`, `description`, `descriptionImages`, `manualUrl`, `returnPolicy`, `returnDays`, `buyerProtection`, `sizeFeedback`, `sizeChartUrl`, `complianceNotes` | product page |
| `reviewStats`, `reviewTopics`, `reviews` | buyer reviews |
| `changeType`, `previousPrice`, `priceChangePercent`, `priceHistory` | monitoring |
| `searchQuery`, `position`, `page`, `sortBy`, `country`, `language`, `searchUrl`, `scrapedAt` | the search that found the product |

### 💡 Tips

#### How to get more results

Set `maxItems` to `0` and use a broad keyword: with `splitByPrice` on, a search that fills AliExpress's 60 pages is read again by price range, so a large search returns far more than 3,600 products. Sorted by price, a search is not split (the price ranges would not come in the order of the sort): it stops at its 60 pages, about 3,600 products, in that order.

#### How to reduce costs

The price is per product, so the levers are `maxItems`, `maxItemsPerQuery`, the filters AliExpress offers (a product it does not list costs nothing) and `onlyNew` for recurring runs (a product delivered once is never charged again; one your filters drop is checked, and charged the filter fee, once, then skipped for free while your filters stay the same, except a product that may pass a time-dependent filter later, such as the minimum number of orders). The product page, the reviews and the price changes are options: leave them off when you do not need them. Reviews are charged per review delivered, so `maxReviewsPerProduct` and the review filters cap that cost too.

#### Several searches in one run

Fill `searchQueries` and / or `countries`: the Actor runs one search per keyword × country (up to 500 per run). The single `query` and `country` fields still work and are added to the lists: `country` is `US` unless you change it, so set it to one of your countries rather than leave the US in. A product found in two countries is saved once per country. To compare countries, match the rows on `globalProductId`: the `id` AliExpress serves can differ from one country to another. If your filters match almost nothing in one search (300 products in a row dropped after their product page was read), that search stops and the run goes on with the next ones; the run stops only when every search is in that case.

#### Product page and delivery country

With `extractDetails`, each product page is read: every variant with its price and stock, shipping options, specifications, seller details and, with `includeDescription`, the seller's description. A product AliExpress does not sell in the delivery country is still returned, with `available` set to `false` and the page fields empty; it is charged like any product page read. `productUrls` always read the product page.

#### Buyer reviews

Tick `includeReviews` to add each product's reviews, with or without the product page. Reviews are always asked in English: `translatedText` is the English translation, `text` stays the buyer's original words. The delivery country of the row orders the reviews (its buyers first) and sets the `local` choice of `reviewFilter`. AliExpress filters reviews by photos and follow-up only: the stars, word and date filters are applied by the Actor, which then reads more review pages to find enough of them. AliExpress gives no choice of order for reviews, so they come in its default order. Reviews are charged per review delivered, up to `maxReviewsPerProduct`.

#### Filters

AliExpress applies these itself: `minPrice`, `maxPrice`, `freeShippingOnly`, `choiceOnly`, `saleOnly`, `localWarehouseOnly`, `shipFromCountry`, and `minRating` from 4. `localWarehouseOnly` and `shipFromCountry` work on searches only. The Actor applies the others on every product it reads: `minOrders`, `minRating`, `minDiscountPercent`, `postedAfter`, `postedBefore`, `excludeSponsored`, `excludeKeywords`, and, with `extractDetails`, `expiringWithinHours` and `onlyWithExpiry`. A product dropped by the Actor is not saved and does not count in `maxItems`, and costs the filter fee shown in the Pricing tab (see the price section for which filter pays which fee). A product without the value a filter reads (no rating, no launch date, no discount) is dropped.

#### Monitoring: only the new products

Tick **Only new products** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the products already delivered: they are not saved and not charged. The memory lives in a named key-value store of your account (`aliexpress-products-scraper-seen`, up to 150,000 products per key) and is only updated with products that really reached the dataset. Give each schedule its own `stateKey`, and tick `resetState` once to start over. AliExpress cannot sort by newest, so a run reads the search page by page and stops after 20 results pages in a row where every product was already delivered (one new product on a page starts the count again). A new product listed further down is then missed, and a search stopped this way gives no removed rows. With price changes, a product whose price moved does not count as already delivered. A product your filters dropped is remembered too (under the same `stateKey`): the next runs with the same filters skip it without reading it or charging the filter fee; change a filter, or tick `resetState`, to check it again. A product that does not pass a time-dependent filter yet (a relative "posted before" date, the minimum number of orders) is checked again on the next run, and charged the filter fee again while it is still refused, so it is delivered as soon as it passes.

#### Monitoring: price changes

Tick **Also products whose price changed** (`includePriceChanges`, with `onlyNew`) and each run also returns the products it delivered before whose price moved: `changeType` is `price_drop` or `price_rise`, with `previousPrice` (the seller's price on the last row we delivered), `priceChangePercent` and `priceHistory` (the seller's price, units sold and rating at each of your last 30 runs that saw the product). A new product has `changeType` `new`.

- **Which price is followed.** The price AliExpress shows on a result (`price`) is different for each visitor and each visit: new-shopper offers, coupons and promotions change it between two reads a few minutes apart (measured on real runs: 49 prices of 124 differed between two connections, some by several times the price). Following it would send you false changes all day. The Actor follows the **seller's own price** (`sellerOfferPrice`), which stays put until the seller changes it. The row still shows the current `price` too. A product whose result shows no seller's price is never reported as changed.
- **How big a change must be.** A product is returned, and charged, only when the seller's price moved by at least `minPriceChangePercent` (3 % by default, `0` = any change, to the cent) compared with the price on the last row we delivered to you for that product. The Actor compares with that row, not with the previous run, so small drifts add up: a currency moving by less than 2 % a day returns nothing at first, then the product comes back once the total passes your threshold.
- **A product that did not move enough** stays out of the dataset and costs nothing, but your run is still added to its history.
- **Removed products.** Tick `includeRemoved` (off by default) to get a row with `changeType` `removed` for each product an earlier run delivered that its search no longer lists: product number, country, link, `previousPrice` (the last price seen) and history, no current `price`. It is said only for a keyword or category search read to its very end in that run (fewer than 3,600 products), with no failed request, and in a run that was neither resumed nor stopped early. `productUrls` never give removed rows. A removed row counts in `maxItems` and in its search's `maxItemsPerQuery`, and is charged like a product; it is reported once, then forgotten. A removed row cut by a cap or by the run's maximum cost is reported by the next run.
- **Memory.** Prices live under the same `stateKey` as `onlyNew`, up to 50,000 products per key (the ones not seen for the longest time are forgotten and learned again silently, never reported as changes). Under 512 MB of run memory, the prices of the 25,000 products seen last are kept (50,000 from 512 MB). `resetState` forgets prices and delivered products together, so the next run returns and charges everything again.

#### Filter by launch date

`postedAfter` and `postedBefore` take a date (`2026-09-01`, the whole day is included) or a period before now (`7 days`, `2 weeks`, `1 month`). The filter reads `publishedAt`, the day AliExpress first listed the product; a product without one is dropped as soon as a date bound is set.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per AliExpress product and delivery country.

Smallest input, for a cheap first call:

```json
{
    "query": "wireless earbuds",
    "maxItems": 10
}
```

Key output fields: `url`, `title`, `price`, `currency`, `soldCount`, `rating`, `estimatedDeliveryTo`. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`. The prices are in the Pricing tab.

### ❓ FAQ

#### Is it legal to scrape AliExpress?

The Actor only reads what AliExpress shows publicly to any anonymous visitor. It logs in to nothing. Results contain no personal data of buyers beyond the masked name AliExpress shows with a review; do not store it without a legitimate reason (GDPR). You are responsible for using the data in compliance with AliExpress's Terms of Use and applicable law. This Actor is not affiliated with AliExpress.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request the site turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 80 seconds).

#### Why is the price not the one I see on AliExpress?

AliExpress shows each visitor their own price: new-shopper offers, coupons and promotions can change it from one visit to the next. The Actor gives the price an anonymous visitor sees in the delivery country and currency you chose; `isNewShopperPrice` tells you when it is a new-shopper offer, and `sellerOfferPrice` is the seller's own price, before those offers.

#### Is the data safe to open in Excel or to show on a web page?

Titles and review texts are the sellers' and buyers' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@`: Excel and Google Sheets may read such a cell of a CSV file as a formula. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. Every URL field holds an http(s) URL or null. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- AliExpress serves 60 pages per search; `splitByPrice` goes past it by price range, which can return a product twice (it is saved once). Not with a sort by price: that search stops at about 3,600 products, in the order of the sort.
- The number of units sold is exact when AliExpress sends it with the search result, else the rounded figure of the page (`soldCountIsLowerBound` is then true).
- Questions and answers, package dimensions and the regular price outside new-shopper offers are not available: AliExpress does not give them.
- AliExpress cannot sort by newest, nor reviews in any other order than its own: those choices are not offered.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

#### Something doesn't work?

The last line of the log counts the products saved, filtered out and the requests that failed after every retry. Those requests are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If AliExpress changes its pages, you are told instead of paying for blank rows. If the first 20 products read all lack their title, price, photo, launch date or category, the run saves nothing more, stops and fails, and its last message names the missing field: at most those first products are charged. A search of fewer products (a scheduled `onlyNew` run, a small **Max products**) is checked at its end, from 5 products.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

## `startUrls` (type: `array`):

AliExpress search URLs (`https://www.aliexpress.com/w/wholesale-phone-case.html`, every filter set on the site is kept), category URLs (`https://www.aliexpress.com/category/100003109/women-clothing.html`) or product URLs (`https://www.aliexpress.com/item/1005006123456789.html`). Pagination is automatic. When this list is not empty, the keyword and category fields below are ignored; filters, caps and monitoring still apply. Max 1 000 URLs × delivery countries (500 URLs in 2 countries).

## `query` (type: `string`):

Free-text search, exactly as typed on AliExpress (e.g. `wireless earbuds`).

## `searchQueries` (type: `array`):

Several keywords in one run: one search per keyword (times each country below). Added to **Search keyword**; products found by several searches are saved once.

## `categoryId` (type: `string`):

AliExpress category number, read in a category URL (`/category/100003109/...` → `100003109`) or in the `categoryId` field of a previous run. With a keyword: the keyword searched inside this category. Alone: the whole category.

## `productUrls` (type: `array`):

Single products to read, as URLs (`https://www.aliexpress.com/item/1005006123456789.html`, any AliExpress country domain or app share link) or bare product IDs (`1005006123456789`). Each one is read from its product page, so it is charged as a product with full details. Max 1 000.

## `maxItems` (type: `integer`):

Maximum number of products to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (keyword × country, category, or search URL), so that the first search cannot use up the whole **Max products** budget. 0 = no per-search cap.

## `splitByPrice` (type: `boolean`):

AliExpress shows at most 60 pages (≈ 3,600 products) per search. When a search fills them, the Actor splits it into price ranges and reads each range, so a large search returns far more products. Off = stop at 3,600 per search. Not used when sorted by price: that search stops at about 3,600 products, in the order of the sort.

## `sortBy` (type: `string`):

Order of the search results, as on AliExpress. AliExpress has no 'newest' order: use **Only new products** below to get the new ones at each run.

## `minPrice` (type: `number`):

Only products from this price, in the currency below. Sent to AliExpress.

## `maxPrice` (type: `number`):

Only products up to this price, in the currency below. Sent to AliExpress. 0 = no maximum.

## `freeShippingOnly` (type: `boolean`):

AliExpress filter 'Free shipping'.

## `choiceOnly` (type: `boolean`):

AliExpress filter 'Choice' (AliExpress's own selection: free shipping, faster delivery, free returns).

## `saleOnly` (type: `boolean`):

AliExpress filter 'Sale' (products in the current promotion).

## `localWarehouseOnly` (type: `boolean`):

AliExpress filter 'Local+': products shipped from a warehouse in the delivery country (faster delivery). Applied to searches.

## `shipFromCountry` (type: `string`):

Only products shipped from this country, as a 2-letter code: `CN`, `US`, `ES`, `FR`, `DE`, `PL`… Empty = any origin. Sent to AliExpress for searches.

## `country` (type: `string`):

Country the products are delivered to: AliExpress shows the prices, shipping options and products of this country.

## `countries` (type: `array`):

Several delivery countries in one run, as 2-letter codes (`GB`, `DE`, `FR`…): every keyword is searched in every country (3 keywords × 4 countries = 12 searches, max 500), to compare prices between countries. Added to **Ship to**. A product found in two countries is saved once per country.

## `currency` (type: `string`):

Currency of the prices.

## `language` (type: `string`):

Language of the titles and specifications (AliExpress's own translation). Reviews always come with an English translation, whatever this field.

## `extractDetails` (type: `boolean`):

Read each product's page: every variant (color, size…) with its price and stock, total stock, shipping options to the delivery country (cost, carrier, delivery days), specifications, category path, number of reviews, seller (store name, number, country, opening date, positive feedback, top-rated) and all photos. Charged per product read (event `product-details`).

## `includeDescription` (type: `boolean`):

With **Full product details**: also the seller's description (text and image URLs). No extra charge.

## `includeReviews` (type: `boolean`):

Add each product's buyer reviews (rating, text and its translation, date, buyer country, variant bought, photos, helpful votes, follow-up review) and its rating breakdown (count per star). Charged per review (event `review`).

## `maxReviewsPerProduct` (type: `integer`):

Reviews kept per product, in AliExpress's default order. Read 50 at a time.

## `reviewFilter` (type: `string`):

AliExpress's review filter: every review, only those with photos, only those with a follow-up review, or only those from buyers in the delivery country.

## `reviewMinRating` (type: `integer`):

Keep only the reviews with at least this many stars (1-5). Empty = all.

## `reviewMaxRating` (type: `integer`):

Keep only the reviews with at most this many stars, e.g. 2 for the complaints. Empty = all.

## `reviewKeyword` (type: `string`):

Keep only the reviews whose text, or its English translation, contains this word or phrase (case and accents ignored), e.g. `battery` or `broke`. AliExpress cannot filter reviews by text, so the Actor reads more pages to find enough of them. Empty = all.

## `reviewPostedAfter` (type: `string`):

Keep only the reviews written on or after this date: `2026-03-01`, or a period before now such as `30 days`, `3 months`. Reviews without a readable date are dropped. Empty = all.

## `postedAfter` (type: `string`):

Only products first listed on AliExpress on or after this date (the launch date AliExpress shows in the search results): `2026-09-01`, or a period before now such as `7 days`, `2 weeks`, `1 month`. Products without a launch date are dropped.

## `postedBefore` (type: `string`):

Only products first listed on or before this date (the whole day is included), or older than a period such as `30 days`.

## `minRating` (type: `number`):

Only products rated at least this (out of 5). 4 or more also uses AliExpress's own '4 stars & up' filter. Products without a rating are dropped.

## `minOrders` (type: `integer`):

Only products with at least this many units sold, as AliExpress shows it ('1,000+ sold' counts as 1000).

## `minDiscountPercent` (type: `integer`):

Only products whose search card shows at least this discount (the crossed-out price against the sale price), e.g. `50`. Products that show no discount are dropped. Empty or 0 = no bound.

## `expiringWithinHours` (type: `integer`):

Only products whose promotion ends from now on and within this many hours. The end date is on the product page: needs **Full product details** (without it this filter is ignored). A dropped product is charged a detail filter fee (see Pricing).

## `onlyWithExpiry` (type: `boolean`):

Only products whose product page gives the end date of their promotion. Needs **Full product details** (without it this filter is ignored). A dropped product is charged a detail filter fee (see Pricing).

## `excludeSponsored` (type: `boolean`):

Drop the products AliExpress marks as ads in the search results.

## `excludeKeywords` (type: `array`):

Drop the products whose title contains one of these words (case and accents ignored).

## `onlyNew` (type: `boolean`):

Skip the products that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and their page is not even opened. First run = everything is new. A search stops after 20 results pages in a row with nothing new.

## `includePriceChanges` (type: `boolean`):

With **Only new products**: also return the products already delivered whose price went up or down since the last run, with the previous price, the change in % and the price history of your runs. Charged per changed product (event `price-change`).

## `minPriceChangePercent` (type: `number`):

With **Also products whose price changed**: a product comes back only if its price moved by at least this many % since the last time it was delivered. The price followed is the seller's price (`sellerOfferPrice`): the price AliExpress displays varies with every visit, so it is never compared. Under this %, the move (a currency conversion, a rounding) is neither returned nor charged. 0 = every change down to the cent.

## `includeRemoved` (type: `boolean`):

With **Also products whose price changed**: also return a row (`changeType` = `removed`) for each product an earlier run delivered that its search no longer lists. Only for a keyword or category search that the run read to its very end (fewer than 3,600 products, no failed request, run not resumed). Each row is charged as a product.

## `stateKey` (type: `string`):

Name of the memory used by **Only new products**. Give each schedule / task its own key (e.g. `earbuds-us`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every product again. Untick it afterwards.

## `proxyConfiguration` (type: `object`):

Apify Proxy or your own proxies. Keep the default: it is included in the price, and the Actor switches IP by itself when AliExpress slows one down. The residential Apify proxy is not available in this Actor.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Most requests to AliExpress in any 60 seconds, all of them counted: search pages, product pages, review pages. A budget, not an even pace: up to this many can leave at once when the minute starts (spread them with the minimum delay below). Lower it if the log shows many 'Blocked' or 'Rate-limited' lines.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. Unlike the per-minute rate, this spreads the requests evenly instead of letting them go out in a burst. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. Behind a proxy, a request AliExpress turns away is also retried on a new proxy session up to 10 times without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "query": "wireless earbuds",
  "searchQueries": [],
  "productUrls": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "splitByPrice": true,
  "sortBy": "relevance",
  "freeShippingOnly": false,
  "choiceOnly": false,
  "saleOnly": false,
  "localWarehouseOnly": false,
  "country": "US",
  "countries": [],
  "currency": "USD",
  "language": "en_US",
  "extractDetails": false,
  "includeDescription": true,
  "includeReviews": false,
  "maxReviewsPerProduct": 20,
  "reviewFilter": "all",
  "onlyWithExpiry": false,
  "excludeSponsored": false,
  "excludeKeywords": [],
  "onlyNew": false,
  "includePriceChanges": false,
  "minPriceChangePercent": 3,
  "includeRemoved": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 5,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "wireless earbuds",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/aliexpress-products-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "query": "wireless earbuds",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/aliexpress-products-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "wireless earbuds",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/aliexpress-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/aliexpress-products-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XZbhwnjuzQQ6RS5Y2/builds/7It3r7pzvmoCirqo1/openapi.json
