kestrel actors docs

kestrel actors › Ecommerce

AliExpress Reviews Scraper — Product Feedback & Ratings

This AliExpress reviews scraper turns any product page into a clean table of AliExpress product reviews: the 1-5 star rating, the buyer's own words plus a machine translation into the language you pick, the buyer's country, the exact SKU variant the review is about, the photos they uploaded, the shipping method the order used, the follow-up review they came back and wrote weeks later, and the helpful votes other shoppers gave it. Give it product URLs or item ids and it reads the same public AliExpress feedback endpoint the product page's own review widget calls — no API key, no login, no cookies, no headless browser. It is built as an AliExpress product research tool for dropshipping product validation: buyer reviews and buyer photos are how you tell a product worth listing from one that will drown you in refunds, and this is the cheapest way to pull them in bulk.

Run AliExpress Reviews Scraper on Apify →

AliExpress reviews scraper pricing: what one row costs

EventWhat it isPriceCharged for
reviewReview row$0.002One AliExpress buyer review: the 1-5 star rating, the buyer's text and AliExpress's translation of it, buyer name and country, the SKU variant bought, the buyer's photos, the shipping method, the follow-up review, helpful votes and the structured product attributes rated. Products with no reviews, unknown item ids, product rows, duplicate targets and filtered reviews are free.

AliExpress reviews scraper input reference

Every field of AliExpress Reviews, straight from the actor's input schema.

FieldTypeDefaultWhat it does
productUrlsarray[]AliExpress product pages, e.g. https://www.aliexpress.com/item/1005006255429323.html — aliexpress.us, aliexpress.ru, the /i/<id>.html short form and any share link with tracking junk after the ? all work, because every AliExpress item URL carries the numeric item id in its path. No page has to be loaded to resolve them.
productIdsarray[]AliExpress numeric item ids, e.g. 1005006255429323 — the number in /item/<id>.html, and what an earlier run's product_id gives you. All three AliExpress id namespaces work (1005…, 3256… on aliexpress.us, and the legacy 2255…/4000… ids), and they resolve to the same reviews.
maxReviewsPerProductinteger1000 = every review the product has (AliExpress pages them in blocks of up to 500; a best-seller runs to a few thousand). N = the first N AliExpress serves. This is the main cost control: it caps how many reviews are read, and the filters below then decide which of them are worth paying for. Range 0-50000.
reviewsFilterstring (enum)"all"The only three filters AliExpress itself applies, so the pages you do not want are never even fetched. All reviews = no filter. Only reviews with photos = AliExpress's own photo filter (buyer photos are the fastest way to judge whether a product is worth listing). Only reviews with a follow-up = buyers who came back weeks later to update their review, the best durability signal on the site. Only buyers from the chosen country = AliExpress's one country filter; it needs exactly one entry in Buyer countries. One of: all, with_photos, with_follow_up, same_country.
minRatinginteger0AliExpress reviews are whole stars, 1 to 5. 0 = no floor; 5 keeps only the perfect scores. AliExpress has no server-side star filter, so this one runs in the actor — the pages are still read, the filtered reviews are simply never charged. Range 0-5.
maxRatinginteger0The other end of the same 1-5 scale: 0 = no ceiling, 3 gives a complaints feed of everything a buyer was unhappy about. Combine with Sort = lowest rated first and the Complaints view in the dataset tab. Filtered reviews are never charged. Range 0-5.
requireTextbooleanfalseDrop star-only reviews (an empty feedback field) before billing. On AliExpress roughly one review in five is a bare star rating, so this is a real saving when you are after review text for product research.
countriesarray[]Keep only reviews from buyers in these ISO-2 countries, e.g. US, BR, ES, PL. Empty = every country. The first entry also tells AliExpress which market to answer as, which decides the ordering it serves and which reviews the "Only buyers from the chosen country" filter keeps. Filtered reviews are never charged.
sortstring (enum)"default"The order the reviews are delivered in. AliExpress ignores every sort parameter its own endpoint accepts (verified 2026-08-29: complex_default, dateDescending and positive_first all return the identical page), so this actor orders the reviews it collected instead of pretending the site does. Newest/oldest use the review date; lowest rated first is the complaints feed. One of: default, newest, oldest, lowest_rating, highest_rating.
languagestring (enum)"en_US"AliExpress machine-translates every review into one language of your choice: text_translated comes back in this language whatever the buyer wrote in, and text keeps the original. It also decides the language the review date is written in, which the actor parses back to an ISO date for you. One of: en_US, es_ES, pt_BR, fr_FR, de_DE, it_IT, nl_NL, pl_PL, tr_TR, ru_RU, ja_JP, ko_KR.
includeProductRowbooleantrueAlso emit one free row per product with its title, image, average rating, the full 1-5 star histogram, the positive/neutral/negative split, how many of its reviews carry photos or a follow-up, and the structured attributes buyers rated it on. It is what a product-research run actually wants, it costs one extra request per product, and it is never charged.
sessionsinteger2How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16.
perIpnumber1Pace for each session. AliExpress's feedback endpoint answered 150 back-to-back requests from a single datacenter IP with no throttling at all, and 10 of 10 at 1 req/s; 1-2 is comfortable. Range 0.1-5.
proxyConfigurationobject{"useApifyProxy": true}Apify Proxy with the datacenter group is enough: the feedback endpoint answers plain HTTP requests from datacenter ranges with no key, no cookies and no bot gate. Residential works too and costs more. Keep the default.

