Scrape Temu search results and product details as clean JSON — a small, zero-dependency Node.js client for the ScrapeUnblocker Temu plugin.
Temu is heavily protected and geo-personalized, so raw HTTP requests get blocked or return empty shells. This library sends your searches and product lookups through ScrapeUnblocker, which handles the unblocking and returns structured data.
Powered by ScrapeUnblocker. You'll need an API key — grab one from the dashboard.
- 🔎 Search — keyword search with sort (
relevance,best_selling,recent,price_asc,price_desc) and a result limit. - 📦 Product details — name, price + currency, rating, review count, availability, images, reviews and more for any product URL.
- ⚡ Search + hydrate — one call to search and fetch full product data for every result, with bounded concurrency and per-item error collection.
- 🧰 CLI + library — use it from the terminal or import it into your code.
- 🪶 Zero runtime dependencies — just the
fetchbuilt into Node 18+.
npm install temu-scraperOr clone and install from source:
git clone https://github.com/ScrapeUnblocker/temu-scraper.git
cd temu-scraper
npm installSet your API key (the client reads SCRAPEUNBLOCKER_KEY):
cp .env.example .env
# then edit .env, or just export it:
export SCRAPEUNBLOCKER_KEY="your_key_here"# Search (prints matching product URLs)
temu-scraper search "wireless earbuds" --limit 10 --sort best_selling
# Search and hydrate full product data, write a CSV
temu-scraper search "phone case" --hydrate --csv products.csv
# One product by URL
temu-scraper product "https://www.temu.com/lt-en/...-g-601101955527840.html"
# Raw JSON for any command
temu-scraper search "kids toys" --jsonimport { TemuClient } from "temu-scraper";
const temu = new TemuClient(); // reads SCRAPEUNBLOCKER_KEY
// 1. Search for product URLs
const { urls } = await temu.searchProducts({
keyword: "wireless earbuds",
limit: 10,
sort: "best_selling",
});
// 2. Fetch full details for one product
const product = await temu.getProduct(urls[0]);
console.log(product.name, product.price, product.currency);
// 3. Or do both at once, with bounded concurrency
const { products, errors } = await temu.searchAndHydrate({
keyword: "phone case",
limit: 15,
concurrency: 4,
});getProduct(url) returns a normalized record:
{
"url": "https://www.temu.com/lt-en/...-g-601101955527840.html",
"name": "Mini Clip-On Wireless Earbuds",
"price": 9.64,
"priceRaw": "9.64",
"currency": "EUR",
"availability": "https://schema.org/InStock",
"rating": 4.7,
"reviewCount": 6255,
"condition": "https://schema.org/NewCondition",
"images": ["https://img.kwcdn.com/product/fancy/..."],
"reviews": [{ "rating": 5, "date": "2026-06-27", "author": "****", "body": "****" }],
"video": null
}price is parsed to a number for convenience; priceRaw keeps the original
string and currency holds the ISO code.
| Method | Returns |
|---|---|
searchProducts({ keyword, limit?, sort? }) |
{ keyword, sort, resultsCollected, urls } |
getProduct(url) |
normalized product record |
searchAndHydrate({ keyword, limit?, sort?, concurrency? }) |
{ products, errors } |
new TemuClient(options) accepts apiKey, baseUrl, timeout (ms),
retries, and an injectable fetch. Transient 5xx/network errors are retried
with exponential backoff; 4xx responses fail fast with a TemuError carrying
the status.
temu-scraper/
├── src/
│ ├── client.js # TemuClient, retry + response shaping
│ └── index.js # public exports
├── bin/
│ └── cli.js # command-line interface
├── examples/ # runnable search / product / CSV scripts
├── test/ # offline unit tests (fetch mocked, no API credit spent)
└── .github/workflows/ci.yml
npm install
npm run lint
npm testTests mock the network, so they run offline and spend no API credit. The
examples/ scripts make real calls and require SCRAPEUNBLOCKER_KEY.
- Product: https://scrapeunblocker.com/?utm_source=github&utm_medium=integration&utm_campaign=example-repos
- Docs: https://docs.scrapeunblocker.com/?utm_source=github&utm_medium=integration&utm_campaign=example-repos
MIT © 2026 ScrapeUnblocker