How to Scrape AliExpress & Alibaba (Python Guide)
- AliExpress search embeds full product JSON in the HTML, so the first few requests from a clean IP parse fine. After ~3 hits my IP got redirected to a 2,139-byte "punish" page (HTTP 200, no products). The DIY path works in short bursts, then throttles.
- Alibaba search blocked me on the first request: a 90,708-byte HTTP 200 that is a captcha/slider page (Alibaba's
baxia/_____tmd_____/punishstack), zero product links to parse. - Both robots.txt files disallow the search paths I hit: AliExpress blocks
/wholesale*and/search/*; Alibaba blocks/trade/. Alibaba does Allow/product-detail/and/showroom/. - For volume you need a real browser engine plus rotating residential IPs. A scraper API like ChocoData bundles both; I show the request shape but did not benchmark its success rate.
AliExpress and Alibaba are the two biggest catalogs of cross-border supplier data on the web: prices, MOQ, supplier ratings, sold counts, and SKU variants across millions of listings. Both run on Alibaba Group’s anti-bot stack, so a naive scraper has a short runway. I built a real Python scraper, ran it against live search pages in June 2026, and pasted the real HTTP behavior below, including the exact point where each site blocked me. Then I cover the route that holds up at volume. AliExpress and Alibaba are different enough that I treat them separately throughout.
Can you scrape AliExpress and Alibaba, and is it legal?
You can request the public pages with a script, and AliExpress even hands you a few good responses, but both sites block automated access fast and their terms restrict it. Separate the legal layer from the technical one, because they give different answers.
On US computer-access law, reading pages anyone can load without logging in sits on the safer side. In hiQ Labs v. LinkedIn, the Ninth Circuit held that scraping public data does not violate the Computer Fraud and Abuse Act, and the Supreme Court’s Van Buren decision narrowed the CFAA toward genuine hacking. Fetching a public listing with a bot is not, by itself, a federal computer crime in the US.
Contract and trespass theories are the real exposure here. Both sites are governed by Alibaba Group terms that prohibit automated collection without permission, and their robots.txt files (which I read live, see the next section) disallow the search paths. The classic eBay v. Bidder’s Edge trespass-to-chattels theory still informs how aggressive crawling of a marketplace is judged. Add data-protection law: supplier and seller pages can contain personal data (contact names, company officers), which pulls in the GDPR in Europe if you store it. Practical guardrails: stay on public pages, never script a logged-in account, throttle hard, skip personal data, and use the affiliate or dropship APIs if your use case fits. I am a tester, not a lawyer, so get advice before a commercial run. My is web scraping legal pillar covers the full picture.
What do the robots.txt files say?
Both files disallow the search URLs a scraper targets, and I fetched both live in June 2026. AliExpress allows only specific wholesale pages and blocks the rest of the search surface:
User-agent: *
Disallow: /items/*
Disallow: /bin/*
Disallow: /search/*
Allow: /wholesale.html$
Allow: /wholesale-page-*.html
Disallow: /wholesale*
Disallow: /productdetail/*
Disallow: /api/*
...
The Disallow: /wholesale* line covers the exact search URL I tested (/wholesale?SearchText=...), and /productdetail/* covers product pages. Only the bare /wholesale.html and paginated /wholesale-page-*.html are allowed.
Alibaba is structured differently. It disallows the /trade/ search path I hit, but explicitly allows product-detail and showroom pages:
User-agent: *
Allow: /product-detail/
Allow: /showroom/
...
Disallow: /trade/
Disallow: /product/
Disallow: /supplier/guide/
Disallow: /member/*/contactinfo.html
So on Alibaba, individual /product-detail/ URLs sit on the allowed side of robots.txt while the /trade/search results page does not. robots.txt is not law, but ignoring it is evidence of intent in a dispute and a breach of the terms that reference it. The technical reality below is harsher than the file anyway.
What data is available on AliExpress and Alibaba?
A search results page plus a product page together carry the commercially useful fields, and on AliExpress most of them are embedded in the HTML as JSON rather than rendered into visible tags. Here is what each platform exposes, assuming you can retrieve the page:
| Field | AliExpress | Alibaba | Where it lives |
|---|---|---|---|
| Product / title | Yes | Yes | AE: _init_data_ JSON; Alibaba: product-detail HTML |
| Price | Yes (localized) | Yes (price tiers) | AE: prices.salePrice; Alibaba: ladder by quantity |
| MOQ (min order qty) | No | Yes | Alibaba product-detail |
| Sold / orders count | Yes (trade) | Sometimes | AE search JSON |
| Rating / reviews | Yes | Yes | AE evaluation.starRating |
| Store / supplier name | Yes | Yes | AE store.storeName |
| Supplier years / verified | No | Yes (Gold Supplier, years) | Alibaba supplier card |
| SKU / variant options | Yes (product page) | Yes (product page) | Detail page JSON |
| Product ID | Yes | Yes | URL: /item/{id}.html, /product-detail/... |
| Image URLs | Yes | Yes | JSON / img tags |
The split matters for what you build. AliExpress is a consumer catalog: price, sold count, and rating per item. Alibaba is B2B: MOQ, price ladders by quantity, and supplier credibility signals (years active, Gold Supplier, verified) are the fields you actually want, and they live on /product-detail/ pages, not the blocked search results.
Method 1: scrape AliExpress with requests and BeautifulSoup (what really happened)
A requests GET with a browser User-Agent plus BeautifulSoup is the standard first attempt, and on AliExpress it works for a few requests, then throttles. The key trick is that AliExpress does not render products into HTML tags; it embeds the whole result set as a JavaScript object literal assigned to _init_data_, which you slice out and parse as JSON. Here is the exact code I ran:
import re
import json
import requests
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}
url = "https://www.aliexpress.com/wholesale?SearchText=wireless+earbuds"
resp = requests.get(url, headers=HEADERS, timeout=30)
print("STATUS:", resp.status_code)
print("BYTES :", len(resp.content))
html = resp.text
# AliExpress embeds: _init_data_ = { data: {<valid JSON>} }
# 'data' is an UNQUOTED JS key, so we slice the inner object and brace-match it.
m = re.search(r"_init_data_\s*=\s*\{\s*data:\s*", html)
def brace_match(s, start):
depth = 0
for i in range(start, len(s)):
if s[i] == "{":
depth += 1
elif s[i] == "}":
depth -= 1
if depth == 0:
return s[start:i + 1]
if not m or len(resp.content) < 50000:
print("BLOCKED: no product JSON in body (soft block / punish page)")
else:
data = json.loads(brace_match(html, m.end()))
def find_items(obj):
if isinstance(obj, dict):
il = obj.get("itemList")
if isinstance(il, dict) and "content" in il:
return il["content"]
for v in obj.values():
r = find_items(v)
if r is not None:
return r
if isinstance(obj, list):
for v in obj:
r = find_items(v)
if r is not None:
return r
items = find_items(data)
print("ITEMS :", len(items))
for it in items[:3]:
title = (it.get("title") or {}).get("displayTitle")
price = (it.get("prices") or {}).get("salePrice", {}).get("formattedPrice")
sold = (it.get("trade") or {}).get("tradeDesc")
print(f" {it.get('productId')} | {price} | {sold} | {title!r:.50}")
What I actually observed across repeated runs in June 2026:
# First runs from a fresh IP:
STATUS: 200
BYTES : 677599
ITEMS : 18 # 18 products parsed: productId, price, sold count, title
# After ~3 requests, the SAME code, same IP:
STATUS: 200
BYTES : 2139
BLOCKED: no product JSON in body (soft block / punish page)
The first few requests returned the full ~680 KB page, and the parser pulled 18 products with IDs, localized prices, sold counts, and titles out of the _init_data_ blob. Then my IP flipped: every later request returned a 2,139-byte HTTP 200 whose body is a script that redirects to /wholesale/_____tmd_____/punish?x5secdata=..., AliExpress’s anti-bot endpoint. No <title>, no products. I will not paste the transient product rows as a headline result, because I cannot reproduce them on demand once the IP is throttled, and presenting them as a stable harvest would overstate what a plain scraper delivers. The honest takeaway: the DIY path parses cleanly in short bursts from a clean residential IP and then dies on a soft block.
Two parsing notes that cost me time. First, there are two _init_data_ matches in the page; the first is a small placeholder, and the real one (the one containing itemList) sits right after window._dida_config_. Second, the assignment is { data: {...} } with data unquoted, so json.loads on the whole thing fails; you must slice the inner object after data:.
How AliExpress blocks you
AliExpress (and Alibaba) run Alibaba Group’s anti-bot stack, and the block is rate-and-fingerprint based, not a simple User-Agent check:
- Soft block via 200. Instead of a 403, you get a small 200 page that JS-redirects to
_____tmd_____/punish. Scripts that only checkstatus_codethink they succeeded. - Request-rate throttling per IP. A clean IP gets a handful of good responses, then flips to the punish page. My flip happened after about three requests.
- TLS and HTTP/2 fingerprinting. Python’s
requestshas a different JA3/JA4 and HTTP/2 frame order than real Chrome, which feeds the risk score. - IP reputation. Datacenter and cloud IPs (where scripts usually run) are scored high-risk from the first request, which is why Alibaba blocked me immediately (next section) while AliExpress gave my residential IP a short grace period.
Matching the User-Agent does nothing for any of these. To sustain volume with DIY tooling you need a real browser engine (Playwright) plus rotating residential IPs, which is most of what a scraper API bundles. See how to scrape without getting blocked before building that yourself.
Method 2: scrape Alibaba with requests (blocked on request one)
Alibaba’s /trade/search page blocked my scraper on the very first request, with no grace period. Same header set, same approach as Method 1, pointed at an Alibaba search URL:
import requests
from bs4 import BeautifulSoup
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}
url = "https://www.alibaba.com/trade/search?SearchText=led+lights"
resp = requests.get(url, headers=HEADERS, timeout=30)
print("STATUS:", resp.status_code)
print("BYTES :", len(resp.content))
soup = BeautifulSoup(resp.text, "html.parser")
title = soup.find("title")
print("TITLE :", title.get_text(strip=True) if title else None)
print("PRODUCT-DETAIL LINKS:", resp.text.count("/product-detail/"))
The real output:
STATUS: 200
BYTES : 90708
TITLE : None
PRODUCT-DETAIL LINKS: 0
A 90,708-byte HTTP 200 with no <title> and zero product-detail links. The body is a captcha/slider verification page: I confirmed the anti-bot markers baxia, x5secdata, slider, captcha, and _____tmd_____ in the HTML, the same Alibaba Group punish stack AliExpress used on me, just triggered on request one instead of request four. There is no embedded offerList JSON to parse, so unlike AliExpress there is nothing to extract. A plain requests scraper does not get a usable Alibaba search page.
If you target individual /product-detail/ URLs instead (the path Alibaba’s robots.txt allows), you will hit the same fingerprint and IP checks; being on an allowed path does not exempt you from the anti-bot layer. The retrieval problem is identical.
Method 3: scrape AliExpress and Alibaba with a scraper API
When you need volume or a market other than your own IP’s, a scraper API gets past both the soft-block throttle on AliExpress and the request-one captcha on Alibaba. It runs the request through a real browser engine and a rotating residential IP, so the site sees a browser-shaped connection from a residential address instead of a Python script on a flagged IP.
ChocoData is the service I reach for in this category. It exposes a universal endpoint (one URL for any site) plus 453 dedicated endpoints for common targets. You hand it the AliExpress or Alibaba URL and it returns the rendered page; for AliExpress you then parse the same _init_data_ JSON from Method 1. The request shape:
import requests
API_KEY = "cd_live_YOUR_KEY" # free tier: 1,000 requests/month, no card
target = "https://www.aliexpress.com/wholesale?SearchText=wireless+earbuds"
endpoint = "https://api.chocodata.com/api/v1/universal/get"
params = {
"api_key": API_KEY,
"url": target,
"render": "true", # real browser engine, so the punish redirect resolves
"country": "us", # US residential IP -> US market and USD pricing
}
resp = requests.get(endpoint, params=params, timeout=90)
print("STATUS:", resp.status_code)
# Rendered HTML comes back; reuse the _init_data_ slice + json.loads from Method 1.
# For Alibaba, point `target` at a /product-detail/ URL and parse MOQ / price tiers.
The difference from Methods 1 and 2 is what happens behind that one call. ChocoData routes through a country-matched residential IP that rotates and retries through a fresh IP on a block, and the render flag runs a browser engine so the _____tmd_____/punish redirect resolves instead of landing in your response. The country parameter also fixes the localization problem I hit, where prices came back in a non-USD currency because of where my request originated. Per ChocoData’s docs, billing applies only to successful responses, so the soft-block 200s that wasted my DIY attempts would not quietly drain a budget. I did not independently benchmark their success rate on either site, so treat the throughput claims as vendor-stated until you test on the free tier. The narrow, honest claim: the universal endpoint takes a URL and returns the rendered page, which removes the two specific failures I reproduced (the AliExpress throttle and the Alibaba request-one captcha).
Should you DIY or use a scraper API?
DIY is fine for a one-off AliExpress pull of a few queries; for Alibaba or for any sustained volume, a scraper API is the practical route. Here is the decision laid out against what I actually observed:
| Approach | AliExpress result (my test) | Alibaba result (my test) | Cost | Best for |
|---|---|---|---|---|
| DIY requests + BS4 | 18 items, then punish page after ~3 reqs | Captcha on request 1, 0 items | Free | A few AliExpress queries, learning |
| DIY + Playwright + residential proxies | Sustains longer, you maintain it | Can pass captcha, heavy to run | Proxy + infra cost | Mid volume, in-house control |
| Scraper API | One call returns rendered page | One call returns rendered page | Free tier, then per request | Volume, multi-market, Alibaba |
My honest read from the test: AliExpress search is genuinely parseable in short bursts because the data is embedded in the HTML, so for a small, slow pull you can DIY it. The moment you want consistent volume, a country other than your IP’s, or Alibaba at all (where I never got a single usable response), you need a real browser engine plus rotating residential IPs, and a scraper API packages that into one request.
To go deeper on the building blocks, see the Python web scraping guide for the language fundamentals, BeautifulSoup for parsing the embedded JSON, and Scrapy if you are crawling many product pages and want a framework. The web scraping pillar ties the whole workflow together.
FAQ
There are affiliate and dropshipping APIs, not an open catalog API. AliExpress runs an Open Platform (portals.aliexpress.com) whose Affiliate/Dropshipping APIs return product, price, and order data to approved partners, and Alibaba.com exposes APIs through its Open Platform for integrated suppliers and partners. Both gate access behind an application and an app key, so they are not a drop-in replacement for scraping arbitrary public search pages. If your use case fits the affiliate or dropship programs, that is the sanctioned route.
Both sites return 200 on a soft block. Instead of an error code they serve a small JavaScript page that redirects to a captcha or "punish" endpoint (the markers x5secdata, baxia, and _____tmd_____/punish in the body). Your script sees a 200 and a few KB of HTML with no product nodes. Always check the byte count and look for a
Prices are localized by IP and cookies, so a datacenter IP can return a region and currency you did not intend. In my test the formatted prices came back in a non-USD currency because of where the request originated. To pin a market you need an IP in that country (a residential proxy or a scraper API's country parameter) plus the matching currency cookie or URL parameter.