Example input

{
  "productUrls": ["https://www.aliexpress.com/item/1005006255429323.html"],
  "productIds": ["1005008342451530"],
  "maxReviewsPerProduct": 200,
  "reviewsFilter": "all",
  "minRating": 0,
  "maxRating": 3,
  "requireText": true,
  "countries": ["US"],
  "sort": "lowest_rating",
  "language": "en_US",
  "includeProductRow": true,
  "sessions": 2,
  "perIp": 1,
  "proxyConfiguration": {"useApifyProxy": true}
}

AliExpress reviews scraper output fields

One row per AliExpress buyer review (charged), plus a free product context row and a free status row per target.

Dataset views in the Apify Console: Overview, Reviews, Complaints, Photo reviews, Products, Status.

FieldTypeWhat it holds
typestringreview | product | status
targetstringThe input value (product URL or item id) this row came from
product_idstringAliExpress's numeric item id (the number in /item/<id>.html)
product_titlestringProduct title as AliExpress lists it
product_urlstringThe product's page on aliexpress.com
product_imagestringMain product image URL (product rows)
review_idstringAliExpress's numeric evaluation id, unique per review
review_datestringPublication date, YYYY-MM-DD, parsed back from the language the review was requested in
review_date_rawstringThe date exactly as AliExpress wrote it, e.g. "02 Mar 2026" or "02 мар 2026"
ratingnumberStar rating, 1-5 (on product rows, the product's average as a decimal such as 4.2)
rating_100integerAliExpress's own 0-100 score behind the stars: 100 = 5 stars, 80 = 4 stars, and so on
textstringThe review exactly as the buyer wrote it; null when they left stars only
text_translatedstringAliExpress's machine translation of the review into the language the run asked for
languagestringLanguage code of the served text, e.g. en, ru, es
buyer_namestringBuyer display name, masked by AliExpress ("S***l") or "AliExpress Shopper" when they stayed anonymous
buyer_countrystringBuyer country as an ISO-2 code, e.g. US, BR, PL
buyer_genderstringBuyer gender when AliExpress publishes it
buyer_anonymousbooleantrue when the buyer hid their identity
buyer_avatarstringBuyer avatar image URL, when there is one
sku_idstringAliExpress's numeric id of the exact variant the buyer bought
sku_infostringThe variant in words, e.g. "Color:LP40 white" — which colour or size the review is actually about
imagesarrayFull-size URLs of the photos the buyer attached to the review
image_countintegerHow many photos the review carries
logisticsstringThe shipping method the order used, e.g. "AliExpress Selection Standard Shipping"
helpful_votesintegerHow many shoppers marked the review helpful
unhelpful_votesintegerHow many shoppers marked the review unhelpful
attributesobjectThe structured attributes AliExpress asked this buyer to rate, e.g. {"Quality of sound": "Good", "Durability": "Fits ok"}
follow_up_textstringThe follow-up review the buyer added later, in their own words
follow_up_translatedstringMachine translation of the follow-up review
follow_up_datestringDate of the follow-up review, YYYY-MM-DD
follow_up_daysintegerHow many days after the first review the follow-up was written — the durability signal
follow_up_imagesarrayPhotos attached to the follow-up review
featuredbooleantrue when AliExpress promotes this review to the top of the product page
ai_generatedbooleanAliExpress's own flag for a review it believes was written by AI
rating_percentnumberThe product's average rating as a percentage, AliExpress's own number (product rows)
reviews_totalintegerHow many reviews AliExpress says the product has (product rows)
reviews_fetchedintegerReviews read in this run for the product, before filters (product rows)
five_starintegerNumber of 5-star reviews (product rows)
four_starintegerNumber of 4-star reviews (product rows)
three_starintegerNumber of 3-star reviews (product rows)
two_starintegerNumber of 2-star reviews (product rows)
one_starintegerNumber of 1-star reviews (product rows)
positive_countintegerReviews AliExpress counts as positive, 4-5 stars (product rows)
neutral_countintegerReviews AliExpress counts as neutral, 3 stars (product rows)
negative_countintegerReviews AliExpress counts as negative, 1-2 stars (product rows)
positive_ratenumberShare of positive reviews as a percentage (product rows)
negative_ratenumberShare of negative reviews as a percentage (product rows)
with_photos_countintegerHow many of the product's reviews carry buyer photos (product rows)
with_follow_up_countintegerHow many carry a follow-up review written later (product rows)
same_country_countintegerHow many were written by buyers in the country the run asked as (product rows)
product_typestringAliExpress's own product classification, e.g. ORDINARY (product rows)
review_attributesarrayThe structured attributes buyers rated the product on, with how many said each thing — the fastest read on what people love and hate (product rows)
statusstringok | no_reviews | not_found | duplicate | error (status rows)
reviewsintegerReviews delivered and charged for this target (status rows)
filteredintegerReviews dropped by the filters, never charged (status rows)
pagesintegerRequests made for this target (status rows)
totalintegerReviews AliExpress lists for the product (status rows)
repeatsintegerReviews AliExpress served twice across its shuffled result shards and this run dropped (status rows)
duplicatesinteger1 when this target resolved to a product another target already covers (status rows)
errorstringWhy a target failed (status rows)
fetched_atstringUTC timestamp of the request

Example row from a real run

Taken from the actor's README — this is the shape your dataset holds.

{
  "type": "review",
  "review_id": "60094105248269979",
  "product_id": "1005006255429323",
  "product_title": "Original Lenovo LP40 wireless headphones TWS Bluetooth Earphones",
  "product_url": "https://www.aliexpress.com/item/1005006255429323.html",
  "rating": 5,
  "rating_100": 100,
  "review_date": "2025-11-29",
  "review_date_raw": "29 Nov 2025",
  "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_name": "AliExpress Shopper",
  "buyer_country": "US",
  "buyer_anonymous": true,
  "sku_id": "12000036486711239",
  "sku_info": "Color:LP40 white",
  "images": ["https://ae-pic-a1.aliexpress-media.com/kf/Ab1b4386f35a14c28b499f08897b6fea0y.jpg"],
  "image_count": 1,
  "logistics": "AliExpress Selection Standard",
  "helpful_votes": 0,
  "unhelpful_votes": 0,
  "attributes": {"Quality of sound": "Fast", "Durability": "Fast", "User Friendly": "Good"},
  "follow_up_text": null,
  "follow_up_days": null,
  "featured": true,
  "ai_generated": false
}
{
  "type": "product",
  "product_id": "1005006255429323",
  "product_title": "Original Lenovo LP40 wireless headphones",
  "product_image": "https://ae-pic-a1.aliexpress-media.com/kf/Haa1bfb60935a4a3ca5e24c9ef3bcd163R.jpg",
  "product_url": "https://www.aliexpress.com/item/1005006255429323.html",
  "rating": 4.2,
  "rating_percent": 84.9,
  "reviews_total": 2836,
  "reviews_fetched": 100,
  "five_star": 1888, "four_star": 380, "three_star": 207, "two_star": 106, "one_star": 266,
  "positive_count": 2268, "neutral_count": 207, "negative_count": 372,
  "positive_rate": 79.7, "negative_rate": 13.0,
  "with_photos_count": 313,
  "with_follow_up_count": 82,
  "same_country_count": 257,
  "product_type": "ORDINARY",
  "review_attributes": [{"attribute": "Durability : Fits ok", "reviews": 1493, "value_id": 1500325947}]
}

AliExpress reviews scraper in Python, JavaScript and curl

Python

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",
    "requireText": True,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["type"] == "review":
        print(row["rating"], row["buyer_country"], row["sku_info"], row["text"])

JavaScript (Node)

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/aliexpress-reviews-scraper').call({
    productIds: ['1005006255429323'],
    maxReviewsPerProduct: 0,
    maxRating: 3,
    sort: 'lowest_rating',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.type === 'review').length, 'complaints');

