# AliExpress Search Listings Scraper – Prices & Orders (`rowfeed/aliexpress-search-listings-scraper`) Actor

AliExpress search-results listings as JSON: title, price, discount, orders sold, rating, shipping, Choice/Plus flags. Search-page data only, no product-detail pages. No login, no browser.

- **URL**: https://apify.com/rowfeed/aliexpress-search-listings-scraper.md
- **Developed by:** [Rowfeed](https://apify.com/rowfeed) (community)
- **Categories:** E-commerce, Other, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

Pull **AliExpress search-results listings** as clean JSON: title, price, discount, orders sold, rating, shipping badge and Choice/Plus flags, no login and no browser. Built for **e-commerce and dropshipping product research** (spot trending, high-order products for a niche), **market analysts** tracking price and discount patterns across a category, and **AI agents and dashboards** that need a structured "what's selling for `<keyword>`" answer.

Plain HTTPS GETs against AliExpress's own public search-results page (`aliexpress.com/w/wholesale-<term>.html`), the same server-rendered page a browser loads, parsed from its embedded JSON - no API key, no cookies, no headless browser.

### What you get

- **Every listing as one row** - product ID, title, canonical product URL, price, currency, discount %, orders sold, star rating, image, a free-shipping flag, and AliExpress's own "Choice"/"Plus" trust badges.
- **Any keyword, sorted your way** - list any number of `searchTerms`; sort by best match, price (either direction), or orders sold.
- **A cost cap per term** - `maxResultsPerTerm` stops each search once that many rows are billed, independently for every keyword you list.

### Search-results data only - by design, not by accident

This Actor reads **only** the search-results page. It never opens an individual product's detail page. That is a deliberate trade-off, not a shortcut:

- AliExpress's product-detail pages (`/item/...`) render client-side (`window._d_c_.isCSR = true`, an empty `runParams`) - a plain HTTP fetch gets a near-blank shell, not real data. Scraping them reliably needs a browser, which this Actor does not use.
- `robots.txt` also disallows `/items/*`, `/search/*` and `/productdetail/*` outright. The search-results path this Actor uses (`/w/wholesale-*.html`) is not disallowed.

The result: **no per-product variants, SKU list or live stock level** - those only exist on the detail page. What you get instead is fast, reliable, HTTP-only search-results data: exactly what a shopper sees scrolling a results grid. If you need variant/SKU/stock detail, this is not that Actor, on purpose - a narrower, honest scope that stays fully reachable is the whole point.

The same reasoning applies to **seller identity**: AliExpress's search-results JSON does not carry a store name, store ID or store URL anywhere per listing (verified by inspecting the full embedded response, not just a sample) - that data also lives only on the detail page. `store_name`, `store_id` and `store_url` are always `null` here, and `reviews_count` is always `null` too (the search page exposes only an aggregate star rating, no review count). Never guessed, never fabricated - documented `null`, the same way this Actor's siblings handle a source field that genuinely does not exist.

### Sample row

A real row from a default run (search term `"wireless earbuds"`):

```json
{
  "product_id": "1005007010288547",
  "title": "New TWS Bluetooth Headphones 9D Stereo Wireless Earphones InEar HiFi Earbuds HandsFree Headset With Microphone For Smartphone",
  "url": "https://www.aliexpress.com/item/1005007010288547.html",
  "price": 30.15,
  "currency": "NOK",
  "original_price": 67.0,
  "discount_pct": 55,
  "orders_count": 916,
  "rating": 4.9,
  "reviews_count": null,
  "store_name": null,
  "store_id": null,
  "store_url": null,
  "image_url": "https://ae-pic-a1.aliexpress-media.com/kf/S65907b18aa284998b6e9df72c1ec7070E.jpg",
  "shipping_info": null,
  "is_choice": true,
  "is_plus": false,
  "rank_on_page": 1,
  "page": 1,
  "search_term": "wireless earbuds",
  "listing_kind": "match",
  "scraped_at": "2026-09-16T07:41:26+00:00"
}
```

`url` is always constructed as `https://www.aliexpress.com/item/<product_id>.html` - the same canonical pattern AliExpress itself uses - never the funnel/bundle-deal redirect that some listings carry internally, and never fetched by this Actor. `original_price` is computed from `price` and `discount_pct` (`price / (1 - discount_pct / 100)`); AliExpress does not expose the pre-discount price as its own field on the search page, so expect it to land within a cent or two of the true value due to rounding. `currency` follows AliExpress's own geo-detection for the request (NOK above); a `?currency=` query parameter was tested and does not change it, so this Actor does not expose one as input. `shipping_info` is `"Free shipping"` when AliExpress's own free-shipping badge is present on the listing, otherwise `null` (the search page does not show a paid shipping cost). `orders_count` is parsed from AliExpress's own text (`"10K+ sold"`, `"50,000+ sold"`, `"916 sold"`) into a plain integer; the raw text survives as `orders_text`.

### Filters

| Input | Default | What it does |
|---|---|---|
| `searchTerms` | `["wireless earbuds", "led strip lights"]` | Keywords to search. Each becomes its own paginated `/w/wholesale-<term>.html` run. |
| `maxResultsPerTerm` | `100` | Stop after this many rows for each search term (independently). |
| `sortBy` | `"default"` | `"default"` (best match), `"price_asc"`, `"price_desc"`, or `"orders_desc"` (best-selling first). All four verified to actually reorder AliExpress's own results. |

The default run (both fields left as-is) searches "wireless earbuds" and "led strip lights", up to 100 rows each, in AliExpress's own default order.

### Pricing

Pay per event, no subscription: **$1 per 1,000 listings** (`$0.001` per row) - undercuts the current Store leader's $2/1,000, reflecting this Actor's narrower, more reliable scope. A default run (2 terms, `maxResultsPerTerm: 100`) costs at most $0.20. A search term AliExpress can't serve at all (a blocked/challenge response) produces one uncharged error row (`error: "blocked"`) and the run continues with the next term; a term with genuinely no matching listings is a normal successful outcome, not an error. Only zero rows *plus* at least one failed request marks the whole run failed.

A search term with **no real matches** returns rows with `"listing_kind": "recommended"` instead of AliExpress's usual "you may also like" grab-bag of unrelated products (Halloween costumes, sunglasses, whatever it's pushing that day) - and **you are never charged for them**. Only `"listing_kind": "match"` rows are billed. The detection is AliExpress's own page-level signal (whether it marks that exact result page indexable), not a guess based on how similar a title looks to your keyword; the one honest gap is that a real, relevant but very low-traffic/niche term can get the same "recommended" flag as a true no-match term, since AliExpress's own signal does not cleanly separate "not worth indexing" from "not a real match" either - see `my_actor/aliexpress.py`'s `classify_listing_kind` for the exact mechanism and what was verified.

