How to Scrape Temu Product and Price Data in 2026: The API-vs-HTML Trade-Off
If you're building a price-intelligence pipeline, you've likely asked how to scrape Temu product and price data in 2026. The platform offers no public API for catalog access, so you're left choosing between two approaches: parsing volatile HTML product cards, or reverse-engineering the internal JSON endpoints that power Temu's listing grids and product detail pages. Each path has distinct trade-offs in reliability, maintenance cost, and anti-bot exposure.
The HTML route means you depend on hashed CSS class names that change with every frontend deploy. A selector like ._2rss6Z7 for the price element might work today and break tomorrow. The JSON route—hitting endpoints like /api/poppy/v1/search or the goods-detail API—returns structured data that's far more stable, but these endpoints require signed headers and tokens that Temu's frontend generates dynamically through obfuscated JavaScript.
There's also a third hybrid approach: extracting the embedded __NEXT_DATA__ state blob from the HTML response. This gives you structured JSON without needing to replicate the anti_content token, making it the most practical entry point for most scraping projects. We'll cover all three approaches below.
Temu's Anti-Bot Stack: What You're Up Against
Temu runs a multi-layered anti-bot defense that goes well beyond simple IP rate limiting. Understanding each layer is critical before you write a single line of scraping code. Let's break down the three primary defenses.
Cloudflare Turnstile and JS Challenges
Temu uses Cloudflare Turnstile alongside traditional JavaScript challenges to filter automated traffic. Turnstile evaluates browser fingerprints, TLS characteristics, and behavioral signals before issuing a clearance token. If your HTTP client doesn't execute JavaScript or presents a mismatched fingerprint, you'll receive a challenge page instead of product data. Unlike legacy CAPTCHAs, Turnstile operates invisibly—there's no puzzle to solve, which means you won't even know you've been blocked until you inspect the response HTML.
The Signed anti_content Token
Beyond Cloudflare, Temu's own API layer requires an anti_content header—a signed token generated client-side through heavily obfuscated JavaScript. This token encodes browser environment data, timestamps, mouse movement patterns, and request parameters. Without it, the JSON endpoints return empty results or HTTP 403 error codes. Replicating this token requires either executing the JS in a headless browser (like Playwright or Puppeteer) or reverse-engineering the signing algorithm, which Temu updates frequently—sometimes weekly.
This is why the embedded __NEXT_DATA__ approach is often more practical: you get structured JSON from a standard HTML page without needing to generate the anti_content token yourself. The trade-off is that you're limited to whatever data the server-side render includes.
TLS and HTTP/2 Fingerprinting
Even if you pass the JS challenge, Temu inspects the TLS handshake and HTTP/2 frame ordering to identify non-browser clients. Standard Python libraries like requests or httpx produce JA3/JA4 fingerprints that don't match real browsers, flagging your requests instantly. Datacenter IPs compound this problem—Temu's edge can distinguish ASN ranges associated with cloud providers (AWS, GCP, Azure, DigitalOcean) and applies stricter thresholds to them. A request from a datacenter IP with a non-browser TLS fingerprint is effectively a double flag.
This is where curl_cffi becomes essential. It wraps libcurl-impersonate, which reproduces the exact TLS handshake and HTTP/2 settings of specific Chrome, Firefox, or Safari versions. Combined with residential proxies, this combination addresses both the fingerprint and IP reputation layers simultaneously.
Locating the Data: HTML State Blobs and JSON Endpoints
Once you're past the anti-bot layer, you need to know where the product data actually lives. Temu's frontend is built on Next.js, which means the server renders an initial HTML payload with embedded state.
The Embedded __NEXT_DATA__ Blob
The most reliable data source in the HTML response is the __NEXT_DATA__ script tag. Temu's Next.js frontend hydrates from a serialized JSON blob embedded in a <script id="__NEXT_DATA__" type="application/json"> tag. This blob contains the full product, pricing, SKU, and shipping data before any client-side rendering. Because it's server-rendered, it's more stable than CSS selectors and doesn't require the anti_content token.
The structure typically nests as: props.pageProps.store or props.pageProps.initialState, depending on the page type. For product detail pages, you'll find goods_id, goods_name, min_normal_price, sku_list, and shipping metadata at this level.
Key JSON Endpoints
If you need search results rather than individual product pages, the listing grid is powered by internal API calls. The two most useful endpoints are:
/api/poppy/v1/search— returns search results with product IDs, titles, prices, ratings, and sales counts. Requiresanti_contentheader,search_key, and pagination parameters.- Goods detail API — returns full SKU lists, variant pricing, shipping costs, and stock status for a single product. Accessed via
goods_idparameter.
Both endpoints return JSON, but both require the anti_content token. If you can't generate it, stick to the __NEXT_DATA__ extraction approach.
Hashed CSS Classes and data-uniqid Attributes
If you must parse the rendered DOM, look for data-uniqid attributes on product cards—these are more stable than class names and often contain the goods_id directly. Price elements frequently use hashed classes like ._2rss6Z7 or attributes like [data-testid*="price"], but these change with each frontend deploy. Always prefer the embedded JSON blob over DOM parsing when possible.
Rate Limits and Why Residential Proxies with Geo-Targeting Are Mandatory
Temu applies aggressive rate limiting that varies by endpoint, IP reputation, and geographic region. Based on empirical testing, unauthenticated requests from a single residential IP are typically throttled at approximately 60–80 requests per minute before receiving HTTP 429 responses. Datacenter IPs hit this ceiling much faster—often after only 15–20 requests—because Temu's WAF assigns lower trust scores to cloud-provider ASN ranges.
More importantly, prices and shipping costs on Temu vary by region. A product listed at $4.98 in one US state may show different shipping fees, delivery estimates, or even availability in another. If your price-intelligence pipeline needs region-accurate data, you must route requests through proxies geo-located to the target market. A datacenter proxy in Virginia will return different pricing than a residential IP in California.
Residential vs Datacenter vs Mobile Proxies for Temu
| Proxy Type | Anti-Bot Evasion | Geo Accuracy | Relative Cost | Best For |
|---|---|---|---|---|
| Residential | High — real ISP IPs | Country + City | Medium | Price monitoring, SERP scraping, catalog crawling |
| Datacenter | Low — flagged quickly | Country only | Low | Testing, non-blocked endpoints, low-stakes tasks |
| Mobile | Highest — carrier-grade IPs | Country + City | High | High-value targets, strictest anti-bot scenarios |
For Temu specifically, residential proxies with city-level targeting (e.g., -country-US) are the practical sweet spot. They use real ISP IP ranges that pass Temu's ASN checks, and you can pin requests to specific cities to capture regional pricing differences. Check our proxy locations page for available geo-targeting options.
Worked Example: Fetching Temu Product Data with Python and ProxyHat
Here's a practical implementation using curl_cffi for TLS-fingerprint spoofing combined with ProxyHat residential proxies. curl_cffi impersonates real browser TLS handshakes, which is essential for bypassing Temu's JA3/JA4 fingerprint checks.
Step 1: Install Dependencies
pip install curl_cffi
Step 2: Fetch a Product Page Through ProxyHat
from curl_cffi import requests
import json
import re
# ProxyHat residential proxy with US geo-targeting
proxy_url = "http://user-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.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",
"Referer": "https://www.temu.com/",
}
# curl_cffi impersonates Chrome's TLS + HTTP/2 fingerprint
response = requests.get(
"https://www.temu.com/goods.html?goods_id=123456789",
headers=headers,
proxies={"http": proxy_url, "https": proxy_url},
impersonate="chrome120",
timeout=30,
)
print(f"Status: {response.status_code}, Size: {len(response.text)} bytes")
Step 3: Parse the Embedded JSON Blob
def extract_next_data(html: str) -> dict:
"""Extract the __NEXT_DATA__ JSON blob from Temu HTML."""
pattern = r'<script id="__NEXT_DATA__"[^>]*>(.*?)</script>'
match = re.search(pattern, html, re.DOTALL)
if not match:
raise ValueError("No __NEXT_DATA__ blob found — possible block page")
return json.loads(match.group(1))
data = extract_next_data(response.text)
store = data.get("props", {}).get("pageProps", {}).get("store", {})
# Truncated sample of the parsed structure:
# {
# "goods_id": "123456789",
# "goods_name": "Wireless Earbuds Bluetooth 5.3",
# "min_normal_price": {"amount": 4.98, "currency": "USD"},
# "sku_list": [
# {"sku_id": "sku_001", "spec": "Black", "price": 4.98},
# {"sku_id": "sku_002", "spec": "White", "price": 5.49}
# ],
# "shipping_info": {"free_threshold": 0, "estimated_days": "5-7"}
# }
goods_id = store.get("goods_id")
goods_name = store.get("goods_name")
price = store.get("min_normal_price", {}).get("amount")
sku_list = store.get("sku_list", [])
print(f"Product: {goods_name} (ID: {goods_id})")
print(f"Price: ${price} | SKUs: {len(sku_list)}")
Step 4: Hitting the JSON Search Endpoint Directly
# Direct API call — requires valid anti_content token
# Use this only if you can generate the token via headless browser
api_url = "https://www.temu.com/api/poppy/v1/search"
params = {
"search_key": "wireless earbuds",
"page": 1,
"page_size": 50,
"sort": "default",
}
api_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
"Anti-Content": "YOUR_GENERATED_TOKEN",
"Referer": "https://www.temu.com/search_result.html?search_key=wireless+earbuds",
"Accept": "application/json, text/plain, */*",
}
response = requests.get(
api_url,
params=params,
headers=api_headers,
proxies={"http": proxy_url, "https": proxy_url},
impersonate="chrome120",
timeout=30,
)
# Truncated sample response:
# {
# "data": {
# "items": [
# {"goods_id": "123456789", "title": "Wireless Earbuds...",
# "price": {"amount": 4.98, "currency": "USD"},
# "sales": 12500, "rating": 4.6}
# ],
# "total": 3500,
# "has_more": true
# },
# "success": true
# }
result = response.json()
items = result.get("data", {}).get("items", [])
print(f"Found {len(items)} products on page {params['page']}")
Sticky Sessions, Retries, and Pagination
For multi-step workflows—like fetching a product page, then checking cart shipping costs—IP consistency matters. If your IP rotates between requests, Temu may flag the session as suspicious or return inconsistent regional pricing. Use ProxyHat's sticky sessions to pin a series of requests to the same exit IP.
Sticky Session Configuration
import time
import random
session_id = "temu_batch_01"
session_proxy = f"http://user-session-{session_id}-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080"
product_ids = ["123456789", "987654321", "456789123"]
request_count = 0
for goods_id in product_ids:
response = requests.get(
f"https://www.temu.com/goods.html?goods_id={goods_id}",
headers=headers,
proxies={"http": session_proxy, "https": session_proxy},
impersonate="chrome120",
timeout=30,
)
request_count += 1
if response.status_code == 200:
data = extract_next_data(response.text)
process_product(data)
elif response.status_code == 429:
# Rotate to a new sticky session on rate limit
session_id = f"temu_batch_{request_count:04d}"
session_proxy = f"http://user-session-{session_id}-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080"
time.sleep(random.uniform(5, 10)) # Back off
else:
time.sleep(random.uniform(2, 5))
# Randomized delay between requests
time.sleep(random.uniform(2, 5))
Pagination Strategy
Temu paginates search results with a page parameter. Keep pagination depth reasonable—going beyond page 20–30 often yields diminishing returns and increases block risk significantly. A practical approach for large-scale collection:
- Limit to 10–15 pages per search query per sticky session
- Rotate to a new sticky session every 50–80 requests
- Add randomized delays of 2–5 seconds between requests to mimic human browsing patterns
- Retry failed requests up to 3 times with exponential backoff (2s, 4s, 8s)
- Distribute queries across multiple concurrent sessions to increase throughput
ProxyHat supports up to 100 concurrent sessions on standard plans—see our pricing page for details on session limits and bandwidth allowances.
Ethics, Legal Considerations, and TOS
Scraping e-commerce data exists in a legal gray area. Before you build a Temu price scraper, consider the following guidelines carefully.
Public Catalog Data Only
Stick to publicly accessible product listings, prices, and availability. Do not scrape user account data, personal reviews tied to identifiable users, or any content behind authentication. The Computer Fraud and Abuse Act (CFAA) in the US and the GDPR in the EU both create significant liability for accessing non-public data or scraping personal information. While public catalog data is generally less risky, you should consult legal counsel before deploying any scraping pipeline at scale.
Respect robots.txt and Rate Limits
Check Temu's robots.txt and respect its directives. Even if a path is technically disallowed, scraping it may violate Temu's Terms of Service. Keep your request rate reasonable—aggressive scraping that degrades service for other users is both unethical and more likely to trigger permanent IP bans. A good rule of thumb: if your scraping volume would be noticeable in Temu's traffic analytics, you're going too fast.
When to Use the Official Merchant Feed
If you're a Temu seller or authorized partner, the Temu merchant center provides product feed APIs that are the legitimate alternative to scraping. For price-intelligence use cases where you're monitoring competitors, scraping public catalog data may be the only option—but keep it scoped, respectful, and compliant with applicable laws.
For broader guidance on ethical scraping practices, see our web scraping use case guide and SERP tracking documentation. Full proxy configuration details are available in the ProxyHat documentation.
Key Takeaways
- Prefer the embedded JSON blob over HTML parsing. The
__NEXT_DATA__state blob gives you structured product data without needing theanti_contenttoken, and it's more stable than hashed CSS selectors.- Residential proxies with city-level geo are mandatory. Temu's anti-bot flags datacenter IPs within 15–20 requests, and regional pricing varies by location. Use
-country-USor finer geo-targeting for accurate data.- Use curl_cffi for TLS fingerprint matching. Standard HTTP libraries produce non-browser JA3/JA4 fingerprints that get blocked instantly.
curl_cffiwithimpersonate="chrome120"solves this.- Sticky sessions for multi-step workflows. Pin related requests to the same IP with
-session-flags to maintain cart and shipping consistency.- Stay ethical and legal. Scrape only public catalog data, respect rate limits, and use the official merchant feed API if you're an authorized partner.






