# Temu Products Scraper (`badr_xba/temu-products-scraper`) Actor

Analyse Temu products from your own export or an Apify dataset: explainable Product Score, filters, price/review history and a visual report. Public temu.com pages are tried politely; when Temu requires sign-in they are reported and never charged.

- **URL**: https://apify.com/badr_xba/temu-products-scraper.md
- **Developed by:** [Badr Bouargan](https://apify.com/badr_xba) (community)
- **Categories:** E-commerce, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Temu Products Scraper Pro

![Temu Products Scraper: visual report of a real run, key figures and charts](https://api.apify.com/v2/key-value-stores/R1P4OnWyoV0LnGBin/records/temu-products-scraper-overview.png)

Turn Temu product data into a ranked shortlist. Every product gets an explainable **Product Score (0–100)**, a clean normalized record, price and review history across runs, and a visual report with image galleries, charts and JSON / CSV / Excel export.

> **Read this first: what works today**
>
> | Source | Status | What happens |
> |---|---|---|
> | **Import**: your own Temu export (JSON, JSON Lines, CSV URL) or an Apify dataset | ✅ Works | Full engine: normalization, dedupe, filters, sorting, Product Score, history, report. |
> | **Public temu.com product pages**: product URLs or ids | ⚠️ A few per run | Measured on Apify 2026-11-08: the first 7 of 13 product pages were read (title, price, rating, reviews, images, category path, stock), then Temu showed its human-verification page for every following request. |
> | **Public temu.com searches and category / listing pages** | ❌ Blocked | Temu always answers with its human-verification page. |
>
> Every blocked input gets **1 polite request**, is reported in `FAILED_INPUTS` and is **never charged**. The Actor never solves CAPTCHAs, never logs in, never rotates proxies to dodge blocks and never bypasses protections.
>
> If you already collect Temu data elsewhere (a Temu affiliate feed, your own export, another Actor's dataset), point this Actor at it and get the scoring, filtering and report on top.

### What you get

![Temu Products Scraper: result cards from a real run](https://api.apify.com/v2/key-value-stores/R1P4OnWyoV0LnGBin/records/temu-products-scraper-results.png)

*Screenshots from a real platform run.*

- **Normalized products**: id, title, description (plain text), URL, main image + all images, videos, price, original price, discount %, amount saved (`amountSaved`, only from a real original price), currency (ISO), rating, review count, category, seller and seller id, availability, variants (SKU, price, stock, options), colours, sizes, specifications, shipping text, listing date, collection time.
- **Missing data stays missing**: absent values are `null`, never guessed. Anything unusual is explained in `dataNotes`.
- **Product Score 0–100** with every signal, its points and its evidence listed on the row.
- **History between runs**: price drops, review growth and stock changes, kept in a named key-value store. `newSinceLastRun` tells which products no earlier run has seen (`null` on the first run), and **Only products new since the last run** (`onlyNewProducts`) keeps just those, e.g. for a daily schedule.
- **Duplicate detection**: the same product (same id or URL) from several inputs is saved once.
- **Visual report** (`REPORT.html` in the key-value store): product grid with up to 3 photos per product (main photo first) and fallbacks, search, filters, sorting, pagination, side-by-side comparison, price / discount / category charts, Product Score ranking, JSON / CSV / Excel export. While the run is in progress it shows live progress (inputs read / total, products so far; refreshed at most once a minute); failed or blocked inputs are grouped by error code at the top, and a data-quality panel shows how complete the key fields are.
- **Run summary** (`RUN_SUMMARY.json`): totals, filter counts, per-input status, request counts, timings, memory and CPU.

### Quick start

1. Export your Temu products (or take the dataset id of another run).
2. Set **Data source** to `import`, paste the file URL in **Import file URLs** or the id in **Apify dataset ids**.
3. Pick filters and a sort order, run, then open **REPORT.html** from the Storage tab.

```json
{
  "source": "import",
  "importUrls": ["https://example.com/my-temu-export.jsonl"],
  "minDiscountPercent": 40,
  "minRating": 4.5,
  "sortBy": "productScore",
  "maxItems": 200
}
```

#### Accepted import formats

- JSON array, or an object with `items`, `data` or `products`.
- JSON Lines (one product per line).
- CSV with a header row.

Common Temu field names are mapped automatically: `goods_id`, `goods_name`, `link_url`, `price` / `price_str` / `*_cents`, `market_price`, `currency`, `thumb_url`, `gallery` / `images`, `goods_score`, `comment_num`, `mall_name`, `mall_id`, `sku_list`, `in_stock`, and more. Unknown fields are kept under `extra`, so nothing is lost.

Prices: texts like `$12.99` and fields named `*_cents` are always understood. Set **Unit of numeric prices** to `cents` if your file stores `1299` for 12.99.

### Ready-made tasks

The `tasks/` folder holds 7 inputs you can save as Apify Tasks:

| Task | Source | What it does |
|---|---|---|
| Temu Products Search | public | Search one keyword (blocked by Temu today; reported, not charged) |
| Temu Product Details | public | One product page (blocked by Temu today; reported, not charged) |
| Temu Products by Category | import | Keep one category, ranked by Product Score |
| Temu Discount Finder | import | 40%+ discounts, biggest first |
| Temu Best-Rated Products | import | Rating ≥ 4.5 with 100+ reviews |
| Temu Product Comparison | import | One product type, cheapest first, top 10 |
| Temu Multi-Keyword Search | public | Several searches in one run (blocked by Temu today) |

### Filters and sorting

Filters: min / max price (in the product's own currency, no conversion), currency, minimum discount, minimum rating, minimum reviews, minimum variants, in stock only, only new since the last run, categories, sellers, keywords, excluded keywords, minimum Product Score.

Sorting: Product Score, price (low → high, high → low), discount, rating, reviews, newest listed, recently collected. Missing values always go last.

Filtered-out products are never saved and never charged. `RUN_SUMMARY.json` counts how many each filter removed.

### How the Product Score works

Six independent signal groups, each with a maximum:

| Group | Max | Signal |
|---|---:|---|
| Rating quality | 25 | 3.0★ = 0, 5.0★ = 25; ignored below 5 reviews |
| Review volume | 20 | Log scale, 10,000+ reviews = 20 |
| Discount | 15 | Only from a real original price above the price; 60%+ = 15 |
| Price vs category | 15 | Cheaper than X% of products in the same category and currency (needs 5+ peers in the run) |
| Availability & variants | 10 | In stock + variant depth |
| History | 15 | Price drop and review growth since the previous snapshot |

Safety caps: a product with points in only 1 group is capped at 49; 90+ needs at least 3 groups. Each row has `scoreSignals` (every signal, including zeros, with its evidence), `scoreLevel`, `scoreConfidence` and `scoreGroups`.

**The score never uses or estimates sales volume.** If your data contains a displayed sales tip, it is kept as `displayedSalesTip` but not scored.

### Output example

Values below are from the Actor's **synthetic** test data, not a real Temu product.

```json
{
  "productId": "900000000000001",
  "title": "Compact wireless earbud demo item 1 (synthetic)",
  "url": "https://www.temu.com/demo-synthetic-item-g-900000000000001.html",
  "imageUrl": "https://img.synthetic.example/demo/900000000000001-1.png",
  "images": ["…-1.png", "…-2.png", "…-3.png", "…-4.png"],
  "price": 3.85,
  "originalPrice": 9.2,
  "discountPercent": 58,
  "currency": "USD",
  "rating": 3.5,
  "reviewsCount": 1442,
  "category": "Wireless earbuds",
  "seller": "Synthetic Demo Shop 2",
  "available": true,
  "variantsCount": 2,
  "colors": ["Black", "White"],
  "productScore": 58,
  "scoreLevel": "Good",
  "scoreConfidence": "high",
  "scoreGroups": 5,
  "scoreSignals": [
    { "group": "rating", "points": 6, "evidence": "Rated 3.5/5 over 1442 reviews (3.0 = 0 points, 5.0 = 25).", "kind": "verified" },
    { "group": "reviews", "points": 16, "evidence": "1,442 reviews (log scale; 10,000+ = 20).", "kind": "verified" },
    { "group": "discount", "points": 15, "evidence": "58% below the original price 9.2 USD (60%+ = 15).", "kind": "inferred" },
    { "group": "pricePosition", "points": 15, "evidence": "Cheaper than 100% of 6 products in \"Wireless earbuds\" (same currency) in this run.", "kind": "inferred" },
    { "group": "availability", "points": 6, "evidence": "In stock; 2 variant(s), 2 colour(s), 0 size(s).", "kind": "verified" },
    { "group": "history", "points": 0, "evidence": "No earlier snapshot of this product yet (history builds up across runs).", "kind": "historical" }
  ],
  "history": null,
  "source": { "type": "import", "synthetic": true },
  "dataNotes": ["SYNTHETIC record (test fixture): not real Temu data", "1 image URL(s) invalid or duplicate: dropped"]
}
```

### Storage

| Key | Content |
|---|---|
| Dataset | One row per saved product |
| `REPORT.html` | Visual report (grid, galleries, charts, comparison, export) |
| `RUN_SUMMARY.json` | Totals, per-input status, filters, timings, memory |
| `FAILED_INPUTS.json` | Every input that could not be read, with a reason. Never charged |
| `temu-products-history` (named store) | Snapshots for price and review history |

### Performance

Measured locally on import runs (512 MB):

| Products | Run time |
|---:|---:|
| 100 | 0.8 s |
| 1,000 | 11.7 s |
| 5,000 | 22.9 s |

The report shows up to 5,000 products; the dataset always keeps every row.

### Pricing

Pay per saved product, plus a tiny $0.00005 start fee per run (one event per GB of memory). The price depends on your Apify plan:

| Apify plan | Price per 1,000 products |
|---|---:|
| Free | $2.00 |
| Starter | $1.80 |
| Scale | $1.60 |
| Business and above | $1.50 |

| Products saved | Cost (Free plan) |
|---:|---:|
| 100 | $0.20 |
| 500 | $1.00 |
| 1,000 | $2.00 |
| 5,000 | $10.00 |

Blocked inputs, failed inputs and filtered-out products are never charged. Set a maximum cost per run in the run options if you want a hard cap.

### FAQ

**Why do only some temu.com URLs return data?**
Temu serves product pages to a handful of automated requests, then asks for human verification; searches and listing pages are always verified. This Actor respects that: it sends one request per input, reports blocks and charges nothing for them. For large volumes, use the import source.

**Will you add a bypass?**
No. Bypassing sign-in, CAPTCHAs or anti-bot protections breaks Temu's terms and makes an Actor fragile. Official sources (for example a Temu affiliate product feed you are allowed to use) will be added as import sources.

**Is my data shared?**
No. Imported files are read with your run's own token and stored only in your Apify storage.

**What are the "synthetic" rows?**
Test records clearly labelled `"synthetic": true`. They are never stored in history and the report shows a demo banner when they are present.

### Feedback and reviews

If this Actor saved you time, a short review on its [Store page](https://apify.com/badr_xba/temu-products-scraper-pro/reviews) helps other users find it. If something did not work as expected, please open an issue on the [Issues tab](https://apify.com/badr_xba/temu-products-scraper-pro/issues) with your run ID: we read every issue.

# Changelog

This Actor's version history is a separate document: https://apify.com/badr_xba/temu-products-scraper/changelog.md

# Actor input Schema

## `source` (type: `string`):

<b>public</b>: temu.com product URLs, ids, searches and category pages (product pages: a few per run; searches and listings: blocked by Temu; blocked inputs are reported and not charged). <b>import</b>: your own file URLs or Apify datasets. Leave empty to choose automatically: <i>import</i> when import inputs are filled in, else <i>public</i>.

## `productUrls` (type: `array`):

temu.com product pages, any locale, e.g. <code>https://www.temu.com/...-g-601099645243854.html</code>.

## `productIds` (type: `array`):

6-20 digit goods ids; each becomes <code>https://www.temu.com/goods.html?goods_id=…</code>.

## `searchQueries` (type: `array`):

One search per line (temu.com search page).

## `categoryUrls` (type: `array`):

temu.com category or listing pages.

## `importUrls` (type: `array`):

http(s) URLs of your own Temu product data: a JSON array (or an object with <code>items</code>/<code>data</code>/<code>products</code>), JSON Lines or CSV with a header row. Common Temu field names are mapped (goods_id, goods_name, price / price_str / \*\_cents, market_price, currency, thumb_url / images, goods_score, comment_num, mall_name, sku_list…); unknown fields are kept under <code>extra</code>.

## `importDatasetIds` (type: `array`):

Datasets you can read with your account (e.g. from another Temu Actor run). Read with this run's own token.

## `importPriceUnit` (type: `string`):

Price texts like <code>$12.99</code> and fields named <code>\*\_cents</code> are always understood. <b>auto</b> reads other numbers as decimal amounts (and notes large integers in dataNotes); choose <b>cents</b> if your file stores 1299 for 12.99.

## `maxItems` (type: `integer`):

Saved (and charged) after filters and sorting. <code>0</code> = no limit.

## `sortBy` (type: `string`):

Order of the saved products. Missing values go last.

## `minProductScore` (type: `integer`):

0-100. 70+ needs at least 2 signal groups, 90+ at least 3.

## `minPrice` (type: `number`):

In the product's own currency (no conversion). Products without a price are excluded when a price filter is set.

## `maxPrice` (type: `number`):

In the product's own currency.

## `priceCurrency` (type: `string`):

3-letter ISO code (e.g. USD). Products in other currencies are excluded by the price filter.

## `minDiscountPercent` (type: `integer`):

Discount computed only from a real original price above the price.

## `minRating` (type: `number`):

0-5.

## `minReviews` (type: `integer`):

Products without a review count count as 0.

## `minVariants` (type: `integer`):

Number of SKU variants in the data.

## `onlyAvailable` (type: `boolean`):

Products with unknown availability are excluded too.

## `onlyNewProducts` (type: `boolean`):

Keep only products that no earlier run of the history store has seen (needs Track history between runs). The first run keeps everything, since there is nothing to compare with. Products dropped by this filter are not charged.

## `categories` (type: `array`):

Keep products whose category contains one of these texts (case-insensitive).

## `sellers` (type: `array`):

Exact seller (mall) names or ids.

## `keywords` (type: `array`):

Keep products whose title, description or category contains one of these.

## `excludeKeywords` (type: `array`):

Drop products containing one of these.

## `includeDescription` (type: `boolean`):

Plain-text description (HTML removed, max 2,000 characters).

## `useHistory` (type: `boolean`):

Keeps price / review / availability snapshots in a named key-value store, so scheduled runs show price drops and review growth. Records labelled synthetic are never stored.

## `historyStoreName` (type: `string`):

Named key-value store for the snapshots.

## `requestTimeoutSecs` (type: `integer`):

Per request. At most one retry per input.

## Actor input object example

```json
{
  "productUrls": [
    "https://www.temu.com/goods-detail-g-601099645243854.html"
  ],
  "importPriceUnit": "auto",
  "maxItems": 100,
  "sortBy": "productScore",
  "minProductScore": 0,
  "onlyAvailable": false,
  "onlyNewProducts": false,
  "includeDescription": true,
  "useHistory": true,
  "historyStoreName": "temu-products-history",
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

## `overview` (type: `string`):

Image, title, seller, category, price, discount, rating, reviews, score and link.

## `pricing` (type: `string`):

Price, original price, discount, stock, variants, shipping.

## `score` (type: `string`):

Score, level, confidence and every signal with its evidence.

## `history` (type: `string`):

Price drops, review growth and availability changes between runs.

## `allFields` (type: `string`):

Every field as JSON, CSV or Excel.

## `report` (type: `string`):

Product cards with up to 3 photos each, Product Score and "why this score", filters, sorts, compare (4), charts, data-quality panel, CSV/JSON export and run details. Updated live while the run is in progress.

## `runSummary` (type: `string`):

Inputs by status, requests, filters, distributions, performance, charged events.

## `failedInputs` (type: `string`):

Blocked, rate-limited, robots-disallowed or invalid inputs with the reason.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "productUrls": [
        "https://www.temu.com/goods-detail-g-601099645243854.html"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("badr_xba/temu-products-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "productUrls": ["https://www.temu.com/goods-detail-g-601099645243854.html"] }

# Run the Actor and wait for it to finish
run = client.actor("badr_xba/temu-products-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "productUrls": [
    "https://www.temu.com/goods-detail-g-601099645243854.html"
  ]
}' |
apify call badr_xba/temu-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,badr_xba/temu-products-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ZIQS6Iav7wxVBcM3t/builds/4xdU5KBCrDrA2uIpa/openapi.json
