# Tokopedia Scraper - Products, Prices, Sellers & Variants (`abotapi/tokopedia-scraper`) Actor

Extract product data from Tokopedia, Indonesia’s largest marketplace. Search by keyword with sorting and filters, or use product/result URLs. Returns price, discount, rating, sold count, seller, category, images, and optional details like description, videos, variants, and wholesale tiers.

- **URL**: https://apify.com/abotapi/tokopedia-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 50 total users, 3 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: 4.00 out of 5 stars

## Pricing

from $1.00 / 1,000 product scrapeds

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

## Tokopedia Scraper: Products, Prices, Variants, Sellers

Tokopedia Scraper turns Tokopedia into structured product data. Search by keyword with price, rating, condition, seller-tier and discount filters, or paste product and search-result URLs directly. Every record carries the price, discount, rating, seller and category, and with detail fetching on, the sold and review counts, the full description, every image and video, variants, wholesale tiers and live stock. Export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Two ways in.** Search by keyword with filters and sorting, or paste product and search-result URLs directly. No need to build query strings by hand.
- **Filters apply to both modes.** Price, rating and discount narrow even a single pasted product URL, not just a keyword search.
- **Full detail on demand.** Turn on detail fetching to add the complete description, every image and video, variants, wholesale price tiers, live stock and seller flags, and pay only for the products you enrich.
- **Seller and pricing depth.** Seller tier, city and badges, plus original price, discount percentage and active flash-sale campaign data when a product is genuinely running one.
- **Built for monitoring.** Resume an interrupted crawl without re-collecting or re-paying for products already gathered, or track the same search on a schedule and get back only new and changed products.
- **Indonesia-scale catalogue.** Search Tokopedia's marketplace across any category, from electronics and fashion to groceries and home goods.
- **Clear failures.** A run that cannot read a resume dataset stops with a clear error instead of silently returning nothing.

### Use Cases

- **Price and stock monitoring:** track competitor prices, discounts and stock levels on a recurring schedule.
- **Catalogue research:** analyze category breadth, seller tiers and badges across a keyword or category.
- **Product data feeds:** enrich your own storefront, comparison site or price-tracking app with structured Tokopedia listings.
- **Flash-sale and deal tracking:** capture active campaign pricing, stock and timing on the products you follow.
- **Seller intelligence:** compare official stores and power merchants by rating, review count and sold count.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `productId` | `"11690069936"` |
| `name` | `"Sepatu Sneakers Pria Casual"` |
| `url` | Tokopedia link to the product |
| `categoryName` / `categoryBreadcrumb` | `"Sepatu Pria"` / `"Pria/Sepatu Pria/Sneakers"` |
| `price` / `priceText` | `150000` / `"Rp150.000"` |
| `originalPrice` / `discountPercentage` | `200000` / `25` |
| `isOnSpecial` / `specialsCategory` | `true` / `"Flash Sale"` (`isOnSpecial` is true for any active discount; `specialsCategory` is filled only on fetched product details when a campaign is active) |
| `rating` / `reviewCount` | `4.8` / `1240` (review count: fetched product details only) |
| `soldCount` / `soldText` | `500` / `"500+ terjual"` (read from the listing label when the site shows one, a rounded lower bound such as `10 rb+` = 10000; the exact count comes with fetched product details) |
| `condition` | `"Baru"` (fetched product details only) |
| `stock` / `stockText` | `85` / `"Tersedia"` (fetched product details only) |
| `shopName` / `shopCity` | `"Toko Sepatu Jaya"` / `"Jakarta Barat"` |
| `shopTier` | `3` (numeric seller tier code from the site; not the same as the `shopTier` input filter, which takes `official_store` or `power_merchant`) |
| `isOfficialStore` / `isPowerMerchant` | `false` / `true` (fetched product details only) |
| `image` | product image URL |
| `gallery` / `videos` | every product photo and video (fetched product details only) |
| `reviews` | newest customer reviews as a nested array: reviewer, rating, date, text, seller reply, helpful votes (fetched product details only; count set by `maxReviews`) |
| `variants` | size and colour options with names and swatches (fetched product details only) |
| `wholesale` | quantity price tiers (fetched product details only) |
| `description` | full product description text (fetched product details only) |
| `changeType` | `"NEW"`, `"UPDATED"`, `"UNCHANGED"`, `"REAPPEARED"`, or `"EXPIRED"` (incremental mode only) |