curl

curl -X POST "https://api.apify.com/v2/acts/kestrel~aliexpress-reviews-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"productUrls":["https://www.aliexpress.com/item/1005006255429323.html"],"maxReviewsPerProduct":100}'

The same three calls are runnable files in the examples repository: examples/aliexpress-reviews-scraper.py|.js|.sh.

What are the limits of the AliExpress feedback endpoint?

Worth knowing before you plan a job, all measured on 2026-08-29:

  • AliExpress does not sort. Every sort value its own endpoint accepts returns the identical page. That is why sort here orders the reviews the run collected rather than pretending the site does it.
  • Page 1 always returns 20 rows, whatever page size is requested, while later pages honour up to 500 at a plain offset. A naive scraper asking for 100 per page silently loses reviews 21-100 of every product. This one only ever asks for a page size that divides the offset and counts the rows it actually received, so nothing is skipped.
  • The backend shuffles. Two result orderings are served at random, so a deep walk sees a small share of reviews twice. They are deduped by review_id, and the count that was dropped is reported in repeats.
  • An empty answer is ambiguous. A product with no reviews and an item id that does not exist return byte-identical responses. This actor never guesses: an empty list is re-asked on a fresh IP and then settled against the product page, so you get no_reviews or not_found, never a false "no reviews" because of a throttle. A genuine refusal is reported as status: "error" with the reason.
  • Price, stock and seller data are not in this dataset. The product page renders them in the browser, so a reviews scraper cannot read them without one. Use a product scraper for those.

