Tony Wang7 min readHow to Scrape Wish in 2026 (API & Python)
Scrape Wish in 2026 — product search and full detail with pricing, variations, and merchant data as structured JSON — DIY, no-code, or a structured API.
The fastest way to scrape Wish in 2026 is to call a structured API that returns normalized JSON for keyword product search and full per-product detail — instead of parsing Wish's own pages. Wish (operated by ContextLogic Inc.) is a different animal from a direct retailer like Costco or Target: it's a discount marketplace built on third-party merchants, ultra-low prices, and long shipping times from mostly overseas sellers, which means the same product can show up under multiple merchants at multiple prices. This guide covers DIY Python, no-code tools, and a structured Wish API, plus what you can actually collect and the legal basics.
Why scrape Wish?
- Merchant price comparison — Wish's marketplace model means the same product can be listed by more than one merchant at different prices; pulling search results and product detail lets you line those up side by side instead of trusting a single listing.
- Discount-pricing research — track how prices move on a marketplace built around aggressive, low-price positioning, across categories or keyword themes.
- Variation and inventory tracking — every purchasable size/color/merchant combination carries its own price, currency, and inventory, so you can watch a specific variation drift or sell out independently of the rest of the listing.
- Rating and review monitoring — pull a product's aggregate rating and review count for sentiment or quality-signal research over time.
- AI pipelines and agents — feed normalized search and detail JSON into a sourcing or price-comparison agent instead of screen-scraping product cards.
Is it legal to scrape Wish?
Option 1: DIY in Python (and why it breaks)
A naive scraper fetches a search or product page and tries to parse listing cards out of the rendered markup:
import requests
from bs4 import BeautifulSoup
resp = requests.get(
"https://www.wish.com/search/wireless%20earbuds",
headers={"User-Agent": "Mozilla/5.0 (compatible; research-bot/1.0)"},
)
soup = BeautifulSoup(resp.text, "html.parser")
# Product cards, pricing, and variations render from client-side app
# state rather than stable server HTML, so a plain HTML parse mostly
# returns an empty shell
It demos once, then breaks:
- Product ids aren't guessable. A Wish product id is a 24-character hex string — you only get a reliable one from a search result's
product_idfield or by extracting it from a product page URL, not from CSS selectors that shift with every redesign. - Client-rendered app state. Search results and product detail — including the per-variation price/currency/inventory/merchant breakdown — render from JavaScript state, not markup you can reliably select.
- Variations are nested, not flat. A single product can carry many purchasable size/color/merchant combinations, each with its own price and inventory — parsing that correctly out of rendered HTML means reverse-engineering the app's internal data shape.
- Marketplace anti-automation. Like most large marketplaces, Wish's front end pushes back on repeated, unauthenticated automated requests — a browser-based scraper needs ongoing upkeep as the defense evolves.
- No sanctioned self-serve API for outside developers. There's no public developer portal to request programmatic catalog access as a third party — which is exactly the gap a structured scraping API fills.
Option 2: No-code tools
Browser extensions and point-and-click scrapers can export a single search page or product snapshot, but they inherit the same client-rendering fragility as DIY and don't give you a stable, versioned schema — comparing merchant pricing on the same product across a schedule needs a real endpoint, not a one-off export.
Option 3: A structured Wish API
For a repeatable workflow, Crawlora's Wish API returns normalized JSON for keyword search and full product detail — credential-free on the Wish side, no page parsing to maintain. Search by keyword:
curl "https://api.crawlora.net/api/v1/wish/search?query=wireless+earbuds" \
-H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"total": 4200,
"offset": 0,
"count": 30,
"products": [
{
"product_id": "5f2e1a9c7b6d4f3e2a1b0c9d",
"title": "Wireless Bluetooth Earbuds with Charging Case",
"price": 6.99,
"currency": "USD",
"rating": 4.1,
"review_count": 2318,
"merchant_id": "5a1e0d2f7c3b9e4a1f0d2c8b",
"url": "https://www.wish.com/product/5f2e1a9c7b6d4f3e2a1b0c9d"
}
]
}
}
query is required. Pagination is real offset-based paging: offset must be an exact multiple of count (the default count is 30), and a nonzero offset costs one extra upstream request per page you walk — budget for that when paging deep into a result set. A query with no matches still returns a normal 200 with an empty products array, not an error.
Resolve a product_id from a search hit (or from a product page URL) and pull full detail, including every purchasable variation:
import requests
h = {"x-api-key": "YOUR_API_KEY"}
base = "https://api.crawlora.net/api/v1/wish"
hits = requests.get(f"{base}/search", headers=h, params={"query": "wireless earbuds"}).json()["data"]["products"]
product_id = hits[0]["product_id"]
detail = requests.get(f"{base}/product/{product_id}", headers=h).json()["data"]
A product-detail response carries description, sold-out state, aggregate rating, images, and every purchasable size/color/merchant variation — each with its own price, currency, inventory, and merchant (real fields — check the docs):
{
"code": 200,
"msg": "OK",
"data": {
"product_id": "5f2e1a9c7b6d4f3e2a1b0c9d",
"title": "Wireless Bluetooth Earbuds with Charging Case",
"description": "True wireless earbuds with touch controls and a portable charging case.",
"sold_out": false,
"rating": 4.1,
"review_count": 2318,
"images": [
"https://canary.contestimg.wish.com/api/webimage/5f2e1a9c-large.jpg"
],
"variations": [
{
"variation_id": "6a3f2b1c9d8e7f6a5b4c3d2e",
"color": "Black",
"price": 6.99,
"currency": "USD",
"inventory": 143,
"merchant": { "id": "5a1e0d2f7c3b9e4a1f0d2c8b", "name": "TechDeals Direct" }
},
{
"variation_id": "7b4a3c2d1e0f9a8b7c6d5e4f",
"color": "White",
"price": 8.49,
"currency": "USD",
"inventory": 0,
"merchant": { "id": "3c1d0e9f2a7b6c5d4e3f2a1b", "name": "GadgetHub Global" }
}
]
}
}
That's the merchant-comparison use case in one call: two variations of the same earbuds, from two different merchants, at two different prices and stock levels. Store one row per product_id + variation_id and re-pull on a schedule to track price, inventory, and sold_out drift.
What you can collect
- Search results:
product_id, title, price, currency, aggregate rating, review count, merchant id, and product URL, paginated with real offset/count pagination - Product detail: description, sold-out state, aggregate rating, review count, and images
- Variations: every purchasable size/color/merchant combination, each with its own
variation_id, price, currency, inventory, and merchant
This is public product-page data — search results and product detail already visible to any visitor — not account, order, checkout, or payment information.
Limitations and common challenges
- Pagination math has to be exact.
offsetmust be an exact multiple ofcount(default 30) — an arbitrary offset gives inconsistent paging, and every nonzero offset costs one extra upstream request per page walked, so budget credits when paging deep. - Empty results are still 200s. A query with no matches returns a normal
200with an emptyproductsarray — handle that as "no results," not a failure to retry. - Product ids only come from two places. A 24-character hex
product_idfrom a search result'sproduct_idfield, or extracted from a product page URL — there's no other reliable way to address a product. - Marketplace churn. Because Wish is a multi-merchant marketplace, price, inventory, and
sold_outstate on a given variation can change quickly and independently of the rest of the listing — treat any pull as a snapshot. - Public data only. This is what a product and search page already show publicly — not a way to reach account, order, or payment data, and not a way around Wish's own Access Terms and Conditions.
Where this gets used
- Merchant price comparison — line up every merchant's variation of the same product to see who's actually cheapest and in stock.
- Discount-pricing and catalog research — track pricing and assortment trends by keyword or category on a marketplace built around low prices.
- Variation and inventory monitoring — watch specific size/color/merchant combinations for price drift or sell-outs over time.
Sources
Start collecting
Try it first, free: run any public URL through the Free Web Scraper, or check whether a site blocks bots with the Anti-Bot Checker — no signup.
Test the search and product-detail endpoints in the Playground, check the request and response schema in the API docs, and review credit costs on the pricing page. Wish sits alongside other third-party marketplaces we cover — see how to scrape Poshmark and how to scrape Mercari for the closet/social-selling side, or how to scrape Vinted for another peer-to-peer marketplace. See also how to choose a web scraping API and is web scraping legal.
Part of our how-to-scrape guide series — every platform we cover, in one index.
Frequently asked questions
Does Wish search support pagination?
Yes — /wish/search uses real offset-based pagination. offset must be an exact multiple of the count used on the first page (default count is 30), and a nonzero offset costs one extra upstream request per page walked. A query with no matches returns a normal 200 with an empty products array.
Can I get every variation of a Wish product with an API?
Yes — /wish/product/{id} returns every purchasable size/color/merchant variation, each with its own price, currency, inventory, and merchant — a single-SKU product still returns a one-element list.
Does the Wish API require an account or API key?
No Wish account or API key is required from the caller — only your Crawlora API key.