### How to Use

1. Pick a **mode**: `search` (build from keywords and filters) or `url` (paste product or search-result links).
2. In Search mode, enter your keywords and set any filters. In URL mode, paste the links; filters still narrow what a pasted search-result URL returns, and price, rating and discount also narrow a single pasted product URL.
3. Turn on **Fetch full product details** if you need the description, all media, variants and stock. Set **Max items** to control run size and cost.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Search with filters:**

```json
{
  "mode": "search",
  "searchTerms": ["sepatu"],
  "sortBy": "price_asc",
  "minPrice": 100000,
  "condition": "new",
  "maxItems": 50
}
```

**Paste a search-result URL:**

```json
{
  "mode": "url",
  "urls": ["https://www.tokopedia.com/search?q=sepatu"],
  "maxItems": 100
}
```

**Official-store search with full product details:**

```json
{
  "mode": "search",
  "searchTerms": ["iphone"],
  "shopTier": "official_store",
  "fetchDetails": true,
  "maxItems": 20
}
```

**Recurring search that returns only changes:**

```json
{
  "mode": "search",
  "searchTerms": ["laptop"],
  "incrementalMode": true,
  "stateKey": "laptop-watch"
}
```

#### Supported URL shapes

| Link | Result |
|---|---|
| `/search?q=...` (also `page`, `ob`, `pmin`, `pmax`, `condition`, `rt`, `shop_tier`) | listed products, link filters applied |
| `/p/<category>/<sub>` and `/find/<slug>` | search on the slug words |
| `/<shop>/<product>` (tracking query like `extParam` ignored) | full product record |
| `m.tokopedia.com/...` | treated like `www` |
| `/<shop>`, `/<shop>/product`, `/<shop>/product/page/<n>` | the shop's product list, 80 products per page (price, rating and discount filters apply; sort, condition, seller type and free shipping do not) |
| `/<shop>/review` | the shop's newest customer reviews, one row per reviewed product with up to **Reviews per product** reviews nested under `reviews` (10 reviews per page, **Max pages per search** bounds the pages, **Max items** bounds the products) |
| other shop tabs (`/etalase`, `/info`, ...), `tokopedia.link/...` | not supported, a warning is logged per URL |

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/tokopedia-scraper").call(run_input={"mode": "search", "searchTerms": ["sepatu"]})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(product["name"], product["priceText"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/tokopedia-scraper').call({ mode: 'search', searchTerms: ['sepatu'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Max items and Max pages

**Max items** is the single cap on how many products a run returns; set it to `0` for no limit. **Max pages per search** is only a safety bound on result pages walked per keyword; it does not cap the number of products. Leaving it empty or `0` means up to 100 pages per keyword (a hard cap), and any value above 100 is treated as 100, so a very large Max items can still return fewer products than asked for on a keyword that has more than 100 pages.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one specific interrupted run: paste its run or dataset ID and this run skips every product already collected there, so you don't pay twice for the same crawl.
- **Incremental mode** (`incrementalMode`) is for scheduling the same search or URL list again and again, for example daily. The actor remembers the previous run of that exact search by itself, so no run ID is needed. The first run returns everything as `NEW`. Later runs classify each product as `NEW`, `UPDATED` (with `changedFields` listing what changed), `UNCHANGED` (suppressed and not billed unless **Emit unchanged** is on), `REAPPEARED`, or `EXPIRED` (only after a run that fully scanned the tracked search, and only with **Emit expired** on). A product is `UPDATED` when any returned field changes (price, discount, stock, rating, sold count, title, images, description and so on). Never compared: `scrapedAt`, signed image and video link hosts and tokens (only the image path counts), and tracking tokens inside URLs. **Ignore fields when detecting changes** (`ignoreFieldsForChanges`) adds your own field names to that list, wherever they appear, while still returning them. `stateKey` names or shares a monitoring campaign; leave it empty and the actor derives one from the search terms or URLs, the active filters, and the detail setting. Shop product lists (`/<shop>`, `/<shop>/product`) carry fewer fields than search results, so for those rows the category, image, rating, labels, shop tier, listing position and search context are left out of the comparison (they are still returned), and a product first stored from a search is not marked `UPDATED` just because a shop page showed it. Starting a link at a later page (`/product/page/2`, `?page=3`) never triggers `EXPIRED`. Shop review rows (`/<shop>/review`) are tracked per product by their review IDs: a row is `NEW` the first time, `UPDATED` when the set of reviews returned for that product changes, and never `EXPIRED`. With incremental mode off, output is exactly as before.

