kestrel actors docs

kestrel actorsGuides

How to scrape AliExpress product reviews without silently losing rows

The AliExpress feedback endpoint is open, fast and needs no key. It also has three behaviours that make a straightforward pager quietly wrong: page 1 ignores the page size you ask for, the backend serves two orderings at random, and every sort parameter it accepts does nothing.

The short version

  1. Take the item id from the URL. Every AliExpress URL form carries it — /item/1005006255429323.html, the /i/<id>.html short form, share links with tracking parameters, and all three id namespaces resolve to the same reviews.
  2. Read page 1 as 20 rows. Page 1 returns 20 rows whatever page size you request. Ask for anything else and you lose the difference without an error.
  3. Advance by the rows you actually received. Later pages are a plain offset and honour up to 500 — but only if the page size divides the offset. Count what arrived rather than what you asked for.
  4. Deduplicate by review id. The backend serves two orderings at random, so a deep walk sees a share of reviews twice. Dedupe on the review id and count the repeats.

The endpoint is open. The paging is the problem.

AliExpress publishes its buyer feedback through a public API that needs no key, no login, no cookies and no browser, over a plain datacenter proxy. Getting at the reviews is not the hard part. Getting all of them is.

Three measured behaviours, all from 2026-08-29:

Page 1 always returns 20 rows. Whatever page size you request. Ask for 100 and you get 20, with no error and no indication that 80 are missing. Then most pagers compute the next offset as page * pageSize — 100 — and jump straight past reviews 21 to 100. Every product. Silently. A 2,836-review product read this way returns a corpus with a hole in the front of it, and the hole is invisible because the rows that do arrive are all valid.

Later pages honour up to 500 at a plain offset — but only when the page size divides the offset cleanly. Some offsets short-change you otherwise.

The backend shuffles. Two result orderings are served at random, so a deep walk sees a share of reviews twice. They are not duplicates in the data; they are the same review served again in a different order.

The walk that works, then, is: ask only for page sizes that divide the offset, advance by the number of rows you actually received rather than the number you asked for, and deduplicate on the review id while counting the repeats. Done that way, a 2,836-review product reads in about 15 requests instead of 142 — because large later pages are allowed once the offset arithmetic is right.

Working code

from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/aliexpress-reviews-scraper").call(run_input={
    "productUrls": ["https://www.aliexpress.com/item/1005006255429323.html"],
    "maxReviewsPerProduct": 500,
    "reviewsFilter": "with_photos",   # applied by AliExpress: unwanted pages are never fetched
    "requireText": True,
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
product = next(r for r in rows if r["type"] == "product")
print(product["reviews_total"], "reviews;", product["with_photos_count"], "with photos;",
      product["negative_rate"], "% negative")
for r in [x for x in rows if x["type"] == "review"][:5]:
    print(r["rating"], r["buyer_country"], r["sku_info"], "|", r["text"][:70])

Three of the filters — photos only, follow-ups only, same-country only — run on AliExpress's side, so the pages you do not want are never fetched. Rating, text and country filters run before billing. Both save money; only the first saves time.

A real row

{
  "type": "review",
  "review_id": "60094105248269979",
  "product_id": "1005006255429323",
  "rating": 5,
  "rating_100": 100,
  "review_date": "2025-11-29",
  "text": "Fast delivery! Package came within estimate delivery. The headphones came in perfect condition, and the sound and connection is very nice.",
  "text_translated": "Fast delivery! Package came within estimate delivery.",
  "language": "en",
  "buyer_country": "US",
  "sku_id": "12000036486711239",
  "sku_info": "Color:LP40 white",
  "images": ["https://ae-pic-a1.aliexpress-media.com/kf/Ab1b4386f35a14c28b499f08897b6fea0y.jpg"],
  "logistics": "AliExpress Selection Standard",
  "attributes": {"Quality of sound": "Fast", "Durability": "Fast", "User Friendly": "Good"},
  "follow_up_text": null,
  "follow_up_days": null,
  "featured": true,
  "ai_generated": false
}

Four fields there are the reason to scrape AliExpress rather than Amazon for product research:

The free product row is often the whole answer

For sourcing work at scale you frequently do not need review text at all. The product row costs nothing and carries the full star histogram (five_star through one_star), the positive/neutral/negative split with rates, how many reviews carry photos, how many have a follow-up, how many came from your own country, and the structured attributes with counts.

Run 500 candidate products, read 500 free rows, shortlist twenty, then pull review text for those twenty. That is the dropshipping-research shape, and it costs about $2 rather than $200.

The honest limitations

Run it

AliExpress Reviews Scraper — $0.002 per delivered review, with product rows, status rows, unknown item ids, duplicates and filtered reviews free. Related: how to scrape Amazon reviews and its 13-review ceiling, and building a voice-of-customer dataset across marketplaces.

FAQ

Why does sorting AliExpress reviews do nothing?

Because the endpoint accepts every sort value and returns the identical page for all of them. That was measured across every value the endpoint itself advertises. Any tool offering "sort by lowest rating" on AliExpress is sorting client-side after collection — which is fine, as long as it says so, because it means a "20 worst reviews" request still reads the whole corpus.

Does an empty response mean the product has no reviews?

Not on its own. A product with no reviews and an item id that does not exist return byte-identical responses. The only honest resolution is to re-ask on a fresh IP and then settle it against the product page, so the outcome is "no reviews", "not found" or "error" — never a false empty caused by a throttle.

Can I get prices, stock or seller data from the review endpoint?

No. The product page renders those in the browser, so a reviews scraper cannot read them without one. The free product row here carries the rating summary — the full star histogram, the positive/neutral/negative split, how many reviews have photos or a follow-up — and nothing about price.

What is a follow-up review?

A second review the same buyer adds weeks after the first, once the product has been used. AliExpress publishes it attached to the original. It is the highest-signal text in the whole dataset for durability questions, and almost nobody exports it.

Run AliExpress Reviews Scraper on Apify →

More guides