Arcadium Data Scrapers
Home / Guides / AliExpress Search Scraper

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)

  1. Open arcadium/aliexpress-search and sign in to Apify (a free account works).
  2. Fill in the input form. The fields below are the ones the example uses.
  3. Set a maximum cost per run in the run options if you want a hard cap.
  4. Start the run. Rows appear in the Output tab as they arrive.
  5. Export the dataset as JSON, CSV or Excel, or connect an integration.
Input fieldTypeDescription
searchTermsarrayKeywords 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.
maxResultsPerTermintegerHow 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).
sortstringBest 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).
freeShippingbooleanOnly 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:

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
}
FieldWhat it holds
rowTypeKind 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).
statedResultsvalidation_report rows: result count AliExpress states for the term across all pages, e.g. 48213; null when not stated.
sampleSizevalidation_report rows: product rows of this run the report is computed from (= delivered products for the term).
sortvalidation_report rows: sort order of the sample: relevance, orders, priceAsc or priceDesc.
priceBandsvalidation_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.
newUserPromoSharevalidation_report rows: share (0-1) of the sample that showed AliExpress' new-user promo price (promoType 'new_user').
discountStatsvalidation_report rows: { shareDiscounted (0-1, share of products with a discount), medianPercent (median discount in %) }.
soldStatsvalidation_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.
salesConcentrationvalidation_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.
adSharevalidation_report rows: share (0-1) of paid placements in the sample; null when includeAds was off.
choiceSharevalidation_report rows: share (0-1) of the sample showing the Choice badge.
freeShippingSharevalidation_report rows: share (0-1) of the sample showing free shipping.
ratingStatsvalidation_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.
shipFromMixvalidation_report rows: warehouse countries [{ country, share (0-1) }], biggest first, over products that state one.
deliveryMaxDaysMedianvalidation_report rows: median of the slowest delivery estimate in days.
trendsvalidation_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.
trendsStatusvalidation_report rows: ok, no_data (too few searches for Trends) or unavailable (Trends rate-limited the request; the rest of the report is complete).
generatedAtvalidation_report rows: ISO timestamp of the report.
platformAlways 'aliexpress'.
productIdAliExpress item id (the 1005... number in the product URL).
titleProduct title as listed.
urlProduct page URL.
priceRegular 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.
priceMaxHighest variant price in USD; equals price when there is one price; null with price.
currencyCurrency code of the prices, normally USD.
originalPriceStruck-through list price in USD as the card shows it.
discountPercentDiscount off originalPrice in percent. Null on new-user promo cards (the shown discount is the promo's).
promoPriceAliExpress' 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.
soldTextUnits sold as shown on the site, e.g. '20K+ sold'.
soldMinNumber parsed from soldText. A lower bound when soldText ends in '+'.
soldCountThe site's exact unrounded sold counter, when the listing carries it.
ratingAverage star rating, 1 to 5.
imageUrlMain image URL.
shipFromWarehouse country code (e.g. 'CN', 'US') when the listing states it.
freeShippingTrue when shipping to the US is free.
deliveryTextDelivery estimate as shown, e.g. a date range.
deliveryMinDaysFastest estimated delivery in days.
deliveryMaxDaysSlowest estimated delivery in days.
isChoiceTrue when the listing carries AliExpress' Choice label.
isAdTrue when the listing is a paid placement.
looseMatchTrue when AliExpress found few or no real matches and listed loosely related products instead.
searchTermThe keyword that produced this row.
searchRank1-based position in the results for searchTerm, ads included.
scrapedAtISO timestamp of when the row was collected.
inputError and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'.
codeError 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).
messageError 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.