FAQ

Do I need an AliExpress API key or account?

No. This reads the same public feedback endpoint the product page itself calls. No key, no cookies, no login, no browser.

How do I get AliExpress reviews by product id?

Put the numeric id straight into productIds. It is the number in /item/<id>.html, and it is what the product_id column of an earlier run gives you, so a two-stage pipeline (find products, then pull reviews) needs no URL building at all.

Can I download AliExpress reviews as a CSV?

Yes — every run's dataset exports to CSV, JSON, Excel, XML or RSS from the run page, or through the Apify API. Use the Reviews or Complaints view first if you want a tidy column set.

How do I get AliExpress negative reviews only?

Set maxRating: 3 and sort: "lowest_rating". The Complaints view then puts the text, the photos, the variant, the shipping method and any follow-up side by side. Filtered-out reviews are never charged.

Can I get only AliExpress review photos?

Yes. Set reviewsFilter: "with_photos" — that is the site's own filter, so the pages without photos are never fetched. The images and image_count columns carry the full-size URLs of the AliExpress buyer photos.

Is there an AliExpress reviews API?

There is no public AliExpress reviews API you can sign up for. This actor is the practical substitute: a REST endpoint you call with a token, returning AliExpress reviews without an api key of your own, in JSON or CSV.

How many reviews can I get per product?

Every review the site will page through. maxReviewsPerProduct: 0 takes them all; the ceiling in practice is what a product actually has. Very large products run to a few thousand and take a couple of minutes.

Does it translate reviews?

Yes. text keeps the buyer's original words and text_translated carries the site's machine translation into whichever of the twelve language options you choose, so a Polish buyer's complaint arrives readable in English.

Can I schedule it to monitor new reviews?

Yes — schedule the actor daily or weekly, sort newest, cap maxReviewsPerProduct at a hundred or so, and dedupe on review_id downstream. That is the cheapest form of review monitoring for a catalogue you resell.

What happens if a product id is wrong?

You get a free status row with status: "not_found" and a reason, and the run carries on with the other targets. Nothing is charged.

How to use AliExpress Reviews Scraper

Run AliExpress Reviews Scraper

The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/aliexpress-reviews-scraper