If you do dropshipping, price tracking or product research, the AliExpress product page has the data you need, but not in a form you can use. Each variant (color, size, plug type) has its own price and stock. Shipping cost and delivery time change with the country. And some prices are not what most buyers pay.
This post shows how to get all of it as clean JSON, then turn it into a CSV with one row per variant.
Disclosure: I built the tool used below. It runs on Apify and costs $1 per 1,000 products.
Why not just scrape the page?
You can, but it gets hard fast. In my tests, AliExpress showed a robot check after about 9 to 10 requests from the same IP address, and datacenter IPs were blocked from the first request. So you need residential IPs, a way to notice the block, and a retry on a fresh IP. The tool does that for you. It never tries to solve the robot check; it just moves to a new IP.
What you get for each product
- Every variant with its own price, crossed-out price, stock and image
- Shipping fee, free shipping, delivery days and the shipping method for the country you choose
- Rating, review count, sold count
- Store name and store rating
- A status for products that are missing, removed or not sold to your country
You can pick from 89 ship-to countries, 41 currencies and 16 languages.
Step 1: Install the client
pip install apify-client
You need a free Apify account for the API token. The free plan gives $5 of usage every month, no credit card needed. Find the token in Apify Console under Settings > API & Integrations.
Step 2: Get the products and save one row per variant
import csv
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("serenido/aliexpress-product-details").call(run_input={
"productUrls": [
"https://www.aliexpress.com/item/1005007857239353.html",
"3256811374070548",
],
"shipTo": "US",
"currency": "USD",
})
rows = []
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
if product["status"] != "ok":
print("skipped", product.get("productId"), product["status"])
continue
if not product["canShip"]:
print("does not ship to", product["shipTo"], product["productId"])
continue
for sku in product["skus"]:
rows.append({
"productId": product["productId"],
"variant": sku["name"],
"price": sku["price"],
"stock": sku["stock"],
"available": sku["available"],
"newShopperPrice": sku["isNewShopperDeal"],
"shippingFee": product["shippingFee"],
"deliveryDays": f'{product["deliveryDaysMin"]}-{product["deliveryDaysMax"]}',
})
with open("variants.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
writer.writeheader()
writer.writerows(rows)
print(len(rows), "variants saved to variants.csv")
You can paste full product links or just the product ids.
The result
These two products (a yoga mat and a wig) gave 104 variants. The first lines of variants.csv, from a real run on October 14:
productId,variant,price,stock,available,newShopperPrice,shippingFee,deliveryDays
1005007857239353,0.6cm Black,5.09,11,True,True,0,6-10
1005007857239353,0.6cm Purple,5.53,9,True,True,0,6-10
1005007857239353,0.3cm Dark Blue,1.97,17,True,True,0,6-10
1005007857239353,0.4cm Black,2.51,25,True,True,0,6-10
1005007857239353,0.4cm Dark Blue,3.1,3,True,True,0,6-10
Same product, same page: prices from $1.09 to $11.19 and stock from 0 to 28, depending on the variant. That is why one row per variant matters.
Three traps to watch for
1. New-shopper prices. AliExpress shows very low "welcome" prices to visitors who are not logged in, and those prices are for a first order only. In my US tests, about 60% of products showed them. Every product and every variant has an isNewShopperDeal flag, so you can keep these prices apart from normal ones. All the rows above are flagged, so treat them as first-order prices.
2. Not every product is sold everywhere. In one test of 250 products shipped to Germany, 92 were not sold there. Those come back as free rows with the status unavailable_in_region. A product can also be on sale but not deliverable to your address; then canShip is false. The code above skips both.
3. Dead links. Removed products, wrong ids and products that no longer exist come back as rows with a status like not_found, no_longer_sold or invalid_link. These rows are free.
Other countries and currencies
Change three fields:
run_input = {
"productUrls": ["1005007857239353"],
"shipTo": "GB",
"currency": "GBP",
"language": "en",
}
Shipping fee, delivery days and the canShip flag then follow the UK. Prices come back in pounds.
Cost and speed
- $1 per 1,000 products. Rows for missing, removed, not-sold or failed products cost nothing.
- 1,000 products took about 5 minutes in my test.
Use it from an AI assistant
The tool also works through Apify's MCP server, so Claude, ChatGPT, Cursor or VS Code can run it for you. Add this server URL to your app: https://mcp.apify.com?tools=serenido/aliexpress-product-details
Links
- The tool: AliExpress Product Details Scraper on Apify
- Code examples (Python, JavaScript, curl): github.com/serenido/aliexpress-product-details-scraper
If you need a field that is missing, tell me in the comments.
Top comments (0)