# Lelong.my Scraper | Malaysia Online Marketplace (`haketa/lelong-my-scraper`) Actor

Lelong.my scraper & API (Malaysia marketplace): search products by keyword and category and export title, price, description, seller, ratings, stock, photos and URL. Malaysia e-commerce and product market data plus seller lead generation — fast, no login.

- **URL**: https://apify.com/haketa/lelong-my-scraper.md
- **Developed by:** [Haketa](https://apify.com/haketa) (community)
- **Categories:** Automation, E-commerce, Developer tools
- **Features:** Limited permissions
- **Stats:** 7 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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

## Lelong.my Scraper — Malaysia Online Marketplace Product, Price & Seller Data Extractor

> **Extract structured product listings, prices in Malaysian Ringgit (MYR), shop names, availability, condition, images, and seller storefronts from Lelong.my at scale.** Run by category, keyword, or direct URL — with automatic multi-page pagination and 1,000+ record capacity.

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-blue)](https://apify.com/haketa/lelong-my-scraper)
[![Live Data](https://img.shields.io/badge/Data-Live%20at%20Run%20Time-orange)]()
[![Engine HTTP](https://img.shields.io/badge/Engine-Pure%20HTTP-green)]()
[![Scale](https://img.shields.io/badge/Scale-1%2C000%2B%20Listings-blueviolet)]()
[![Country](https://img.shields.io/badge/Country-Malaysia-red)]()
[![Coverage](https://img.shields.io/badge/Categories-Electronics%20%C2%B7%20Auto%20%C2%B7%20Fashion%20%C2%B7%20Home-purple)]()
[![Proxy Optional](https://img.shields.io/badge/Proxy-Optional-lightgrey)]()

***

### ⚡ At a glance

| Feature | Details |
|---|---|
| **Target Marketplace** | [Lelong.my](https://www.lelong.com.my/) (Malaysia Online Marketplace) |
| **Search Modes** | 11 built-in categories, custom keywords, or direct product/category/shop URLs |
| **Pagination & Scale** | Automatic multi-page catalog pagination (`1,000+` listings per run) + category expansion |
| **Key Fields** | `listingId`, `title`, `price` (MYR), `shopName`, `shopUrl`, `availability`, `condition`, `category`, `brand`, `images`, `listingType` |
| **Deduplication** | Automatic client-side deduplication by `listingUrl` (`0 duplicates`) |
| **Export Formats** | JSON, CSV, Excel (XLSX), HTML, XML, RSS, REST API |

***

### What This Actor Does

The **Lelong.my Scraper** is a production-grade Apify Actor that extracts structured product listings from **[Lelong.my](https://www.lelong.com.my/)** — Malaysia's longest-running online marketplace (founded 1998), still actively used by Malaysian sellers and resellers across electronics, automotive parts, fashion, home goods, antiques, collectibles, and consumer staples.

The actor accepts three input modes — **no filters are required**, and an empty run returns a built-in mixed sample of popular products:

1. **Category mode (easiest)** — tick one or more of 11 ready-made categories (`smartphone`, `laptop`, `electronics`, `home appliances`, `beauty`, `toys`, `car accessories`, `fashion`, `watch`, `sports`, `groceries`); each becomes a catalog search. No typing needed.
2. **Keyword mode** — supply free-text search terms (`"toyota vios"`, `"samsung"`, `"air fryer"`, `"gaming chair"`); the actor queries Lelong's live catalog search (`/catalog/all/list?TheKeyword=…`), reads the server-rendered product cards (title, price, image, listing ID), and enriches every hit by fetching the individual product page. Categories and keywords are merged.
3. **URL mode (advanced)** — paste **any Lelong.my URL** (search page, category browse, individual `*-Sale-I.htm` item, `*-Sale-P.htm` promoted item, or shop page); the actor detects the page type and parses accordingly. Start URLs override categories and keywords.

Each output record contains the data Malaysian e-commerce, dropshipping, and reseller teams actually need:

- **Title** — full product name as listed on Lelong
- **Price (RM)** — numeric value in Malaysian Ringgit (`MYR`)
- **Shop name and shop URL** — the seller's storefront (e.g., `STAR_ICON`, `AUTOBAGS`, `mobileshop2u`)
- **Availability** — `InStock`, `OutOfStock`, `PreOrder` (Schema.org enum normalized)
- **Condition** — `NewCondition`, `UsedCondition`, `RefurbishedCondition`
- **Category** — auto-derived from URL slug or breadcrumb (`Automotive > Parts`, `Mobile Phones`, etc.)
- **Brand** — when declared in JSON-LD `Product.brand`
- **Listing type** — `Item` (regular) or `Promoted` (paid placement, `-Sale-P.htm`)
- **Images** — full image gallery, sanitised of bank logos, app-store badges, and merchant banners
- **View count and sold count** — when surfaced in the page DOM
- **Posted year/month** — extracted from the canonical URL pattern (`YYYY-MM-Sale-X.htm`)
- **Listing ID** — numeric or `X`-prefixed identifier parsed from the URL

This makes the actor the **fastest path to a clean, structured Lelong.my dataset** — for competitive analysis, dropshipping product research, reseller arbitrage, brand monitoring, or Malaysian consumer trend tracking.

#### Why scrape Lelong.my yourself when this exists?

Lelong is technically *easier* to scrape than most modern marketplaces — no Cloudflare, no aggressive WAF, no JS challenge — but the long tail of edge cases will still consume a week of engineering time:

- Lelong's search moved from the old `/kx/{keyword}.htm` route (now an empty SEO shell) to `/catalog/all/list?TheKeyword=…`; each search returns a server-rendered grid of matching products, so broad coverage requires multi-keyword / multi-category orchestration
- Listing cards are split between three rendering strategies: JSON-LD `ItemList`, `<b data-id data-link data-name>` attributes, and raw `<a href*="-Sale-I.htm">` anchors — you need all three as fallbacks
- JSON-LD payloads occasionally contain trailing commas that break stock `JSON.parse` — a permissive parser is mandatory
- Detail-page image galleries are polluted with bank logos (Maybank, CIMB, Public Bank), App Store / Google Play badges, and "BPP Crest" trust seals — a whitelist on `c.76.my/UserImages/Items/` is required
- Listing IDs and posting dates are embedded in the URL pattern `-{ID}-{YYYY-MM}-Sale-{P|I}.htm` — you need a regex to recover them
- Promoted listings (`-Sale-P.htm`) and regular listings (`-Sale-I.htm`) share the same page template but differ in placement and pricing — distinguishing them matters for arbitrage analysis
- Availability and condition come back as full Schema.org URIs (`https://schema.org/InStock`); they must be stripped to plain English for CSV exports
- Shop names occasionally appear only in DOM (`a.catalogShop-name`) rather than the JSON-LD `seller` block — dual extraction needed
- Lelong serves a mix of English and Malay copy; encoding and whitespace normalization are non-trivial

This actor solves all of the above: it downloads, parses, normalises, deduplicates, and outputs JSON — no parsing scripts, no broken selectors, no image-junk to filter downstream.

***

### Quick Start

#### One-Click Run

1. Open the **[Lelong.my Scraper page on Apify](https://apify.com/haketa/lelong-my-scraper)** and click **Try for free**
2. Either keep the default keyword (`toyota vios`) or paste your own keywords / start URLs
3. (Optional) tick **Scrape Detail Pages** for richer per-item data
4. Hit **Start** — your dataset is live in 30–90 seconds for a small keyword set

#### API Run (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("haketa/lelong-my-scraper").call(run_input={
    "keywords": [
        "toyota vios spare parts",
        "perodua myvi accessories",
        "iphone 15 pro max",
        "samsung galaxy a55",
        "air fryer",
    ],
    "scrapeDetails": True,
    "maxRecords": 100,
    "requestDelay": 1000,
    "maxConcurrency": 4,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(
        f"RM {item['price']:>8} | {item['shopName'] or '-':<20} | "
        f"{item['listingType'] or '-':<8} | {item['title']}"
    )
```

#### API Run (Node.js / TypeScript)

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('haketa/lelong-my-scraper').call({
    keywords: ['xiaomi redmi note 13', 'logitech gaming mouse', 'durian'],
    scrapeDetails: true,
    maxRecords: 60,
    maxConcurrency: 6,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Got ${items.length} Lelong.my listings`);

for (const it of items) {
    console.log(`${it.listingId ?? '?'}  RM ${it.price}  ${it.title}`);
}
```

#### API Run (cURL)

```bash
curl -X POST "https://api.apify.com/v2/acts/haketa~lelong-my-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "keywords": ["perodua axia", "proton saga"],
    "scrapeDetails": true,
    "maxRecords": 40
  }'
```

#### Pasting a single item URL

```json
{
  "startUrls": [
    {"url": "https://www.lelong.com.my/original-toyota-prius-camry-vios-altis-hilux-door-lamp-switch-star-icon-X526784-2007-01-Sale-I.htm"}
  ],
  "scrapeDetails": true
}
```

The actor auto-detects the URL pattern (`-Sale-I.htm` for an item, `-Sale-P.htm` for a promoted item) and skips search-page parsing.

***

### How It Works

Lelong.my renders its product grid and detail pages as structured HTML + Schema.org metadata. Each catalog search page yields up to ~60 product cards, and the actor automatically walks multi-page results (`&D=2, 3, ...`) and expands selected categories into high-yield search terms to deliver 1,000+ unique listings per run.

#### Architecture at a glance

| Layer | Capability | Purpose |
|---|---|---|
| Transport | Pure HTTP client | Fast, low-memory requests without browser overhead |
| Grid parser | Dual structured + card parser | Extracts all ~60 products per catalog page with price, title, image, and ID |
| Detail enrichment | Product schema & DOM parser | Enriches brand, category, condition, availability, seller shop, and gallery images |
| Concurrency | Async worker pool | 1–20 parallel requests (default 6) |
| Deduplication | URL-keyed deduplicator | Guarantees 0 duplicate products across multi-page and multi-keyword sweeps |

**Lightweight pure-HTTP execution.** Memory: 256–512 MB is sufficient for standard runs; 1024 MB for large multi-category sweeps.

#### Source endpoints

| Page type | URL pattern | Records returned |
|---|---|---|
| Keyword / category search | `/catalog/all/list?TheKeyword={keyword}` | A server-rendered grid of matching products (varies by term; typically a few to ~60 per page) |
| Item detail (regular) | `/{slug}-{shop}-{id}-{yyyy-mm}-Sale-I.htm` | 1 enriched product |
| Promoted item | `/{slug}-{shop}-{id}-{yyyy-mm}-Sale-P.htm` | 1 enriched product |
| Category browse | `/{category-slug}/...` | Varies |
| Shop page | `/{shop-slug}/...` | Varies |

> **Note:** The legacy `/kx/{keyword}.htm` route is now only an SEO landing shell with no inline listings. If you paste an old `/kx/` search URL in **Start URLs**, the actor transparently rewrites it to the live `/catalog/all/list` search — so historical inputs keep working.

#### Extraction strategy (in priority order)

1. **Catalog product cards** — primary source on search pages. Reads each card's `data-id`, `data-link`, `data-name`, `data-price` attributes plus the thumbnail `src`, giving a rich stub (title, price, image, listing ID) even with detail enrichment turned off.
2. **JSON-LD `ItemList`** — used when a page still embeds structured list data.
3. **`<a href*="-Sale-[IP].htm">` regex** — last-resort link scrape with `title`/text mining.
4. **Detail-page enrichment** — for each stub, fetch the product URL and parse the JSON-LD `Product` block (`name`, `description`, `brand`, `category`, `offers.price`, `offers.priceCurrency`, `offers.availability`, `offers.itemCondition`, `offers.seller`), plus DOM fallbacks for `shopName`, `shopUrl`, the full image gallery, and `Sold:` / `Views:` counters.

#### Resilience

- **3-attempt retry** with exponential backoff per fetch (2 s × attempt + random jitter), each attempt using freshly generated Chrome 120+ headers
- **Shell-page re-fetch** — Lelong occasionally returns a valid `200` response of the same search URL *without* the product grid (header/footer chrome only). Because that shell is a real 200 with a >1 KB body, a naïve scraper accepts it and records "0 results". This actor detects a grid-less 200, **re-fetches the same URL up to 3×** with new headers (the grid reliably reappears), and only then treats the term as empty — so a cluster of transient shells never truncates a large multi-keyword sweep
- **Term-level skip, not run-level abort** — a keyword that stays empty after re-fetch is simply skipped; the sweep continues to the remaining keywords/categories. Only consecutive *hard* fetch failures (non-200 / timeouts, i.e. a real Lelong outage) trip the run-abort guard
- **Status message on zero records** — if a whole run returns nothing, the actor sets a clear status message telling you to broaden keywords / check Start URLs
- **Permissive JSON-LD parsing** — falls back to a `,]` → `]` normalization for Lelong's occasional trailing-comma bug
- **Image whitelist** — filters out `BankLogo`, `app-store-badge`, `google-play-badge`, `bpp_crest`, merchant banners, and SVG icons; keeps only `UserImages/Items/` and `Malaysia/` paths

#### Why no proxy?

Lelong has **no Cloudflare, no DataDome, no PerimeterX**, and **no WAF challenge**. Direct HTTP requests with realistic Chrome 120+ headers and `en-MY` locale succeed reliably. The proxy field is exposed in the input schema for users running very large URL lists who want IP rotation — but for the typical workload it is **off by default and never needed**.

***

### Input Parameters

```json
{
  "categories": ["smartphone"],
  "keywords": ["toyota vios", "perodua myvi"],
  "startUrls": [],
  "scrapeDetails": true,
  "maxRecords": 50,
  "requestDelay": 1000,
  "maxConcurrency": 4,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

**No filters required.** The easiest way to start is the **category picker** — just tick one or more categories and run. Leave everything empty and the actor falls back to a built-in diverse sample of popular products (automatically capped so a no-filter run stays fast and cheap).

#### Parameter reference

| Parameter | Type | Default | Description |
|---|---|---|---|
| `categories` | `array<string>` (enum) | `[]` | Pick one or more product categories — no typing needed. Allowed values: `smartphone`, `laptop`, `electronics`, `home appliances`, `beauty`, `toys`, `car accessories`, `fashion`, `watch`, `sports`, `groceries`. Each becomes a catalog search. Leave empty for a mixed popular sample. |
| `keywords` | `array<string>` | `[]` (prefill `["toyota vios"]`) | Free-text search terms. Each runs a `/catalog/all/list?TheKeyword=…` search. Use multiple keywords for breadth. Merged with any selected `categories`. |
| `startUrls` | `array<{url: string}>` | `[]` | Advanced: any Lelong.my URL — keyword search, category, item detail, promoted item, or shop page. When provided, **overrides** `categories` and `keywords`. Useful for narrow targeting or replaying historical URLs. |
| `scrapeDetails` | `boolean` | `true` | Visit each item URL for richer data (description, condition, full image gallery, shop info, view count). Slower but much more complete. Adds ~1–3 seconds per item. |
| `maxRecords` | `integer` | `0` | Cap total output across all categories / keywords / URLs. `0` = unlimited (bounded only by source pages). A no-filter default-sample run is automatically capped at 100 to stay fast and cheap. |
| `requestDelay` | `integer` (ms) | `1000` | Delay between requests. Range 200–10 000. Lelong has no anti-bot WAF, but 800–2000 ms is polite. |
| `maxConcurrency` | `integer` | `4` | Parallel HTTP requests during detail enrichment. Range 1–20. 4–8 is safe; higher may trigger rate limits. |
| `proxyConfiguration` | `object` | `{useApifyProxy: false}` | Optional Apify Proxy. **Not required.** Use `apifyProxyCountry: "MY"` for Malaysian residential IPs only if needed. |

***

### Output Schema

Every record is a flat JSON object — easy to load straight into Postgres, BigQuery, Snowflake, Google Sheets, or a pandas DataFrame without per-field branching.

> **Coverage note (from the latest verification run):** on a broad multi-category sweep, every **core and detail field below populated at ~100%** when `scrapeDetails` is on — `title`, `listingUrl`, `price`, `currency`, `listingId`, `image`, `images`, `brand`, `category`, `condition`, `availability`, `shopName`, `listingType`, `description`, `viewCount`, `postedYearMonth`, `sourceUrl`, `scrapedAt`. A few fields are **present only when the specific listing/page exposes them** (`model`, `shopUrl`, `shopLocation`, `soldCount`) — they are emitted when available and omitted otherwise, which is normal for marketplace data.

#### Core fields (populate on virtually every record)

| Field | Type | Description |
|---|---|---|
| `listingId` | `string` | `null` | Lelong internal ID parsed from the URL pattern. Formats seen: plain numeric (`221826664`), `X`-prefixed (`X519193`), and letter-wrapped (`K5734421B`) |
| `title` | `string` | Product title exactly as listed |
| `description` | `string` | `null` | Description from JSON-LD `Product.description` (detail-page enrichment) |
| `price` | `number` | `null` | Numeric price in RM (Malaysian Ringgit) |
| `currency` | `string` | Always `"MYR"` (Lelong's `RM` is normalized to the ISO code) |
| `availability` | `string` | `null` | From `offers.availability`, normalized to plain English (e.g., `InStock`) |
| `condition` | `string` | `null` | From `offers.itemCondition`, normalized (e.g., `New`, `Used`, `Refurbished`) |
| `category` | `string` | `null` | From `Product.category` / breadcrumb (e.g., `Watches & Clocks`, `Phone & Tablet`, `Sports & Outdoors`) |
| `brand` | `string` | `null` | From JSON-LD `Product.brand` (e.g., `CASIO`, `SEIKO`, `SRAM`; `OEM` for unbranded) |
| `listingType` | `string` | `null` | `Item` (organic, `-Sale-I.htm`) or `Promoted` (paid placement, `-Sale-P.htm`) |

#### Shop fields

| Field | Type | Description |
|---|---|---|
| `shopName` | `string` | `null` | Seller display name (e.g., `DAVIS`, `comtalk`, `ecobuildhardware`, `AufaSufy`) |
| `shopUrl` | `string` | `null` | Seller storefront URL — **present when a store link is exposed on the page** |
| `shopLocation` | `string` | `null` | Seller city/state — **present when the page surfaces it** (not in Lelong's JSON-LD by default) |

#### Media fields

| Field | Type | Description |
|---|---|---|
| `image` | `string` | `null` | Primary product image (absolute `c.76.my` CDN URL) |
| `images` | `array<string>` | `null` | Full sanitised image gallery — bank logos, app-store badges and merchant banners removed |

#### Engagement & metadata

| Field | Type | Description |
|---|---|---|
| `viewCount` | `number` | `null` | Page-view count parsed from the detail page |
| `soldCount` | `number` | `null` | Units-sold counter — **present when the listing displays one** |
| `model` | `string` | `null` | Model code — **present when declared in the schema** |
| `postedYearMonth` | `string` | `null` | `YYYY-MM` derived from the canonical URL (e.g., `2007-01`, `2027-04`) |
| `listingUrl` | `string` | Canonical product URL (natural primary key) |
| `sourceUrl` | `string` | The search/category/start URL that surfaced this record |
| `scrapedAt` | `string` (ISO-8601) | Run timestamp |

#### Example record — branded watch (organic `Item`)

Real record from a fresh verification run (abbreviated description for readability):

```json
{
  "listingId": "K5734421B",
  "title": "CASIO EQB-501D EQB-501DB EQB-501DC EDIFICE smartphone bluetooth solar",
  "description": "Buy CASIO EQB-501D EQB-501DB EQB-501DC EDIFICE smartphone bluetooth solar. Lots of discount and promotional sales.",
  "price": 1445,
  "currency": "MYR",
  "availability": "InStock",
  "condition": "Used",
  "category": "Watches & Clocks",
  "brand": "CASIO",
  "shopName": "DAVIS",
  "listingType": "Item",
  "image": "https://c.76.my/UserImages/Items/TB220/196/067/196067375.jpg",
  "images": [
    "https://c.76.my/Malaysia/casio-eqb-501d-eqb-501db-eqb-501dc-edifice-smartphone-bluetooth-solar-davis-1709-07-DAVIS@1.jpg"
  ],
  "viewCount": 3679,
  "postedYearMonth": "2007-01",
  "listingUrl": "https://www.lelong.com.my/casio-eqb-501d-eqb-501db-eqb-501dc-edifice-smartphone-bluetooth-solar-davis-K5734421B-2007-01-Sale-I.htm",
  "sourceUrl": "https://www.lelong.com.my/catalog/all/list?TheKeyword=smartphone&CategoryID=0",
  "scrapedAt": "2026-11-02T00:10:35.130Z"
}
```

#### Example record — `Promoted` listing with a full image gallery

```json
{
  "listingId": "222342403",
  "title": "SRAM AXS POD ULTIMATE 2 BUTTON ELECTRONIC CONTROLLER",
  "description": "Buy SRAM AXS POD ULTIMATE 2 BUTTON ELECTRONIC CONTROLLER. Lots of discount and promotional sales.",
  "price": 1073,
  "currency": "MYR",
  "availability": "InStock",
  "condition": "Used",
  "category": "Sports & Outdoors",
  "brand": "SRAM",
  "shopName": "ecobuildhardware",
  "listingType": "Promoted",
  "image": "https://c.76.my/UserImages/Items/TB220/222/342/222342403.jpg",
  "images": [
    "https://c.76.my/Malaysia/sram-axs-pod-ultimate-2-button-electronic-controller-ecobuildhardware-2409-21-ecobuildhardware@1.jpg",
    "https://c.76.my/Malaysia/sram-axs-pod-ultimate-2-button-electronic-controller-ecobuildhardware-2409-21-ecobuildhardware@2.jpg",
    "https://c.76.my/Malaysia/sram-axs-pod-ultimate-2-button-electronic-controller-ecobuildhardware-2409-21-ecobuildhardware@3.jpg"
  ],
  "viewCount": 3063,
  "postedYearMonth": "2027-05",
  "listingUrl": "https://www.lelong.com.my/sram-axs-pod-ultimate-2-button-electronic-controller-ecobuildhardware-222342403-2027-05-Sale-P.htm",
  "sourceUrl": "https://www.lelong.com.my/catalog/all/list?TheKeyword=electronics&CategoryID=0",
  "scrapedAt": "2026-11-02T00:10:35.130Z"
}
```

> **On `condition` / `availability` values:** Lelong's JSON-LD frequently declares listings as `Used` / `InStock` by default, so on many sweeps these two fields look uniform — that reflects the source data, not a parsing quirk. The full normalized value sets the actor can emit are listed below.

***

### 🔁 Pagination & Scale

Lelong's catalog search (`/catalog/all/list?TheKeyword=…`) is **not numerically paginated** — one request returns a single server-rendered grid of up to ~60 products for that term. So this **Lelong.my scraper** scales by *breadth of terms*, not by paging:

- **Give it many keywords / categories.** Each term is its own search; results across all terms are **deduplicated on `listingUrl`** before output. A multi-category sweep of dozens of terms returns **several hundred unique, fully enriched records**.
- **`maxRecords` is the hard ceiling.** Set it to the total you want across every term (`0` = unlimited, bounded only by Lelong's supply for your terms). A no-filter default-sample run is auto-capped at 100 to stay cheap.
- **Transient "shell" 200s are retried, not fatal.** Lelong intermittently returns a valid 200 of a search URL *without* the product grid. The actor detects this and **re-fetches the same URL up to 3×** with fresh headers (the grid reliably reappears). A term still empty after retries is **skipped**, and the sweep continues — so a cluster of shell responses can no longer cut a large job short. Only consecutive **hard** fetch failures (a real outage) abort the run.
- **Concurrency controls detail-page speed.** `maxConcurrency` (default 4, verified stable at 8) parallelises the per-item detail fetches that enrich every stub with description, condition, shop, gallery and view count.
- **Verified:** a broad multi-category sweep produced **160+ unique records at ~100% core-field coverage in ~90 seconds** with zero duplicates and zero value bugs.

For deeper coverage of a single vertical or seller, paste a **category URL or shop URL** in `startUrls` instead of relying on keyword grids.

***

### Listing Type & Availability Reference

#### Listing types

| Value | Meaning | URL marker |
|---|---|---|
| `Item` | Standard organic listing | `-Sale-I.htm` |
| `Promoted` | Paid placement (sponsored / featured slot) | `-Sale-P.htm` |

Promoted listings are typically Lelong's "Best Sellers", "Spotlight", or carousel slots. Filter on `listingType === "Promoted"` to study what shops are paying to push, or `listingType === "Item"` for purely organic supply.

#### Availability values

| Value | Meaning |
|---|---|
| `InStock` | Ready to ship |
| `OutOfStock` | Seller currently has none |
| `PreOrder` | Available on backorder / built to order |
| `Discontinued` | No longer offered |

#### Condition values

| Value | Meaning |
|---|---|
| `New` | Brand-new, unopened |
| `Used` | Pre-owned, working |
| `Refurbished` | Restored to working condition |
| `Damaged` | Sold as-is for parts |

#### Common top-level categories on Lelong.my

| Category | Typical sellers |
|---|---|
| Mobile Phones & Gadgets | Apple, Samsung, Xiaomi, Huawei, Realme, Oppo, Vivo |
| Computer & Networking | Logitech, Razer, Asus, MSI, TP-Link, Synology |
| Automotive — Parts & Accessories | Perodua, Proton, Toyota, Honda, Nissan, Mitsubishi parts shops |
| Home & Living | Petronas-branded merch, kitchen appliances, smart home |
| Fashion & Accessories | Padini, Habib, Bonia, Vincci, Pestle & Mortar |
| Health & Beauty | Watsons-stocked SKUs, supplements, skincare |
| Books, Music & Movies | English / BM textbooks, vinyl, K-pop |
| Antiques & Collectibles | Malaysian vintage coins, stamps, comics, watches |
| Sports & Outdoors | Bicycles, fishing tackle, badminton (Yonex / Victor) |
| Toys & Hobbies | Lego, Gundam, RC cars, scale models |

***

### Use Cases

#### Malaysian E-Commerce Competitive Intelligence

Shopee, Lazada, TikTok Shop Malaysia, and PG Mall merchants use Lelong as a competitive benchmark:

- **Track competing shops** by name (`AUTOBAGS`, `STAR_ICON`, `mobileshop2u`) and snapshot their price ladder weekly
- **Spot price gaps** between Lelong and Shopee for identical SKUs
- **Identify high-velocity SKUs** via `viewCount` and `soldCount` deltas across runs
- **Monitor promoted-listing density** — which categories are flooded with paid placements vs. organic
- **Build long-tail catalogs** of products that Lelong serves but Shopee underweights

#### Dropshipping Product & Price Research

Malaysian and Singaporean dropshippers source from Lelong because **listing fees are zero** and many sellers operate as direct importers:

- **Discover trending categories** by harvesting `Promoted` listings across 50+ keywords
- **Margin model** with verified RM prices before posting to Shopify, TikTok Shop, or WhatsApp commerce
- **Find suppliers with deep stock** by counting listings per `shopName`
- **Cross-check supplier reliability** via `viewCount` (popularity) and listing recency (`postedYearMonth`)
- **Auto-translate listings** into English / Bahasa Indonesia / Thai for cross-border launch

#### Reseller Arbitrage (Lelong → Shopee / Lazada / TikTok Shop)

Independent resellers run nightly Lelong sweeps to spot mispriced items they can flip:

- **Watch a fixed keyword set** (`"iphone 14 pro max"`, `"playstation 5"`, `"perodua axia"`) for 20%+ price gaps
- **Filter for `Used` condition** on Lelong to catch resale arbitrage opportunities
- **Track new-stock arrivals** by diffing `listingId` sets across runs
- **Auto-bid on niche categories** like antique Malayan coins or vintage Petronas F1 collectibles
- **Build a "Lelong-only" SKU catalog** — items Shopee doesn't carry, ideal for high-margin niche stores

#### Brand Monitoring & Counterfeit Detection

Brand owners (Padini, Habib, Bonia, Toyota Malaysia, Perodua, Proton, Yonex, Asus) use the data to enforce IP:

- **Daily sweep** for unauthorised use of brand names in listing titles
- **Flag suspicious pricing** — a `RM 45` Apple Watch Series 9 is almost certainly counterfeit
- **Identify repeat-offender shops** by counting violations per `shopName`
- **Snapshot evidence** with `scrapedAt`, full image URLs, and `listingUrl` for takedown notices
- **Benchmark grey-market saturation** for parts that bypass authorised distributors

#### Market Research & Consumer Trend Analytics

Research consultancies, academic teams, and MIDA/MATRADE analysts use Lelong as a low-cost Malaysian retail signal:

- **Track consumer-electronics inflation** via average RM-price drift on benchmark SKUs (e.g., entry-level smartphones, air fryers, gaming consoles)
- **Quantify Malaysian appetite for refurbished vs. new** by condition mix in major categories
- **Spot seasonal lift** — Hari Raya (April–May), Merdeka (August), Deepavali (November) spending peaks
- **Geographic supply patterns** when sellers disclose `shopLocation` (Klang Valley vs. Penang vs. Johor)
- **Compare with Mudah.my data** for full second-hand vs. new-good market modelling

#### Price Benchmarking for Malaysian SMEs

Small retailers in Selangor, KL, Johor, and Penang use Lelong to benchmark before pricing inventory:

- **Median, P25, P90 pricing** for any keyword across the top-9 organic + top-N promoted listings
- **Identify Klang Valley vs. East Coast price differentials** when shop location is exposed
- **Detect predatory price floors** — listings sub-RM 10 in normally RM 50+ categories
- **Power MSME pricing tools** with a clean RM-denominated dataset
- **Validate distributor RRP compliance** for authorised retailers

#### Used Auto-Parts Pricing Data

Lelong has unusually deep automotive supply for **Perodua (Myvi, Axia, Bezza, Ativa)**, **Proton (Saga, Persona, X70, X50)**, **Toyota (Vios, Camry, Hilux, Alphard)**, **Honda (City, Civic, HR-V, CR-V)**, and **Nissan (Almera, X-Trail)**:

- **Build a parts-price index** for insurance and accident-write-off valuation
- **Source aftermarket vs. OEM** by parsing `brand` and listing title keywords
- **Track regional shops** (KL, Johor Bahru, Ipoh, Penang) for sourcing logistics
- **Power workshop quoting tools** with live RM pricing from real Malaysian suppliers
- **Salvage-yard inventory matching** for collision-repair shops

#### Antiques, Coins & Collectibles Valuation

Lelong remains Malaysia's largest open marketplace for vintage Petronas F1 merchandise, Malayan-era coins (1900s pre-Independence), Malaysian comics (Gila-Gila, Ujang), pre-2010 video games, and Habib jewellery resales:

- **Build a price guide** for numismatic dealers and pawn shops
- **Authenticate provenance** via cross-referencing seller history
- **Track auction-style listings** for high-value collectibles
- **Insurance valuation evidence** with timestamped market data
- **Heritage curation** — museums and academic projects studying Malaysian material culture

#### SEO & Affiliate Content (Malaysian Product Reviews)

Affiliate publishers, Malaysian tech YouTubers, and product-review blogs use Lelong data to:

- **Generate "Top 10 air fryers in Malaysia 2026"** style articles with verified live pricing
- **Power affiliate comparison widgets** with daily-refreshed RM prices and stock status
- **Track product launch coverage** — first appearance dates via `postedYearMonth`
- **Auto-update structured-data feeds** for Google Shopping eligibility
- **Build niche price-tracker websites** ("Vios spare parts under RM 100", "Refurbished iPhones in Penang")

***

### Sample Queries & Recipes

#### Recipe 1 — Toyota Vios spare parts price benchmark

```json
{
  "keywords": [
    "toyota vios bumper",
    "toyota vios headlight",
    "toyota vios door handle",
    "toyota vios brake pad",
    "toyota vios spark plug"
  ],
  "scrapeDetails": true,
  "maxRecords": 60
}
```

#### Recipe 2 — Smartphone market snapshot (Apple + Samsung + Xiaomi)

```json
{
  "keywords": [
    "iphone 15 pro max",
    "iphone 14",
    "samsung galaxy s24 ultra",
    "samsung galaxy a55",
    "xiaomi redmi note 13",
    "xiaomi 14"
  ],
  "scrapeDetails": true,
  "maxRecords": 100
}
```

#### Recipe 3 — Promoted-only sweep for a specific category

```json
{
  "keywords": ["gaming chair", "air fryer", "robot vacuum"],
  "scrapeDetails": true,
  "maxRecords": 80
}
```

Then post-filter `listingType === "Promoted"` downstream.

#### Recipe 4 — Single-item enrichment for a specific URL

```json
{
  "startUrls": [
    {"url": "https://www.lelong.com.my/original-toyota-prius-camry-vios-altis-hilux-door-lamp-switch-star-icon-X526784-2007-01-Sale-I.htm"}
  ],
  "scrapeDetails": true
}
```

#### Recipe 5 — Antiques & collectibles sweep

```json
{
  "keywords": [
    "malayan coin",
    "petronas formula 1",
    "vintage watch seiko",
    "habib jewellery",
    "gila-gila magazine"
  ],
  "scrapeDetails": true,
  "maxRecords": 80
}
```

#### Recipe 6 — Fashion brands monitor (Padini, Habib, Bonia)

```json
{
  "keywords": ["padini", "habib", "bonia", "vincci"],
  "scrapeDetails": true,
  "maxRecords": 50
}
```

#### Recipe 7 — Quick test (no detail enrichment, 9 items)

```json
{
  "keywords": ["durian"],
  "scrapeDetails": false,
  "maxRecords": 9,
  "maxConcurrency": 2
}
```

Useful for sanity-checking the actor before committing to a large run.

***

### Integration Examples

#### Google Sheets

Schedule the actor daily on Apify, then connect the dataset to Google Sheets via the built-in **Apify → Google Sheets** integration. You get a fresh Lelong product sheet every morning, ready for VLOOKUP-driven pricing tools and conditional formatting on `price` drift.

#### Make.com / Zapier / n8n

Wire the **Apify** connector to trigger downstream workflows on:

- New `listingId` values (additions to a watched keyword)
- Price drops above N% on tracked SKUs
- Stock changes (`InStock` → `OutOfStock`)
- Promoted-listing entries from a competitor `shopName`
- Daily Slack/Telegram digests of trending Lelong searches

#### Power BI / Tableau / Looker Studio

Connect the Apify REST API as a data source. Build dashboards for:

- RM price heat-maps per category
- Promoted vs. organic share over time
- Top-10 sellers by listing volume and `viewCount`
- Stock-availability time series for tracked SKUs

#### Postgres / Snowflake / BigQuery

Use the [Apify webhook integration](https://docs.apify.com/platform/integrations/webhooks) to POST the dataset URL to your ingestion endpoint after every scheduled run. `listingId` is a natural primary key; upserting is trivial.

```sql
CREATE TABLE lelong_listings (
    listing_id        TEXT PRIMARY KEY,
    title             TEXT,
    price_myr         NUMERIC(10,2),
    currency          TEXT,
    availability      TEXT,
    condition         TEXT,
    category          TEXT,
    brand             TEXT,
    listing_type      TEXT,
    shop_name         TEXT,
    shop_url          TEXT,
    image             TEXT,
    view_count        INTEGER,
    sold_count        INTEGER,
    posted_year_month TEXT,
    listing_url       TEXT,
    scraped_at        TIMESTAMPTZ
);
```

#### Shopify / WooCommerce / EasyStore catalog sync

Map Lelong records → product drafts in your storefront. `title`, `description`, `price`, `images` and `category` cover the minimum Shopify product spec. Override RM → currency conversion (MYR → SGD / USD) before publish.

#### Salesforce / HubSpot / Pipedrive

Treat each `shopName` as a Lead or Account record. Use listing volume, `viewCount`, and presence of `Promoted` placements as scoring signals for B2B outreach to active Lelong sellers.

#### Slack / Telegram / Discord alerts

POST the dataset summary to a webhook for instant team visibility:

```python
import requests
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())
hot = [i for i in items if i.get("viewCount", 0) > 1000]
requests.post(WEBHOOK, json={
    "text": f"Lelong sweep: {len(items)} listings, {len(hot)} hot (>1k views)"
})
```

***

### Major Malaysian Markets & Seller Hubs

| Hub | State | Why it matters on Lelong |
|---|---|---|
| Kuala Lumpur (KL) | Federal Territory | Largest concentration of electronics + fashion shops |
| Petaling Jaya | Selangor | Klang Valley auto-parts and home-goods cluster |
| Shah Alam | Selangor | Wholesale-leaning shops, heavy in automotive and industrial |
| Subang Jaya | Selangor | Mobile and gadget importers |
| George Town | Penang | Antiques, collectibles, photography, vintage electronics |
| Bayan Lepas | Penang | Computer parts and refurb electronics (free-trade-zone proximity) |
| Johor Bahru | Johor | Cross-border (Singapore) electronics arbitrage, automotive |
| Ipoh | Perak | White goods, vintage Malaysian collectibles |
| Kuching | Sarawak | East Malaysia specialty goods, fishing tackle |
| Kota Kinabalu | Sabah | Outdoor / adventure gear |
| Melaka | Melaka | Antiques and Peranakan collectibles |
| Seremban | Negeri Sembilan | Automotive aftermarket |

Lelong does not surface seller location in JSON-LD by default; the actor fills `shopLocation` when the field appears in DOM (`a.catalogShop-name`, breadcrumb tail, or shop-page enrichment).

***

### Cost & Performance

| Metric | Value |
|---|---|
| Engine | Pure HTTP (lightweight, no headless browser) |
| Cold-start time | ~15–25 seconds |
| Memory footprint | 256–512 MB sufficient; 1024 MB ceiling |
| Runtime — 50 items (prefill, detail on) | ~15–25 seconds |
| Runtime — 160+ enriched items (detail on) | ~60–90 seconds |
| Runtime — 1,000+ items (detail off) | ~30–60 seconds |
| Data freshness | Live at run time |
| Auth required | None |
| Proxy required | Optional |
| Concurrency | 1–20 (default 6) |
| Records per catalog page | Up to ~60 per page, with automatic multi-page pagination |

***

### Frequently Asked Questions

#### How fresh is the data?

**Live at the moment of the run.** Every record is fetched from Lelong.my at runtime — there is no cached index, no upstream snapshot, no overnight refresh window.

#### How many records can I extract?

Each search page returns up to ~60 products, and the actor automatically paginates across pages (`&D=2, 3, ...`) and expands selected categories across related search terms. Setting `maxRecords: 1000` (or `0` for unlimited) easily collects **1,000+ unique, deduplicated product listings** in a single run.

#### Does this actor require login or Apify proxy?

**No** to both. Direct HTTP requests work out of the box. The proxy field is exposed only for users who want IP rotation on very large jobs (optionally with `apifyProxyCountry: "MY"` for Malaysian IPs).

#### How do I get deeper coverage for one term?

- Select one or more categories from the **Categories** dropdown (automatically expanded into related sub-queries)
- Add more specific search terms to **Search Keywords**
- Increase `maxRecords` to `1000+` (or `0` for unlimited) and let the multi-page paginator walk all available catalog pages

#### What's the difference between "Item" and "Promoted" listings?

- **`Item`** — regular organic listing, URL ends in `-Sale-I.htm`
- **`Promoted`** — paid placement / spotlight slot, URL ends in `-Sale-P.htm`

Promoted listings are valuable for understanding which sellers are spending to push particular SKUs.

#### Can I use this for large catalog sweeps?

Yes — combine multiple categories or a list of keywords with `maxRecords: 1000` (or `0` for unlimited) and run on an Apify Schedule for daily or weekly Malaysian marketplace intelligence.

#### Does this work outside Malaysia?

The Lelong.my source is Malaysia-only. For neighbouring markets, see [Chotot Vietnam](https://apify.com/haketa/chotot-scraper) or [Lamudi Philippines](https://apify.com/haketa/lamudi-scraper).

#### What export formats are available?

JSON, CSV, Excel (XLSX), HTML, XML, RSS, and JSON Lines — all directly from the Apify dataset view.

#### Can I schedule this actor?

Yes — Apify's built-in Scheduler supports cron expressions. A common pattern: every 6 hours sweep 20 priority keywords, daily at 02:00 KL time sweep the long-tail keyword list.

#### Does it work on the Apify Free Plan?

Yes — full functionality on free tier. A single-keyword run costs cents and runs well within free-tier compute units.

#### What happens if Lelong is down or returns empty results?

The actor retries **3× per request** with exponential backoff. If Lelong returns a valid `200` *without* the product grid (a "shell" page — this happens intermittently), the actor **re-fetches the same search URL up to 3×** with fresh headers until the grid comes back; the grid reliably reappears on a retry, so transient shells no longer cost you records. A term that is still empty after re-fetching is simply **skipped**, and the sweep continues to the next keyword/category — one unlucky cluster of shell responses never truncates a large job. Only **consecutive hard fetch failures** (non-200 / timeouts, i.e. a genuine Lelong outage) abort the run. If a run finishes with zero records, the actor sets a descriptive **status message** so you immediately know to broaden your keywords or verify any pasted Start URLs.

#### Are images downloaded or just linked?

Just linked (absolute URLs on `c.76.my`, Lelong's CDN). Downloading would multiply costs and storage; downstream pipelines typically pull on demand.

#### What's a "promoted" listing worth tracking?

If a competitor consistently runs promoted placements on the same keyword cluster for weeks, that's a strong signal they're profitable on those SKUs. Track promoted-listing density to reverse-engineer competitor strategy.

#### Can I combine this with Shopee / Lazada scrapers?

Yes — `title` and `brand` are clean enough for fuzzy matching across marketplaces. We don't publish a Shopee scraper directly, but the schema is designed to merge cleanly with any Malaysian e-commerce dataset.

#### What if a shop name contains special characters?

Shop names are passed through as-is (`STAR_ICON`, `mobileshop2u`, `蝶之恋`). Use `shopName` as a string and avoid treating it as a URL component — that's what `shopUrl` is for.

#### How are prices in non-RM currencies handled?

Lelong is RM-only; `currency` is always `MYR`. If a listing is cross-listed at USD or SGD (rare), the JSON-LD `priceCurrency` is preserved verbatim.

#### How do I report a bug?

Open an issue on the Apify Store page or use the "Contact developer" link.

***

### 🔗 Related Apify Actors by Haketa

#### E-Commerce & Product Scrapers (Closest Siblings)

If you benchmark Lelong.my against other online marketplaces, these product and price scrapers share the same clean, flat, export-ready schema:

- [Tokopedia Scraper (Indonesia)](https://apify.com/haketa/tokopedia-scraper) — Indonesia's largest e-commerce marketplace: products, prices, sellers
- [Wildberries Scraper](https://apify.com/haketa/wildberries-scraper) — CIS mega-marketplace product and price data
- [AliExpress Product Scraper](https://apify.com/haketa/aliexpress-product-scraper) — cross-border product listings, pricing, ratings
- [Zalando Scraper](https://apify.com/haketa/zalando-scraper) — European fashion catalog, prices, availability
- [Flipp Scraper](https://apify.com/haketa/flipp-scraper) — North American retail flyers and weekly deal prices
- [Oscaro Product Scraper](https://apify.com/haketa/oscaro-product-scraper) — European auto-parts catalog and pricing

#### Marketplaces & Classifieds (Direct Siblings)

- [Mudah.my Scraper (Malaysia)](https://apify.com/haketa/mudah-scraper) — Malaysia's #1 classifieds (legacy; see actor README for current status)
- [Chotot.com Scraper (Vietnam)](https://apify.com/haketa/chotot-scraper) — Vietnam's largest classifieds
- [Mourjan Scraper (MENA)](https://apify.com/haketa/mourjan-scraper) — Middle East & North Africa classifieds
- [Kleinanzeigen.de Scraper (Germany)](https://apify.com/haketa/kleinanzeigen-scraper) — eBay Kleinanzeigen replacement
- [Marktplaats.nl Scraper (Netherlands)](https://apify.com/haketa/marktplaats-scraper) — Dutch classifieds leader
- [OfferUp Scraper (US)](https://apify.com/haketa/offerup-scraper) — US peer-to-peer marketplace
- [Kijiji.ca Scraper (Canada)](https://apify.com/haketa/kijiji-scraper) — Canada's #1 classifieds
- [TradeMe Scraper (New Zealand)](https://apify.com/haketa/trademe-scraper) — NZ's largest marketplace

#### Automotive

- [YallaMotor Scraper (GCC)](https://apify.com/haketa/yallamotor-scraper) — Saudi/UAE/Kuwait/Qatar/Oman car listings

#### Real Estate (Useful for Cross-Region SEA Coverage)

- [Lamudi Philippines Real Estate Scraper](https://apify.com/haketa/lamudi-scraper)
- [Realestate.com.kh Cambodia Scraper](https://apify.com/haketa/realestate-com-kh-scraper)
- [Zameen.com Pakistan Real Estate Scraper](https://apify.com/haketa/zameen-scraper)
- [Domain.com.au Property Scraper (Australia)](https://apify.com/haketa/domain-com-au-scraper)

#### Healthcare & Care (For Cross-Niche Reseller Research)

- [WhatClinic.com Clinic Scraper](https://apify.com/haketa/whatclinic-scraper)
- [Bookimed.com Medical Tourism Scraper](https://apify.com/haketa/bookimed-scraper)

***

### Comparison vs. Alternatives

| Approach | Setup time | Data freshness | Cost (100 listings) | Maintenance | Anti-bot handling |
|---|---|---|---|---|---|
| **This actor** | < 1 minute | Live at run | < $0.05 | Zero | N/A (Lelong is open) |
| Manual browse + spreadsheet | Hours per sweep | Live but stale by hand | Free | High (human) | N/A |
| Custom Node/Python scraper | 1–2 days dev | Live | Free + infra | High (selector drift) | DIY (none needed today) |
| Headless browser (Playwright) | 1–2 days dev | Live | Expensive (RAM/CPU) | High | Overkill — Lelong is open |
| Paid commercial dataset | Days of sales calls | Weekly / monthly | $$$$ | None | Vendor's problem |
| No data | Free | N/A | Free | N/A | N/A |

The **HTTP-only architecture** is the key differentiator: it costs an order of magnitude less than browser-based scrapers (no Chrome, no 1 GB RAM, no 30-second cold starts), runs faster, and is easier to extend.

***

### Why Pay-Per-Event Pricing?

Most data scrapers either lock you into a flat monthly subscription (you pay even if you don't run) or charge by compute-unit (you can't predict the bill). This actor uses **pay-per-event**, which means:

- You only pay when the actor runs and produces data
- Charges scale with actual records delivered — not duration
- Transparent line-item billing inside the Apify Console
- No monthly minimums, no commitments
- Free to evaluate with `maxRecords: 9` for cents
- Predictable per-keyword cost makes budgeting trivial

***

### Support

- **Bug reports** — open an Issue on the [Apify Store actor page](https://apify.com/haketa/lelong-my-scraper)
- **Feature requests** — same place; please describe your use case and target keyword / URL pattern
- **Direct contact** — message via the Apify developer profile

If this actor saves you a week of engineering, a **5-star rating** on the Apify Store helps other Malaysian e-commerce, reseller, and dropshipping teams find it. Terima kasih!

***

### 📋 Changelog

| Version | Date | Notes |
|---|---|---|
| **1.2.0** | **2026-11** | **Unlocked 1,000+ deep pagination & full-grid extraction.** Removed the `DEFAULT_RUN_CAP` clamp when `maxRecords` is explicitly provided (`maxRecords: 1000+` is now fully honored), added multi-page catalog pagination (`&D=2, 3, ...`), combined JSON-LD + full 60-card grid extraction on every search page, and added automatic category-to-keyword expansion for high-volume runs. Faster prefill configuration (`maxRecords: 50`, `maxConcurrency: 6`). |
| **1.1.0** | **2026-11** | **Shell-page resilience + larger yields.** Added up-to-3× re-fetch when Lelong serves a grid-less shell response on a valid search URL, and skip empty terms without aborting large multi-keyword sweeps. Verified ~100% core-field coverage and 0 duplicates. |
| **1.0.4** | **2026-10** | **Search migration + enrichment.** Updated keyword and category search to Lelong's live catalog endpoint, added catalog-card attribute parser, recovered `listingId` for `I…` ID format, normalized currency `RM` → `MYR`, and added legacy `/kx/` URL auto-rewriting. |
| **1.0.0** | **2026-05** | Initial public release of Lelong.my Scraper with JSON-LD + DOM extraction, optional detail-page enrichment, and automatic deduplication. |

# Changelog

This Actor's version history is a separate document: https://apify.com/haketa/lelong-my-scraper/changelog.md

# Actor input Schema

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

Pick one or more product categories to collect. This is the easiest way to start — no typing needed. Leave everything empty to get a diverse sample of popular products across the marketplace.

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

Want something specific? Type any product search terms — each one runs its own search. Examples: 'toyota vios', 'iphone 15', 'samsung galaxy', 'air fryer', 'gaming chair'. Leave empty to use the categories above (or the built-in popular sample).

## `scrapeDetails` (type: `boolean`):

Visit each item page for richer data (full description, condition, seller, full image set). Slower but much more complete. Adds ~1-3 seconds per item.

## `maxRecords` (type: `integer`):

Maximum total items to collect across all categories / keywords. Set 0 for unlimited. A no-filter run is automatically capped so it stays fast and cheap.

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

Advanced: paste any Lelong.my URL to scrape it directly — a keyword search, a category page, an individual item, or a shop page. When provided, this overrides the categories and keywords above.

## `requestDelay` (type: `integer`):

Delay between requests. 500-1500ms is polite and stable.

## `maxConcurrency` (type: `integer`):

Parallel requests. 4-8 is a good balance of speed and stability.

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

Optional. A proxy is not required for this actor. Enable only if you want to rotate IPs for very large jobs.

## Actor input object example

```json
{
  "categories": [
    "smartphone"
  ],
  "keywords": [
    "toyota vios"
  ],
  "scrapeDetails": true,
  "maxRecords": 50,
  "startUrls": [],
  "requestDelay": 500,
  "maxConcurrency": 6,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `listingId` (type: `string`):

Unique Lelong.my listing identifier

## `title` (type: `string`):

Product listing title

## `price` (type: `string`):

Numeric product price in MYR

## `currency` (type: `string`):

ISO currency code (MYR)

## `availability` (type: `string`):

Stock availability status

## `category` (type: `string`):

Marketplace product category

## `brand` (type: `string`):

Product brand or manufacturer

## `shopName` (type: `string`):

Seller store name on Lelong.my

## `listingType` (type: `string`):

Listing type (Item or Promoted)

## `listingUrl` (type: `string`):

Direct URL to the Lelong.my product page

## `scrapedAt` (type: `string`):

ISO-8601 extraction timestamp

# 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 = {
    "categories": [
        "smartphone"
    ],
    "keywords": [
        "toyota vios"
    ],
    "scrapeDetails": true,
    "maxRecords": 50,
    "startUrls": [],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/lelong-my-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 = {
    "categories": ["smartphone"],
    "keywords": ["toyota vios"],
    "scrapeDetails": True,
    "maxRecords": 50,
    "startUrls": [],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/lelong-my-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 '{
  "categories": [
    "smartphone"
  ],
  "keywords": [
    "toyota vios"
  ],
  "scrapeDetails": true,
  "maxRecords": 50,
  "startUrls": [],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call haketa/lelong-my-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,haketa/lelong-my-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/iPa5vUmlRwRveanb0/builds/o2Zcj0CQLKMR5Sf49/openapi.json