### Send results into your apps (MCP connectors)

Optionally push each scraped product into apps you already use, through Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape; the Apify dataset is never changed.

**What gets sent:** a condensed, human-readable summary of each product, a title plus its key fields flattened to plain text, not the full JSON record. The complete record always stays in the Apify dataset.

1. Authorize a connector once under Apify, Settings, Integrations (Notion, Linear, Airtable, or Apify).
2. Select it in the **Export to your apps (MCP)** input field. If the picker is empty, you haven't authorized a connector yet.
3. For Notion, also set `notionParentPageUrl` to the page under which product pages should be created.
4. Optionally set **Max items to export** to cap how many products are pushed to each connector per run (defaults to 50 when left empty).

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (keyword + filters) or `url` (paste product or result URLs). |
| `searchTerms` | array | `[]` | Keywords to search for, e.g. `"sepatu"`, `"laptop"`, `"iphone"`. Search mode only. |
| `urls` | array | 10 example links (one per supported shape) | URL mode only. Supported: search (with `page`, `ob`, `pmin`, `pmax`, `condition`, `rt`, `shop_tier` read from the link), `/p/<category>/<sub>` and `/find/<slug>` (the slug is used as the keyword), product pages (tracking query stripped), `m.tokopedia.com` links, shop pages (`/<shop>` or `/<shop>/product`, with `/product/page/<n>` to start at a later page: 80 products per page, then the same filters as above) and shop review pages (`/<shop>/review`: one row per reviewed product with its newest reviews nested). Other shop tabs and `tokopedia.link` short links are not supported and log a warning per URL. Filters you set in the input win over the link's own. Behaviour change: the field now ships with these examples as its default instead of an empty list. |
| `sortBy` | string | `relevance` | Result ordering: `relevance`, `newest`, `most_reviewed`, `price_asc`, or `price_desc`. Applies in Search mode and to a pasted search URL; not to a pasted product URL or shop URL. |
| `minPrice` | integer | (none) | Keep only products at or above this price, in Indonesian Rupiah. Applies in Search mode and to a pasted product, shop or search URL. |
| `maxPrice` | integer | (none) | Keep only products at or below this price, in Indonesian Rupiah. Applies in Search mode and to a pasted product, shop or search URL. |
| `condition` | string | `any` | `any`, `new`, or `used`. Applies in Search mode and to a pasted search URL; not to a pasted product URL or shop URL. |
| `minRating` | integer | (none) | Keep only products with an average rating at or above this value (1 to 5). Applies in Search mode and to a pasted product, shop or search URL. |
| `shopTier` | string | `any` | `any`, `official_store`, or `power_merchant`. Applies in Search mode and to a pasted search URL; not to a pasted product URL or shop URL. |
| `freeShippingOnly` | boolean | `false` | Keep only products eligible for free shipping. Applies in Search mode and to a pasted search URL; not to a pasted product URL or shop URL. |
| `discountOnly` | boolean | `false` | Keep only products with an active discount. Applies in Search mode and to a pasted product, shop or search URL. |
| `fetchDetails` | boolean | `false` | Adds the full description, every image and video, variants, wholesale tiers, stock, sold and review counts, weight, condition and seller flags. |
| `maxReviews` | integer | `20` | With detail fetching on, newest reviews attached to each product; a shop `/review` link returns reviews with or without detail fetching (0 = none, capped at 1000 per product). Reviews are ignored when deciding whether a product changed in incremental mode. |
| `maxPages` | integer | (none) | Safety bound on pages walked per keyword (or per shop reviews link). Empty or `0` means the hard cap of 100 pages; values above 100 are clamped to 100. This does not cap the number of products. |
| `maxItems` | integer | `20` | Maximum number of products to return across the whole run. `0` is unlimited. |
| `resumeFromRunId` | string | (none) | Paste a previous run ID or dataset ID to continue a large crawl without returning or charging for products already collected there. |
| `incrementalMode` | boolean | `false` | Turn on for daily or recurring monitoring; returns only new and changed products against remembered state. |
| `stateKey` | string | (none) | Optional name for an incremental-mode monitoring campaign. Leave empty to derive one automatically from the search or URLs, the active filters, and the detail setting. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also return (and bill for) products with no change since the last run. |
| `emitExpired` | boolean | `false` | Incremental mode only. Also return (and bill for) products no longer found, once a run has fully scanned the tracked search. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode only. Field names left out of UPDATED detection and `changedFields`, at any nesting level (for example `soldCount`); the fields are still returned in the output. |
| `proxy` | object | `{"useApifyProxy": true}` | Connection settings. Keep Apify Proxy enabled for the most complete results. |
| `mcpConnectors` | array | `[]` | Optional: send a summary of each product to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which product pages are created. |
| `maxNotifyListings` | integer | (none) | Alias of `maxNotifyItems`; either may be used. |
| `maxNotifyItems` | integer | (none) | Optional cap on how many products are pushed to the selected MCP connectors (defaults to 50 when left empty). |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "productId": "11690069936",
  "productKey": "sepatu-sneakers-pria-casual",
  "name": "Sepatu Sneakers Pria Casual",
  "url": "https://www.tokopedia.com/tokosepatujaya/sepatu-sneakers-pria-casual",
  "categoryName": "Sepatu Pria",
  "categoryBreadcrumb": "Pria/Sepatu Pria/Sneakers",
  "price": 150000,
  "priceText": "Rp150.000",
  "originalPrice": 200000,
  "discountPercentage": 25,
  "isOnSpecial": true,
  "specialsCategory": "Flash Sale",
  "rating": 4.8,
  "reviewCount": 1240,
  "soldCount": 500,
  "soldText": "500+ terjual",
  "condition": "Baru",
  "stock": 85,
  "stockText": "Tersedia",
  "minOrder": 1,
  "weight": 800,
  "weightUnit": "gr",
  "image": "https://images.tokopedia.net/img/product/sample.jpg",
  "gallery": [
    { "type": "image", "original": "https://images.tokopedia.net/img/product/sample-1.jpg" }
  ],
  "videos": [],
  "variants": [
    { "type": "Ukuran", "options": [{ "name": "40" }, { "name": "41" }, { "name": "42" }] }
  ],
  "wholesale": [
    { "minQty": 3, "price": 140000 }
  ],
  "description": "Sepatu sneakers pria bahan kanvas, nyaman dipakai harian, tersedia berbagai ukuran.",
  "shopId": "2000001",
  "shopName": "Toko Sepatu Jaya",
  "shopCity": "Jakarta Barat",
  "shopTier": 3,
  "isOfficialStore": false,
  "isPowerMerchant": true,
  "badgeTitle": "Power Merchant",
  "searchQuery": "sepatu",
  "searchPage": 1,
  "searchPosition": 3,
  "platform": "tokopedia",
  "mode": "detail",
  "scrapedAt": "2026-09-28T04:00:00.000Z"
}
```

**Output views.** The run's **Output** tab has five views over the same data: **Products** (the search and listing fields), **Product details** (description, media, variants, wholesale tiers, stock; filled when **Fetch full product details** is on), **Reviews** (one row per review, unwound from each product's `reviews` array), **Changes (incremental)** (`changeType` and `changedFields`; incremental mode only), and a **Run summary** with the run's counts and status message.

### Plan Requirement

The default connection (`{"useApifyProxy": true}`) works on a plan that includes Apify Proxy: the actor tries Apify datacenter proxy first. Only if a connection fails does it move on to Apify residential proxy, and then to a backup connection. If you supply your own proxy URLs, they are used as given.

Apify free plans do not include Apify Proxy access, so a free-plan run goes straight to the backup connection. The run logs this notice, and says so again once pages are served that way:

> The selected connection is unavailable to this run.
> Apify free plans do not include residential proxies.
> Continuing on a backup connection; some pages may still be unavailable.
> For residential access, upgrade at https://apify.com/pricing or supply your own proxy URLs.

On the backup connection some pages may still be unavailable. If it cannot load the pages either, the run finishes with zero products and this message:

> No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Tokopedia pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again.

If the proxy is refused outright, the message is:

> The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid.

If every results page request fails, the message is:

> Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later.

There is no direct-connection fallback.

### FAQ

#### How much does it cost?

You pay a small charge when a run starts, a charge per product returned, and an extra charge only for products where **Fetch full product details** was on. The **Pricing** tab shows the current rates. Reviews add no extra charge: nested reviews ride on the per-product row, and the detail charge applies once per product whose full detail is fetched (shop `/review` rows are not detail-fetched, so they add no detail charge). Use **Max items** and **Reviews per product** to cap the cost of any run.

#### Is it legal to scrape Tokopedia?

This actor collects only publicly available product listings from Tokopedia. You are responsible for how you use the data: follow Tokopedia's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Product names and prices are generally facts, but photos, videos and descriptions may be subject to third-party rights.

#### Can I get only new or changed products on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. The first run returns everything as `NEW`; later runs return only `NEW`, `UPDATED` and `REAPPEARED` products by default, and unchanged ones are not billed.

#### What's the difference between Resume and Incremental mode?

**Resume** (`resumeFromRunId`) continues ONE specific interrupted run using a pasted run or dataset ID. **Incremental mode** is for running the SAME search or URL list again and again on a schedule; the actor remembers state on its own, so no ID is pasted. Combining both only works to bootstrap a new incremental campaign from an existing dataset; if incremental state already exists for that search, the run stops with an error asking you to remove Resume or choose a different State key.

#### Why did my run fail instead of returning an empty dataset?

A run fails, rather than finishing with zero products, only when **Resume from a previous run** points at an ID this account cannot read, or when it's combined with **Incremental mode** that already has saved state for the same search. Everything else, including the site refusing requests or a search matching nothing, finishes as a successful run with zero products and a status message explaining why. Check the run's status message and log before assuming something is broken.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the **Export to your apps** field to push scraped products into Notion, Linear or Airtable.

### 🔗 Want more e-commerce data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🛒 <a href="https://apify.com/abotapi/trendyol-scraper"><b>Trendyol Scraper</b></a><br>From $0.80/1K. Scrape Trendyol products, prices, ratings, badges, sellers, full reviews...</td><td>🛒 <a href="https://apify.com/abotapi/thegioididong-scraper"><b>Thegioididong Scraper</b></a><br>Scrape thegioididong.com products with full specifications, current &amp; original price...</td></tr>
<tr><td>🛒 <a href="https://apify.com/abotapi/nitori-net-jp-scraper"><b>Nitori Japan Furniture &amp; Home Goods Scraper</b></a><br>Scrape Nitori Japan (nitori-net.jp) furniture and home goods by keyword, category or...</td><td>🛒 <a href="https://apify.com/abotapi/cdiscount-scraper"><b>Cdiscount Scraper</b></a><br>Scrape product data from Cdiscount.com by keyword, category, or URL. Apply filters and...</td></tr>
<tr><td>🛒 <a href="https://apify.com/abotapi/lazada-scraper"><b>Lazada Scraper</b></a><br>Scrape Lazada products and reviews across SEA markets, including Malaysia, Singapore...</td><td>🛒 <a href="https://apify.com/abotapi/ozon-ru-scraper"><b>Ozon.ru Scraper</b></a><br>Extract structured product data from Ozon.ru, Russia’s largest marketplace. Search by...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/tokopedia-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

Choose Search to build queries from keywords and filters, or URL to scrape pasted product or result-page URLs directly.

## `searchTerms` (type: `array`):

One or more keywords to search for, e.g. "sepatu", "laptop", "iphone".

## `urls` (type: `array`):

Paste search, category (/p/), collection (/find/), product, or shop URLs (home, /product list, or /review). A shop /review link returns one row per reviewed product with the shop's newest reviews nested under reviews. Short links (tokopedia.link) are not supported and log a warning. Filters and sorting below still apply — see each field for which URL shape it covers.

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

Result ordering. Applies in Search mode and to a pasted search URL; does not apply to a pasted product URL or shop URL.

## `minPrice` (type: `integer`):

Only keep products at or above this price, in Indonesian Rupiah. Applies in Search mode and to a pasted product, shop or search URL.

## `maxPrice` (type: `integer`):

Only keep products at or below this price, in Indonesian Rupiah. Applies in Search mode and to a pasted product, shop or search URL.

## `condition` (type: `string`):

Keep only products that match this condition. Applies in Search mode and to a pasted search URL; does not apply to a pasted product URL or shop URL.

## `minRating` (type: `integer`):

Only keep products with an average rating at or above this value (1-5). Applies in Search mode and to a pasted product, shop or search URL.

## `shopTier` (type: `string`):

Keep only products from this type of seller. Applies in Search mode and to a pasted search URL; does not apply to a pasted product URL or shop URL.

## `freeShippingOnly` (type: `boolean`):

Keep only products eligible for free shipping. Applies in Search mode and to a pasted search URL; does not apply to a pasted product URL or shop URL.

## `discountOnly` (type: `boolean`):

Keep only products with an active discount. Applies in Search mode and to a pasted product, shop or search URL.

## `fetchDetails` (type: `boolean`):

Adds the full description, every image and video, variants, wholesale tiers, stock, sold and review counts, weight, condition and seller flags. Without it, runs already return name, price, image, rating, seller and category.

## `maxReviews` (type: `integer`):

With Fetch full product details on, attaches this many newest customer reviews to each product (reviewer, rating, date, text, seller reply, helpful votes). A shop /review link returns at most this many reviews per product regardless of that switch. 0 turns reviews off. Capped at 1000 per product. Reviews add no charge beyond the per-product row and the per-product detail charge.

## `maxPages` (type: `integer`):

Safety bound on pages walked per keyword (or per shop reviews link, 10 reviews a page). Leave empty or 0 for the hard cap of 100 pages; values above 100 are clamped to 100. This does NOT cap the number of products; the run still stops at Max items.

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

Maximum number of products to return across the whole run. The single finite cap. Set 0 for unlimited. Capped at 1,000,000.

## `proxy` (type: `object`):

Keep Apify Proxy enabled for the most complete results.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large crawl without returning or charging for products already collected there. Use this after an interrupted run, or to continue a catalogue pull across multiple runs. For recurring daily/weekly monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching products as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED products. Turn on "Emit unchanged" or "Emit expired" only when you also want those products returned (and billed). A product counts as UPDATED when any returned field changes (price, discount, stock, rating, sold count, title, images, description and so on), and changedFields lists them. Never compared: scrapedAt, signed image/video link hosts and tokens (only the image path counts), and tracking tokens inside URLs; add more with Ignore fields when detecting changes below. State is kept separately for each search/URL and filter/detail setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search terms/URLs and filter/detail settings — different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return products that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return products that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max items capped it, when Max pages capped it, or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names to leave out of UPDATED change detection, in addition to the built-in handling (scrapedAt, signed image and video link tokens and tracking tokens never trigger a false UPDATED). A name is ignored wherever it appears, including inside nested lists such as gallery. The field is still returned in the output. Use this if a field in your setup turns out to be noisy, e.g. soldCount when you only care about price.

## `mcpConnectors` (type: `array`):

Optional. Pick connected apps (Notion, Airtable, Linear, Apify) to also push each scraped product to, in addition to the dataset.

## `notionParentPageUrl` (type: `string`):

Optional. When a Notion connector is selected, create one page per product under this parent page.

## `maxNotifyItems` (type: `integer`):

Optional cap on how many products are pushed to the selected MCP connectors.

## `maxNotifyListings` (type: `integer`):

Same as Max items to export (alias, either may be used). Optional cap on products pushed to the selected MCP connectors.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "sepatu"
  ],
  "urls": [
    "https://www.tokopedia.com/search?q=sepatu",
    "https://www.tokopedia.com/search?q=sepatu&page=2&ob=5",
    "https://www.tokopedia.com/search?q=sepatu&pmin=100000&pmax=500000&condition=1&rt=4&shop_tier=2",
    "https://www.tokopedia.com/p/fashion-pria/sepatu-pria",
    "https://www.tokopedia.com/find/sepatu-pria",
    "https://www.tokopedia.com/adidasindonesiaofficial/adidas-lifestyle-breaknet-3-0-shoes-men-white-jr3547-1732311054446462551",
    "https://m.tokopedia.com/search?q=sepatu",
    "https://www.tokopedia.com/adidasindonesiaofficial",
    "https://www.tokopedia.com/adidasindonesiaofficial/product/page/2",
    "https://www.tokopedia.com/adidasindonesiaofficial/review"
  ],
  "sortBy": "relevance",
  "condition": "any",
  "shopTier": "any",
  "freeShippingOnly": false,
  "discountOnly": false,
  "fetchDetails": false,
  "maxReviews": 20,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "mcpConnectors": []
}
```