### Use it from your tools

- **API and SDKs** - call it via the Apify API or the official Python/JavaScript clients: one call to start the run, one to fetch the results as JSON or CSV.
- **Schedules** - run it daily inside Apify and pipe freshly-priced listings into Google Sheets, a webhook or your own database.
- **n8n, Make and Zapier** - trigger runs and feed extracted listings into a workflow through Apify's integration for each.
- **AI agents and MCP** - eligible for agentic use via Apify's MCP server with pay-per-event pricing, so an agent can call it mid-task and pay only for the listings it actually pulls.

### Details

- **Source**: `https://www.aliexpress.com/w/wholesale-<term>.html`, AliExpress's own public search-results page, parsed from the `window._dida_config_._init_data_` JSON it embeds server-side. No authentication, no proxies, no browser - and never a product-detail page (see "Search-results data only" above).
- **Pagination**: follows AliExpress's own `?page=N` results (60 listings per page, AliExpress's own page size) until `maxResultsPerTerm` is reached or a page comes back empty.
- **Reliability**: 429 and 5xx responses are retried with exponential backoff (5 tries); 404 and 401/403 are never retried; a 200 response without the expected embedded JSON, or a challenge/captcha page, counts as a failure, not a silent empty success.
- **Run stats**: the `STATS` record in the run's key-value store holds row/error counts and per-category request-error counts.
- **Politeness**: 0.5 s between requests, 30 s timeout, a User-Agent that names the Actor.
- **Not affiliated with AliExpress / Alibaba Group.** Built against AliExpress's own public search page; no login, no scraping of member-only or JavaScript-only content.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords to search on AliExpress (each becomes its own `/w/wholesale-<term>.html` search-results page). Not product URLs or category URLs.

## `maxResultsPerTerm` (type: `integer`):

Stop after this many rows for each search term. Each row is one billed listing (see Pricing).

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

How AliExpress orders the results. Verified working query parameter.

## Actor input object example

```json
{
  "searchTerms": [
    "wireless earbuds",
    "led strip lights"
  ],
  "maxResultsPerTerm": 100,
  "sortBy": "default"
}
```

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("rowfeed/aliexpress-search-listings-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("rowfeed/aliexpress-search-listings-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 '{}' |
apify call rowfeed/aliexpress-search-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rowfeed/aliexpress-search-listings-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/1LurTEFoUda6QkYVM/builds/PgryynqNLdCqGfPr0/openapi.json
