How to use AliExpress Search Scraper
Step by step: in the Apify Store, over the API, from JavaScript or Python, in n8n, and from AI agents through MCP or LangChain.
$2.25 per 1,000 products on the Free plan, down to $1.50 on higher plans (live Apify price on 2026-11-02).
1. In the Apify Store (no code)
- Open arcadium/aliexpress-search and sign in to Apify (a free account works).
- Fill in the input form. The fields below are the ones the example uses.
- Set a maximum cost per run in the run options if you want a hard cap.
- Start the run. Rows appear in the Output tab as they arrive.
- Export the dataset as JSON, CSV or Excel, or connect an integration.
| Input field | Type | Description |
|---|---|---|
searchTerms | array | Keywords to search AliExpress for, for example "phone case". Each term is searched on its own and every product keeps the term and its position in the results. Also read from keywords, searchQueries or queries. |
maxResultsPerTerm | integer | How many products to return per search term, for example 120. Default: 60. A results page holds about 60 products and AliExpress shows at most 60 pages, so 3,600 is the ceiling. Also read from maxItems, maxResults or maxProducts (other AliExpress actors' names; 0 = the ceiling). |
sort | string | Best match (the site's default), most orders, or price, for example orders. Default: relevance. The other AliExpress actors' values default, price_asc and price_desc are accepted too, and rating is read as relevance (also under the name sortBy). |
freeShipping | boolean | Only products that ship for free. Default: false. Also read from freeShippingOnly. |
Example input:
{
"searchTerms": [
"phone case"
],
"maxResultsPerTerm": 30,
"sort": "relevance",
"freeShipping": false
}
2. Over the Apify API
The synchronous endpoint runs the actor and returns its rows in one call (for runs up to about five minutes; use the asynchronous run endpoint for longer ones).
curl -X POST "https://api.apify.com/v2/acts/arcadium~aliexpress-search/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchTerms":["phone case"],"maxResultsPerTerm":30,"sort":"relevance","freeShipping":false}'
3. From JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/aliexpress-search').call({
"searchTerms": [
"phone case"
],
"maxResultsPerTerm": 30,
"sort": "relevance",
"freeShipping": false
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
4. From Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/aliexpress-search").call(run_input={
"searchTerms": ["phone case"],
"maxResultsPerTerm": 30,
"sort": "relevance",
"freeShipping": False,
})
items = client.dataset(run.default_dataset_id).list_items().items # apify-client 3.x returns a Run object
print(items)
5. In n8n
Paste this node into a workflow (it is a plain HTTP Request node). Create a Header Auth credential with name Authorization and value Bearer <APIFY_TOKEN>; each returned row becomes one item for the next node.
{
"nodes": [
{
"name": "Run arcadium/aliexpress-search",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~aliexpress-search/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"searchTerms\":[\"phone case\"],\"maxResultsPerTerm\":30,\"sort\":\"relevance\",\"freeShipping\":false}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}
Ready-made workflow to import:
- Amazon and AliExpress product search to Google Sheets The same keywords searched on both marketplaces, written to one sheet with shared columns.
6. From an AI assistant (MCP)
Add Apify’s MCP server to Claude Desktop, Cursor, VS Code or any MCP client, limited to this actor:
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/aliexpress-search",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}
More on the MCP page.
7. From LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/aliexpress-search")
rows = tool.invoke({"run_input": {
"searchTerms": ["phone case"],
"maxResultsPerTerm": 30,
"sort": "relevance",
"freeShipping": False,
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])
Sample output
One row, from the example in the actor’s README. Long text is shortened and lists are cut to two items.
{
"rowType": "product",
"platform": "aliexpress",
"productId": "1005007097199333",
"title": "Wireless LED Night Lights Motion Sensor Lights Closet Kitchen",
"url": "https://www.aliexpress.com/item/1005007097199333.html",
"price": 0.33,
"currency": "USD",
"originalPrice": 5.36,
"discountPercent": 93,
"soldText": "100K+ sold",
"soldMin": 100000,
"soldCount": 241114,
"rating": 4.9,
"shipFrom": "CN",
"isAd": false,
"searchTerm": "lamp",
"searchRank": 1
}
| Field | What it holds |
|---|---|
rowType | Kind of row. Filter on this first. Values: 'product': one product (charged). 'validation_report': one market summary per search term, only with includeValidationReport (charged as validation-report). 'error': an input could not be read; see code and message (free). |
statedResults | validation_report rows: result count AliExpress states for the term across all pages, e.g. 48213; null when not stated. |
sampleSize | validation_report rows: product rows of this run the report is computed from (= delivered products for the term). |
sort | validation_report rows: sort order of the sample: relevance, orders, priceAsc or priceDesc. |
priceBands | validation_report rows: per currency { currency, products, min, p10, median, p90, max } of the regular sale price, e.g. USD 2.10 / 4.99 / 12.50 (p10 / median / p90). New-user promo prices are never included. |
newUserPromoShare | validation_report rows: share (0-1) of the sample that showed AliExpress' new-user promo price (promoType 'new_user'). |
discountStats | validation_report rows: { shareDiscounted (0-1, share of products with a discount), medianPercent (median discount in %) }. |
soldStats | validation_report rows: units sold per product { productsWithSold, totalSoldMin, min, p10, median, p90, max, share100Plus, share1000Plus (0-1 of the sample) }; counts are lower bounds ('10,000+ sold' = 10000). Null when no product shows sales. |
salesConcentration | validation_report rows: share of the sample's units sold held by the top 1, 5 and 10 products (0-1) and the HHI (0-1; above 0.25 = a few listings own the demand). Search cards carry no seller id, so this is per product, not per seller. |
adShare | validation_report rows: share (0-1) of paid placements in the sample; null when includeAds was off. |
choiceShare | validation_report rows: share (0-1) of the sample showing the Choice badge. |
freeShippingShare | validation_report rows: share (0-1) of the sample showing free shipping. |
ratingStats | validation_report rows: { productsRated, ratedShare (0-1), mean, median, share45Plus, share40To45, shareBelow40 (0-1 of rated products) } on the 1-5 star scale. Null when no product is rated. |
shipFromMix | validation_report rows: warehouse countries [{ country, share (0-1) }], biggest first, over products that state one. |
deliveryMaxDaysMedian | validation_report rows: median of the slowest delivery estimate in days. |
trends | validation_report rows: 12-month Google Trends interest of the term { geo, timeRange, monthly [{ month 'YYYY-MM', value 0-100 }], average, slopePerMonth (index points per month), lastVsFirstQuarterPct, direction rising|flat|falling }; null unless trendsStatus is ok. |
trendsStatus | validation_report rows: ok, no_data (too few searches for Trends) or unavailable (Trends rate-limited the request; the rest of the report is complete). |
generatedAt | validation_report rows: ISO timestamp of the report. |
platform | Always 'aliexpress'. |
productId | AliExpress item id (the 1005... number in the product URL). |
title | Product title as listed. |
url | Product page URL. |
price | Regular sale price in USD (lowest when the product has variants). On a new-user promo card it is the struck-through regular price of the listed variant; null only when such a card shows no struck-through price. The promo (see promoPrice) is never put here. |
priceMax | Highest variant price in USD; equals price when there is one price; null with price. |
currency | Currency code of the prices, normally USD. |
originalPrice | Struck-through list price in USD as the card shows it. |
discountPercent | Discount off originalPrice in percent. Null on new-user promo cards (the shown discount is the promo's). |
promoPrice | AliExpress' new-user welcome price shown to first-time visitors, e.g. 1.09 (USD). Set only when promoType is set; price then holds the struck-through regular price. |
promoType | 'new_user' when the card carries AliExpress' new-user / welcome-deal marker, else null. |
soldText | Units sold as shown on the site, e.g. '20K+ sold'. |
soldMin | Number parsed from soldText. A lower bound when soldText ends in '+'. |
soldCount | The site's exact unrounded sold counter, when the listing carries it. |
rating | Average star rating, 1 to 5. |
imageUrl | Main image URL. |
shipFrom | Warehouse country code (e.g. 'CN', 'US') when the listing states it. |
freeShipping | True when shipping to the US is free. |
deliveryText | Delivery estimate as shown, e.g. a date range. |
deliveryMinDays | Fastest estimated delivery in days. |
deliveryMaxDays | Slowest estimated delivery in days. |
isChoice | True when the listing carries AliExpress' Choice label. |
isAd | True when the listing is a paid placement. |
looseMatch | True when AliExpress found few or no real matches and listed loosely related products instead. |
searchTerm | The keyword that produced this row. |
searchRank | 1-based position in the results for searchTerm, ads included. |
scrapedAt | ISO timestamp of when the row was collected. |
input | Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'. |
code | Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing). |
message | Error rows: short human-readable explanation. Do not parse it, use code. |
Tutorials
Schedule it
In Apify Console, create a Task from your input and add a Schedule (for example daily). Each run writes a new dataset; integrations can push the rows to Google Sheets, Slack, a webhook or your database.