# Actor output Schema

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

No description

## `detail` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {
    "mode": "search",
    "searchTerms": [
        "sepatu"
    ],
    "urls": [
        "https://www.tokopedia.com/search?q=sepatu",
        "https://www.tokopedia.com/search?q=sepatu&page=2&ob=5",
        "https://www.tokopedia.com/search?q=sepatu&pmin=100000&pmax=500000&condition=1&rt=4&shop_tier=2",
        "https://www.tokopedia.com/p/fashion-pria/sepatu-pria",
        "https://www.tokopedia.com/find/sepatu-pria",
        "https://www.tokopedia.com/adidasindonesiaofficial/adidas-lifestyle-breaknet-3-0-shoes-men-white-jr3547-1732311054446462551",
        "https://m.tokopedia.com/search?q=sepatu",
        "https://www.tokopedia.com/adidasindonesiaofficial",
        "https://www.tokopedia.com/adidasindonesiaofficial/product/page/2",
        "https://www.tokopedia.com/adidasindonesiaofficial/review"
    ],
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/tokopedia-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 = {
    "mode": "search",
    "searchTerms": ["sepatu"],
    "urls": [
        "https://www.tokopedia.com/search?q=sepatu",
        "https://www.tokopedia.com/search?q=sepatu&page=2&ob=5",
        "https://www.tokopedia.com/search?q=sepatu&pmin=100000&pmax=500000&condition=1&rt=4&shop_tier=2",
        "https://www.tokopedia.com/p/fashion-pria/sepatu-pria",
        "https://www.tokopedia.com/find/sepatu-pria",
        "https://www.tokopedia.com/adidasindonesiaofficial/adidas-lifestyle-breaknet-3-0-shoes-men-white-jr3547-1732311054446462551",
        "https://m.tokopedia.com/search?q=sepatu",
        "https://www.tokopedia.com/adidasindonesiaofficial",
        "https://www.tokopedia.com/adidasindonesiaofficial/product/page/2",
        "https://www.tokopedia.com/adidasindonesiaofficial/review",
    ],
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/tokopedia-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 '{
  "mode": "search",
  "searchTerms": [
    "sepatu"
  ],
  "urls": [
    "https://www.tokopedia.com/search?q=sepatu",
    "https://www.tokopedia.com/search?q=sepatu&page=2&ob=5",
    "https://www.tokopedia.com/search?q=sepatu&pmin=100000&pmax=500000&condition=1&rt=4&shop_tier=2",
    "https://www.tokopedia.com/p/fashion-pria/sepatu-pria",
    "https://www.tokopedia.com/find/sepatu-pria",
    "https://www.tokopedia.com/adidasindonesiaofficial/adidas-lifestyle-breaknet-3-0-shoes-men-white-jr3547-1732311054446462551",
    "https://m.tokopedia.com/search?q=sepatu",
    "https://www.tokopedia.com/adidasindonesiaofficial",
    "https://www.tokopedia.com/adidasindonesiaofficial/product/page/2",
    "https://www.tokopedia.com/adidasindonesiaofficial/review"
  ],
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/tokopedia-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/tokopedia-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/WLYJXjaPMb1pHRG23/builds/1r5y4IIdWvyWdgBh0/openapi.json
