AliExpress Scraper - products, variants, prices & reviews
Pricing
from $1.80 / 1,000 product results
AliExpress Scraper - products, variants, prices & reviews
Scrape AliExpress products: search by keyword or category, or process pasted search, category and product URLs page by page. Identity, price, availability, media and SKU fields come from the results page; optional detail and reviews steps add variant tables, specifications, seller data and reviews.
Pricing
from $1.80 / 1,000 product results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
4
Total users
0
Monthly active users
14 days ago
Last modified
Categories
Features
Share
AliExpress Scraper: Products, Prices, Variants & Reviews
AliExpress Scraper turns AliExpress into a clean product data API. Search by keyword or category, or walk pasted search, category and product URLs page by page, across 11 storefront markets (US, UK, DE, FR, ES, IT, BR, CA, AU, NL, PL) with prices in that market's currency. Every result carries identity, price, discount, rating, units sold, media and seller fields; switch on an optional detail step for SKU variant tables, specifications and full seller data, or an optional reviews step for per-product review records. Export to JSON, CSV or Excel, or pull results straight into your app through the API.
Why This Scraper?
- Two ways to find products. Search by keyword (with category, price, rating and free-shipping filters) or paste search, category and product URLs and let the actor walk them forward page by page.
- Results-page fields by default, deep detail on demand. The default (fast, low cost) pass already covers id, title, price, discount, rating, sold count, images and seller name; turn on detail fetching only when you need SKU variants, specifications, description or full seller data.
- Reviews two ways. Embed each product's top reviews inline, or switch to reviews-only mode to get one row per review across every matching product.
- 11 markets, one actor. Pin the market and the storefront renders prices in that market's own currency.
- Built for schedules. Incremental mode returns only new and changed products on recurring runs, and a fully refused run fails loudly instead of returning an empty dataset.
- Resume without re-paying. Continue an interrupted large pull with
resumeFromRunId, or extend one pull across separate runs, without re-collecting or re-billing products you already have. - Send results into your apps. Pipe a summary of every record into Notion, Linear, Airtable or Apify's own MCP server as an extra delivery step alongside the dataset.
Use Cases
- Price monitoring and repricing: track a product list's price, discount and stock across markets on a schedule and get only what changed.
- Market and competitor research: pull a category or keyword's full result set, sellers and brands to see how a niche is priced and positioned.
- Product sourcing and dropshipping: compare SKU variants, seller ratings and units sold before committing to a supplier.
- Review and reputation analysis: collect review text, ratings and buyer counts per product to spot quality issues or standout listings.
- Catalog and feed building: turn a search or category into a structured product feed for your own store, app or spreadsheet.
Data You Get
Sample shape: values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
type | "product" (also "review" in reviews-only mode) |
productId / id | "1234567890123" |
productUrl / url | AliExpress link to the product |
productName / title | "Bluetooth Earbuds, Wireless Headphones" |
currentPrice / price | 12.99 |
originalPrice | 45.90 |
discountPct / discountPercentage | 72 |
currency | "USD" |
ratingScore / rating | 4.8 |
reviewCount | 2300 |
itemSold / totalSold | "1,000+ sold" |
itemSoldCount / totalSoldCount | 1000 |
inStock / isSponsored | true / false |
sellingPoints | ["New shoppers save $5.00", "Free shipping"] |
sellerName / sellerCompanyName | "Example Store" / "Example Trading Co." |
storeId / storeUrl | "1100000000" / "https://www.aliexpress.com/store/1100000000" (the store's own id, not the seller id; filled when the product page is fetched) |
brandName | "Example Brand" |
primaryImage / imageUrl | product photo URL |
imageGallery / additionalImages | ["https://...jpg", "https://...jpg"] |
categoryId / categoryNames | category id and breadcrumb names |
sourceUrl / seedType / seedValue | which search query or URL this run started from |
shippingCountry | "US" |
fetchedAt / scrapedAt | "2026-09-30T08:20:40.310Z" |
raw | the complete source object (SERP card plus detail page when fetched) |
When Fetch full product detail is on, each product additionally carries skuList (per-variant SKU table with prices), skuPropertyList (size/color/model options), specifications, description, descriptionHtml, breadcrumbs, shipping, warranty, sellerId, storeId, storeUrl, sellerPositiveRate, brandId and detail-page images. When Include reviews is on, each product carries an embeddedReviews array; Output reviews only instead emits one record per review with reviewId, buyerName, rating, reviewTime, reviewContent, skuInfo, likeCount and media. Incremental mode adds changeType, changedFields, firstSeenAt and lastSeenAt to every record (see below).
How to Use
- Pick a mode:
search(keyword and filters) orurl(paste search, category or product links). - For search mode, fill in keywords and optional category/price/rating/sort filters. For URL mode, paste the links.
- Turn on Fetch full product detail and/or reviews only if you need them; they add cost and time.
- Set Max products total to control run size and cost, then click Start.
Search by keyword with filters:
{"mode": "search","queries": ["led strip lights"],"shippingCountry": "us","minPrice": 5,"maxPrice": 50,"minRating": 4,"sortBy": "bestSelling","maxListings": 100}
Walk a pasted search URL, with full product detail:
{"mode": "url","urls": ["https://www.aliexpress.com/w/wholesale-earbuds.html"],"fetchDetails": true,"maxListings": 50}
Reviews only, for a keyword search:
{"mode": "search","queries": ["phone case"],"reviewsOnly": true,"maxReviewsPerProduct": 50,"maxListings": 20}
One pasted product URL:
{"mode": "url","urls": ["https://www.aliexpress.com/item/1234567890123.html"],"fetchDetails": true}
Modes
- Search (
mode: search): one paginated walk per keyword inqueries(or the singlequery), with category, price, rating, free-shipping and sort filters.pagesets the start page. - URL (
mode: url): walk pasted AliExpress links forward page by page. Eight link shapes are understood:
| # | Shape | Example |
|---|---|---|
| 1 | Search results page | https://www.aliexpress.com/w/wholesale-earbuds.html |
| 2 | Search results page, start page N (a sort is optional) | https://www.aliexpress.com/w/wholesale-earbuds.html?page=2&SortType=price_asc |
| 3 | Search results page with sort | https://www.aliexpress.com/w/wholesale-earbuds.html?SearchText=earbuds&sortType=total_tranpro_desc |
| 4 | Legacy search link | https://www.aliexpress.com/wholesale?SearchText=earbuds |
| 5 | Category page | https://www.aliexpress.com/category/100003109/women-clothing.html |
| 6 | Product page | https://www.aliexpress.com/item/1005011755232439.html |
| 7 | Regional host | https://de.aliexpress.com/w/wholesale-earbuds.html |
| 8 | Search results page with filters (category id and free shipping) | https://www.aliexpress.com/w/wholesale-earbuds.html?catId=200003409&isFreeShip=y |
The URLs field now ships these eight examples as its default, so a URL-mode run with no edits scrapes all eight (it used to be empty). Search mode ignores the URLs field, so search-mode runs and their incremental state are not affected by this default. Clear the field and paste your own links to change that. m.aliexpress.com links are rewritten to the www host. A dead or removed product URL logs a per-URL warning and is not billed.
Filters and sort
categoryId (a numeric id, a category URL, or the exact name of a top-level category such as Electronics or Pet Supplies; a name is matched case-insensitively against the site's own category menu and resolved to its id at the start of the run, and a name that is not on the menu stops the run with the closest names listed), minPrice, maxPrice, minRating and freeShippingOnly narrow results; the price and rating bounds are applied after fetching. sortBy is one of relevance, bestSelling, newest, priceAsc, priceDesc.
Reviews
Turn on Include reviews on each product (fetchReviews) to embed top reviews, or Output reviews only (reviewsOnly) for one row per review. maxReviewsPerProduct defaults to 20 (maximum 5000; it used to default to 100); every delivered review row is a billed result row, so raise it deliberately. Incremental runs that embed reviews are not billed an UPDATED wave by this change: a product tracked by an older run is compared against its first 20 stored reviews, once, and a real change inside that window is still reported.
Output views
The dataset has four views: Products (core columns), All fields, Reviews and Changes (incremental mode). A run summary is also saved to the key-value store under OUTPUT.
Pricing
Pay per event. The live events are: a product result (each product or review row returned in the dataset), a detail enrichment surcharge (once per product, only when Fetch full product detail is on or in reviews-only mode, never per review) and the standard Apify actor-start event. Unchanged, skipped, resumed or failed rows are not charged. The Pricing tab shows the current rates; use Max products total to cap a run.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/aliexpress-scraper").call(run_input={"mode": "search", "queries": ["airpods"], "maxListings": 20})for product in client.dataset(run["defaultDatasetId"]).iterate_items():print(product["productName"], product["currentPrice"], product["currency"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/aliexpress-scraper').call({ mode: 'search', queries: ['airpods'], maxListings: 20 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Running multiple queries or URLs in one run
When a run has more than one search query or pasted URL, Max products total is split evenly between them. A product id seen under one query or URL is not collected again under another one in the same run, so you never pay twice for a product that shows up in overlapping searches. This sharing has side effects when the searches overlap:
- When a query or URL reaches its share partway through a results page, the rest of that page still counts as seen. If those products also appear under a later query or URL, they are skipped there too, even when the later one has room left. In incremental mode their price or stock changes are then not reported for that run.
- If a later query's or URL's results page contains only products already seen under earlier ones, the actor treats that page as the end of that query's results and stops. Its later pages are not collected. With
emitExpiredon, tracked products that only appear on those later pages can come back asEXPIRED(and billed) even though they are still listed. - In incremental mode, a product found under a different query or URL than last time comes back
UPDATED, with onlysourceUrlandseedValueinchangedFields.
For monitoring overlapping searches with incremental mode, run each query or URL as its own run with its own state key.
Drop-in fields for other AliExpress search actors
Every product row also carries a legacy object with the search-card layout other AliExpress search actors publish: image (imgUrl, imgWidth, imgHeight), store (storeUrl, storeName, storeId), title (displayTitle, seoTitle), evaluation (starRating, and starUrl only when the search card provides one, which the live cards currently do not), prices (originalPrice, salePrice, currencyCode), trade.tradeDesc, itemType, productId and productDetailUrl. The inputs query (one keyword) and page (start page, search mode) are accepted too. Sub-objects without data are left out: store is filled from the product page (the search card carries no store block), and seoTitle is present only when the site serves one, which the live search cards currently do not. This is output only and does not change what is billed or what counts as a changed product in incremental runs.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted run: paste its run or dataset ID and the actor skips every product already saved there (matched by product id), so you don't pay twice. Skipped products are not fetched, returned or charged in any mode: products, embedded reviews, reviews-only rows and the detail surcharge. Some limits:- In reviews-only mode, a product counts as saved once any of its reviews is in that dataset. Reviews the interrupted run had not reached yet for that product are not collected.
- If the result order has shifted since the interrupted run (new products near the top), a results page made up only of already-saved products can end the walk early. Check the item count, and resume again if it looks short.
- Starting incremental mode with resume: the resumed products are not re-billed in that run. The next scheduled run returns them once as
UPDATED(products) orNEW(reviews-only), and they are billed then.
- Incremental mode (
incrementalMode) is for recurring runs over the same search or URL scope. Each product is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEAREDorEXPIRED(only after a run that fully scanned the tracked scope, and only withemitExpired).stateKeynames or shares the stored state. Never reuse onestateKeyfor both a reviews-only run and a product run: reviews-only state is tracked per review, so a product run withemitExpiredon would return (and bill) those stored reviews asEXPIREDrows. Compared: every output field. Never counted as a change (still kept in the output): fetch timestamps, the raw passthrough, sold counters, and the rotating image CDN host (only the image path counts). Nested fields are covered too.ignoreFieldsForChangesexcludes more (names, or dotted paths such asskuList.image).changeScopeset to price and stock only makesUPDATEDfire solely when a price, discount, in-stock flag or per-SKU stock or price moves.compactUpdatesmakesUPDATEDandEXPIREDrows slim (ids, url, title, current price and stock,priceChanges,variantChanges); NEW rows stay full, and a compact row does not carry the detail data so it does not bill the detail-enrichment event.expireAfterMissedScans(default 1) tombstones a product only after that many consecutive complete scans missed it;EXPIREDmeans the product dropped out of the scanned results, not that it is unavailable. A pasted search URL that starts at page 2 or later, a capped run or a resumed run is a partial scan and never producesEXPIRED. The controls above are all off by default. Two defaults did change in this version, and both are handled so that existing monitoring is not re-billed: the review cap (20 instead of 100, see Reviews) and the URLs example list (it applies to URL mode only; search-mode state keys are unchanged). When a field is added to the output in a newer version (store id and URL, seller name, positive rate, selling points, was-price, in-stock flag, brand name), a product tracked by an older run does not come backUPDATEDjust because that one field now appears; this is judged field by field, applies once per tracked product, and a field that was already stored and then changes, or any other change in the same row, is still reported. Typing changes alone (a number now written as text) are never a change. With incremental mode off, output is exactly as before. - Tip: turning on
emitExpiredwith a low Max products total can still return more EXPIRED rows than that cap in one run: the EXPIRED batch is computed after the capped scan finishes and is not itself limited by Max products total. Raise the cap, or leaveemitExpiredoff, if you monitor a large tracked scope with a small per-run budget. - Tip: in incremental mode, Max products total limits the products scanned, not the rows returned. Unchanged products use up the cap even though they are hidden. If the top results stay unchanged, each scheduled run can hit the cap on them and return nothing, even when new products appear further down. This happens again on every run until the cap is raised. Set Max products total well above the number of products you expect to stay unchanged, or to
0.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify β Settings β Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (keyword/category) or url (walk pasted links). |
shippingCountry | string | us | Target market: pins the connection exit country and the currency/locale the storefront renders. |
queries | array | (none; the form is prefilled with airpods) | Search keywords (search mode). One paginated walk per keyword. |
query | string | (none) | One keyword (search mode); alternative to queries. |
page | integer | 1 | Start page (search mode). |
categoryId | string | (none) | Optional: restrict a keyword search to one AliExpress category. Accepts a numeric id, a /category/<id>/ URL, or the exact name of a top-level category (case-insensitive). A link that carries no category id is ignored with a warning and the search runs unfiltered. |
freeShippingOnly | boolean | false | Request only free-shipping eligible items. |
sortBy | string | relevance | relevance, bestSelling, newest, priceAsc, or priceDesc. |
urls | array | one example per URL shape | AliExpress search (including ?page=N start pages, sorted and legacy /wholesale?SearchText= links), category, product and regional-host (for example de.aliexpress.com) URLs to walk (url mode). The field ships eight example URLs (one per shape above) as its default, so a URL-mode run with no edits scrapes them; m.aliexpress.com links are rewritten to www. A dead or removed product URL logs a per-URL warning and is not billed. |
minPrice | integer | (none) | Minimum price in the market's currency; dropped after fetching. |
maxPrice | integer | (none) | Maximum price in the market's currency; dropped after fetching. |
minRating | integer | (none) | Drop products whose average rating is below this (1 to 5). |
reviewsOnly | boolean | false | Emit one record per review instead of one per product. |
fetchReviews | boolean | false | Embed each product's top reviews (adds a separate fetch per product). |
maxReviewsPerProduct | integer | 20 | Hard cap on reviews fetched per product (maximum 5000; previously defaulted to 100). Raise it deliberately: every delivered review row is a billed result row. |
fetchDetails | boolean | false | Visit each product's detail page for SKU variants, specifications, description and seller data (adds a per-product fetch and surcharge). |
maxPages | integer | 0 | Optional bound on result pages per query/URL (0 = no limit; still bounded by Max products total). |
maxListings | integer | 3 | Hard cap on products across all keywords/URLs (0 = unlimited). In reviews-only mode this caps discovered products, not reviews. |
resumeFromRunId | string | (none) | Continue one interrupted run without re-collecting or re-billing products already saved there. |
incrementalMode | boolean | false | Return only new and changed products on recurring runs of the same search/URL scope. |
stateKey | string | (none) | Name or share an incremental-mode state; auto-derived from the scope when left empty. |
expireAfterMissedScans | integer | 1 | With Emit expired: consecutive complete scans that must miss a product before it is marked EXPIRED. |
changeScope | string | all | all or priceAndStock (only price, discount, stock and per-SKU stock or price create UPDATED). |
compactUpdates | boolean | false | Slim UPDATED and EXPIRED rows with old and new price and stock. |
ignoreFieldsForChanges | array | [] | Field names or dotted paths that never make a product UPDATED. |
emitUnchanged | boolean | false | Also return (and bill) unchanged products. |
emitExpired | boolean | false | Also return (and bill) products no longer found after a complete scan. |
proxy | object | Automatic | Connection settings. The automatic connection was measured to load search and product pages; Residential is tried automatically when it is refused, and you can also select it yourself. Your own proxy URLs are used as given, with no backup connections. |
mcpConnectors | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which item pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live listing.
{"type": "product","id": "1234567890123","url": "https://www.aliexpress.com/item/1234567890123.html","productName": "Bluetooth Earbuds, Wireless Headphones with Noise Cancelling","productId": "1234567890123","productUrl": "https://www.aliexpress.com/item/1234567890123.html","sourceUrl": "https://www.aliexpress.com/w/wholesale-earbuds.html","seedType": "url","seedValue": "https://www.aliexpress.com/w/wholesale-earbuds.html","shippingCountry": "US","currency": "USD","title": "Bluetooth Earbuds, Wireless Headphones with Noise Cancelling","price": 12.99,"originalPrice": 45.9,"discountPercentage": 72,"rating": 4.8,"totalSold": "1,000+ sold","totalSoldCount": 1000,"currentPrice": 12.99,"discountPct": 72,"ratingScore": 4.8,"reviewCount": 2300,"inStock": true,"isSponsored": false,"sellingPoints": ["New shoppers save $5.00", "Free shipping"],"sellerName": "Example Store","brandName": "Example Brand","primaryImage": "https://ae-pic-a1.aliexpress-media.com/kf/S00000000000000000a.jpg","imageGallery": ["https://ae-pic-a1.aliexpress-media.com/kf/S00000000000000000b.jpg"],"categoryId": "100003109","raw": { "serp": {} },"fetchedAt": "2026-09-30T08:20:40.310Z"}
Plan Requirement
The default Automatic connection is used first; it was measured to load search, category and product pages. Whether the cheaper datacenter tier alone is enough was not measured, so no datacenter-only guarantee is made. If the site refuses the automatic connection, the actor escalates to Apify Residential, then to an unblocking pool and backup connections, and the run log says when a backup connection served the run ("Served over the backup connection (the default connection was unavailable)."). Apify free plans do not include residential proxies: there the log warns "The selected connection is unavailable to this run. Apify free plans do not include residential proxies." and the run continues on a backup connection, where some pages may still be unavailable. If the proxy is refused for the whole account, the run fails with a message that a paid plan is required (https://apify.com/pricing). For very large or frequent runs, select Residential under Proxy & connection.
FAQ
How much does it cost?
You pay per product (or review) result returned, plus a per-product enrichment surcharge only when Fetch full product detail is on or in reviews-only mode (once per product, not per review). The Pricing tab shows the current rates. Use Max products total to cap the cost of any run.
Is it legal to scrape AliExpress?
This actor collects only publicly available product listing data. You are responsible for how you use it: follow AliExpress's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices, titles and specifications are generally facts, but product photos and seller branding may be subject to third-party rights.
Can I get only new or changed products on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated products, and unchanged ones are not billed unless you turn on Emit unchanged.
Why does the actor recommend keeping detail and review fetching off?
The default results-page fields already cover id, title, price, discount, rating, sold count, images and seller name, which is enough for most uses. Turning on Fetch full product detail or Include reviews on each product adds a separate page fetch per product, which meaningfully slows the run; detail fetching also adds a per-product surcharge. Turn them on only when you specifically need SKU variants, specifications or review text.
Why did my run fail instead of returning an empty dataset?
AliExpress intermittently refuses a share of connections with a refusal page instead of real results. Search result pages are first read directly over the selected connection, with no browser; the browser starts only for product pages, reviews, a category-name lookup, or a page the direct read was refused, and a refusal never moves the run to another connection type by itself. The actor rotates to a fresh connection automatically (up to 12 browser launches per run, 3 attempts per connection type, escalating Automatic, Residential, the unblocking pool, then backup connections). If every connection tried is still refused, the run fails with a clear message instead of returning an empty dataset, so "the site refused every attempt" is never confused with "no matching products." Simply retry; a fresh connection is very likely to succeed. A partially refused run instead reports a "Partial results" status message and keeps what it found.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
π More scrapers you might like
Pair this actor with these related scrapers from the same team:
| π Cdiscount Scraper Scrape product data from Cdiscount.com by keyword, category, or URL. Apply filters and... | π§© Miravia Spain Scraper Scrape Miravia Spain by keyword, seller store or URL. Every row carries the selling price... |
| π Americanas Brazil Scrape Americanas Brazil (americanas.com.br) by keyword, department or pasted link... | π OTTO.de Scraper Scrape OTTO.de products by search or URL. Extract prices, discounts, variants... |
| π Lazada Scraper Scrape Lazada products and reviews across SEA markets, including Malaysia, Singapore... | π UNIQLO Scraper Scrape UNIQLO products across 21 country storefronts in local currency. Price with pre... |
π Browse all abotapi scrapers
π¬ Support
- π Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- π οΈ Need another site, extra fields or a private build? Email contact@abotapi.com.
- β Enjoying it? A quick review on the actor page helps other users find it.