# Lazada Scraper - Products, Prices, Sold Count & Reviews (`zkill3r/lazada-product-scraper`) Actor

Scrape Lazada product listings by keyword, category or product URL: price, discount, rating, review count, sold count, seller, location, images, category path and optional reviews. Malaysia, Singapore, Thailand, Philippines, Vietnam, Indonesia. Reliable: validated items, fails loudly.

- **URL**: https://apify.com/zkill3r/lazada-product-scraper.md
- **Developed by:** [Zee Lex](https://apify.com/zkill3r) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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

## Lazada Product Scraper

Scrape product data from **Lazada** in Malaysia, Singapore, Thailand, the Philippines, Vietnam and Indonesia. Search by keyword, crawl a category, or pull specific product pages. You get price, discount, rating, sold count, seller, images, category path and, optionally, customer reviews.

Built for **reliability**: every item is validated before it is saved, blocked requests are retried on fresh proxies, and the run **fails loudly** instead of quietly returning empty or half-finished data.

### What it does

- Searches Lazada by keyword, or crawls category and search-result URLs, with automatic pagination
- Opens each product page to add category path, stock status and the full image gallery (can be switched off for a cheaper, faster run)
- Optionally collects customer reviews (rating, text, date, reviewer, variant)
- Reads Lazada's own JSON data rather than fragile HTML, with HTML fallbacks
- Never saves an item without a URL, title and price
- Writes a run summary (`RUN_SUMMARY` in the key-value store) showing missing fields and anti-bot blocks

### Pricing

Pay only for results: **$3.50 per 1,000 products** (cheaper on higher Apify plans), with no extra charge for platform usage or proxies. Reviews are included in the product item at no extra cost. Example: 1,000 air fryer listings with full details ≈ $3.50.

### Use cases

- **Price monitoring**: track your products' or competitors' prices and discounts on a schedule
- **Competitor research**: see who sells what, at what price, with what ratings and sales volume
- **Product research for sellers and dropshippers**: find best-selling, highly rated products in a niche before you stock them
- **Review mining**: collect reviews to spot product weaknesses and customer complaints
- **Market reports**: compare categories and brands across six Southeast Asian markets

### Supported countries

| Code | Site | Currency |
|---|---|---|
| `my` | lazada.com.my | MYR |
| `sg` | lazada.sg | SGD |
| `th` | lazada.co.th | THB |
| `ph` | lazada.com.ph | PHP |
| `vn` | lazada.vn | VND |
| `id` | lazada.co.id | IDR |

Start URLs use the country of their own domain, so you can mix countries in one run.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `country` | string | `my` | Site used for keyword searches |
| `searchKeywords` | string\[] | – | Each keyword is searched separately |
| `startUrls` | URL list | – | Category, search-results or product URLs |
| `maxItems` | integer | `100` | Total products across all inputs |
| `includeReviews` | boolean | `false` | Fetch reviews for each product |
| `maxReviewsPerProduct` | integer | `20` | Used only with reviews on |
| `proxyConfiguration` | object | Apify Proxy, RESIDENTIAL | Residential strongly recommended |

At least one of `searchKeywords` or `startUrls` is required.

#### Input example

```json
{
    "country": "my",
    "searchKeywords": ["air fryer", "iphone 15"],
    "startUrls": [
        { "url": "https://www.lazada.com.my/shop-air-fryers/" },
        { "url": "https://www.lazada.com.my/products/example-product-i123456789-s987654321.html" }
    ],
    "maxItems": 200,
    "includeReviews": true,
    "maxReviewsPerProduct": 20,
    "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "MY" }
}
```

### Output sample

One dataset item per product:

```json
{
    "url": "https://www.lazada.com.my/products/philips-air-fryer-i123456789-s987654321.html",
    "itemId": "123456789",
    "skuId": "987654321",
    "title": "Philips Essential Air Fryer HD9200 4.1L",
    "brand": "Philips",
    "price": 289,
    "originalPrice": 459,
    "discountPercent": 37,
    "currency": "MYR",
    "rating": 4.9,
    "reviewCount": 1600,
    "soldCount": 5100,
    "inStock": true,
    "sellerName": "Philips Official Store",
    "sellerId": "1000123",
    "sellerRating": 97,
    "location": "Selangor",
    "images": ["https://img.lazcdn.com/g/p/1.jpg", "https://img.lazcdn.com/g/p/2.jpg"],
    "categoryPath": ["Home Appliances", "Air Fryers"],
    "reviews": [
        { "rating": 5, "text": "Works great", "date": "12 Oct 2026", "reviewerName": "A***n", "variant": "Colour:Black" }
    ],
    "dataSource": "product_page",
    "scrapedAt": "2026-10-27T07:12:45.123Z"
}
```

Field notes:

- `sellerRating` is Lazada's positive-rating percentage (0–100). `rating` is the product score (0–5).
- `soldCount` comes from search listings and is converted from labels like "5.1K sold" or "10rb terjual". It is `null` for products added by direct URL.
- `reviews` appears only when `includeReviews` is on.
- `dataSource` is `product_page` when full details were collected, or `search_listing` when the product page stayed blocked and only listing data was saved. Listing-only items still have URL, title and price, but usually lack seller rating, category path and full images.

### Reliability and run status

- Items missing URL, title or price are dropped and counted, never saved.
- Captcha or slider pages, HTTP 403/429, empty or malformed JSON are detected as blocks. The session and proxy are rotated and the request retried (up to 6 times).
- The run **fails** with a clear status message when:
  - no products were saved, or
  - 25 blocks happen in a row (the proxy pool is burned), and the target was not reached, or
  - at least 25% of requests failed permanently and the target was not reached.
- Products already saved before a failure stay in the dataset for inspection.
- `RUN_SUMMARY` in the default key-value store lists saved counts, missing-field percentages, block counts by reason and review fetch failures.

### Limitations

- Lazada uses strong anti-bot protection. **Datacenter proxies are usually blocked.** Use RESIDENTIAL proxies, ideally in the same country as the site (`apifyProxyCountry`).
- Lazada serves at most about 100 search pages per keyword (~4,000 products). Use several narrower keywords or category URLs for more.
- Lazada changes its page data format from time to time. Missing-field counts in `RUN_SUMMARY` are the early warning.
- Review dates are returned as Lazada displays them (locale-dependent text).
- Seller location is taken from search listings; it may be missing for products added by direct URL.
- Only publicly visible data is collected. You are responsible for using the data in line with Lazada's terms and applicable law, including personal-data laws for reviewer names.

### Run locally

```bash
npm install
npm test            # offline parser tests
apify run           # uses storage/key_value_stores/default/INPUT.json
```

#### Data-source notes (verified against live lazada.com.my, Oct 2026)

- **Search results** come from Lazada's `?ajax=true` listing JSON (`mods.listItems`). This endpoint is protected by Lazada's anti-bot system and is blocked for datacenter IPs, so use **RESIDENTIAL proxies in the target country**.
- **Product pages** are now server-rendered as a "shell": title, brand, category, images, stock and displayed price are present, but rating, seller name, seller rating and sold count are loaded later by the browser. The scraper fills rating and review count from Lazada's public review endpoint, and seller name, sold count and location from the search listing.
- **Cost tip:** set `includeProductDetails: false` to skip product pages (≈250 KB of residential traffic each). You still get price, discount, rating, review count, sold count, seller, location and image from the search listing; you lose category path and the full gallery.
- Search pages are loaded in a headless Chrome browser (plain HTTP requests to the listing JSON are blocked even on residential IPs); product pages and reviews use fast HTTP requests.
- **sellerRating** is not available from any source this Actor uses and is usually `null`.
- Product prices from the product page can differ by a few cents/ringgit from the search listing (the listing sometimes shows a voucher-adjusted price).

# Actor input Schema

## `country` (type: `string`):

Which Lazada site to search. Ignored for startUrls, which use their own domain.

## `searchKeywords` (type: `array`):

Keywords to search on Lazada, e.g. "air fryer". Each keyword is searched separately.

## `startUrls` (type: `array`):

Lazada category, search-results or product URLs. Provide these and/or search keywords.

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

Maximum number of products to save across all keywords and URLs.

## `includeProductDetails` (type: `boolean`):

Visit every product page to add category path, stock status and full image gallery. Off by default: for a faster, much cheaper run that uses search-listing data only (price, rating, sold count, seller, location are still included).

## `includeReviews` (type: `boolean`):

Also fetch customer reviews for each product. Slower and uses more requests.

## `maxReviewsPerProduct` (type: `integer`):

Only used when "Include reviews" is on.

## `proxyConfiguration` (type: `object`):

Lazada blocks datacenter IPs aggressively. RESIDENTIAL proxies in the same country as the Lazada site work best.

## Actor input object example

```json
{
  "country": "my",
  "searchKeywords": [
    "air fryer"
  ],
  "maxItems": 10,
  "includeProductDetails": false,
  "includeReviews": false,
  "maxReviewsPerProduct": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One item per Lazada product: price, discount, rating, review count, sold count, seller, images, category path and optional reviews.

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

JSON summary of the run: products saved, pages crawled, blocks and errors.

# 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 = {
    "searchKeywords": [
        "air fryer"
    ],
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zkill3r/lazada-product-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 = {
    "searchKeywords": ["air fryer"],
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("zkill3r/lazada-product-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 '{
  "searchKeywords": [
    "air fryer"
  ],
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call zkill3r/lazada-product-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zkill3r/lazada-product-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/Vq0w1Ykh5pPGII35c/builds/riYPtXhgCdcdo4gve/openapi.json
