# Wildberries Scraper API - Парсер Wildberries и Вайлдберриз (`mattnews17/wildberries-scraper`) Actor

Аналитика и парсер Вайлдберриз (ВБ): цены, скидки, реальные остатки по каждому SKU, рейтинги, продавцы с wildberries.ru. Wildberries scraper API for Russia and the largest RU marketplace — verified per-SKU stock, no duplicate billing. 100 товаров за запрос. $0.001 per product. Неофициальный.

- **URL**: https://apify.com/mattnews17/wildberries-scraper.md
- **Developed by:** [Matt Newsome](https://apify.com/mattnews17) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 4.71 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.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Wildberries Scraper — Product, Price, Discount & Stock Data

**Wildberries scraper** (парсер Wildberries) that extracts structured product
data from wildberries.ru, Russia's largest online marketplace. Search by keyword
and get clean rows with **current price, original price, discount percentage,
stock quantity, rating, review count, brand and seller** — at **$0.001 per
product**, and never billed twice for the same SKU.

> **Unofficial tool.** This Actor is not affiliated with, endorsed by, or
> connected to Wildberries or its operators in any way. "Wildberries" is used
> solely to describe the publicly accessible website this Actor reads. All
> trademarks belong to their respective owners.

Cheap by design: **100 products per request**, straight from JSON. No headless
browser, no per-product page fetches.

Throughput is deliberately throttled rather than fast. Wildberries returns HTTP
429 to parallel requests from datacenter IPs, so the defaults are 1 concurrent
request at 6 per minute — about **600 products per minute**, so a 1,000-product
run takes roughly two minutes. Raising those defaults on datacenter IPs does not
make it faster, it makes it return nothing. With residential proxies you can
raise both.

### What you get

| Field | Description |
|---|---|
| `sku` | Wildberries product ID |
| `title` | Product name |
| `brand` | Brand name (`null` when the seller left it blank) |
| `price` | Current selling price |
| `originalPrice` | Pre-discount list price, when genuinely marked down |
| `discountPct` | Discount percentage, computed from the two prices |
| `currency` | `RUB` |
| `inStock` | Availability as a boolean |
| `stockQuantity` | Units available — not just in/out |
| `rating` | Average review score |
| `reviewCount` | Number of reviews |
| `supplier` | Selling entity |
| `url` | Canonical product URL |
| `scrapedAt` | ISO timestamp |

`categoryPath`, `image` and `description` are always `null` in Wildberries mode —
the search endpoint does not return them. Image URLs are deliberately omitted
rather than guessed: Wildberries shards images across numbered CDN baskets whose
ID ranges shift over time, and a plausible-looking dead link is worse than an
honest null.

### Real per-SKU stock (optional)

Most Wildberries scrapers report the search endpoint's quantity field as if it
were stock. It isn't. Measured across three runs (100 headphones, 200
smartphones, 200 sneakers) it returns **one repeated value for 65–95% of rows** —
46 in one run, 48 in two others — with only a handful of genuine numbers. A
"stock" column that says the same thing about two thirds of the catalogue cannot
tell you anything about any single product.

Switch on **Verify real stock per product** and each search page makes one extra
batched request to Wildberries' product-card endpoint, which does hold real
per-SKU quantities. Measured on the same 100 headphones:

| | Search field | Card endpoint |
|---|---|---|
| Distinct values per 100 rows | 7 | **22** |
| Distinct values per 300 rows | — | **42** |
| Genuine zero-stock rows found | 0 | **10 per 100** |
| Low-stock numbers (1, 2, 13, 14…) | almost none | **yes** |

So out-of-stock detection and low-stock alerts actually work — which is the
question sellers are really asking.

**The ceiling, stated plainly.** Wildberries clusters a large share of rows on a
single high value, and **that value moves between runs**. Measured: one run
topped out at 46 with 61 of 100 rows on it; a larger run topped out at 52 with
157 of 300 rows on it — same region, same kind of query. Below the ceiling the
numbers vary genuinely (42 distinct values in that 300-row run) and include real
zeros.

So treat a row sitting on the run's maximum as **"at least this many"**, and
anything below it as a real count. Every run's log prints the ceiling it actually
observed, e.g.:

```
stock ceiling observed this run: 52. Wildberries clusters a large share of rows
on the run maximum, and that maximum moves between runs (46 and 52 both seen)...
```

There is deliberately **no** hardcoded cap and no per-row "capped" flag. An
earlier version asserted the ceiling was always 46; on the very next larger run
that mislabelled 161 rows. Reporting the observed maximum per run cannot go stale
in that way.

Every row also carries `stockSource`:

| Value | Meaning |
|---|---|
| `card` | Quantity verified against the product card |
| `search-fallback` | Card lookup was requested but did not cover this SKU — the search value is kept, unverified |
| `search` | Verification was off; this is the unreliable listing value |

**Cost.** One `fetch-detail` event per batch of 50 products, not per product — so
verifying 1,000 products costs 20 extra events, and the per-product price is
unchanged. Off by default, because you should not pay for it unless you want it.

### Sample output

Real rows from a live run, unedited — search term `наушники`, 100 products, stock
verification on:

| title | brand | price | was | disc % | stock | src | rating | reviews |
|---|---|---|---|---|---|---|---|---|
| Наушники проводные совместимы… | STIMAXON | 358 | 960 | 62.7 | 52 | `card` | 4.5 | 14325 |
| Наушники проводные Type-C совм… | `null` | 296 | 2100 | 85.9 | 2 | `card` | 4.9 | 3359 |
| Наушники проводные | Borofone | 203 | 798 | 74.6 | 0 | `card` | 4.7 | 617 |

Those three rows are chosen to show the range: one sitting on the run's ceiling
(52, read as "at least 52"), one genuinely down to its last 2 units, and one
actually out of stock. That is the difference the card lookup makes — without it
all three would have read the same placeholder.

That run: **100 rows, 100 unique SKUs, 0 duplicates, 0 invalid discounts, 100/100
relevant to the query**, 24 distinct stock values, 9 genuine zeros. Every field
populated except `brand`, which 25 sellers left blank at source.

One complete row, every field the Actor emits:

```json
{
  "sku": "337343445",
  "title": "Наушники проводные совместимы с iPhone с микрофоном",
  "brand": "STIMAXON",
  "price": 358,
  "originalPrice": 960,
  "discountPct": 62.7,
  "currency": "RUB",
  "inStock": true,
  "stockQuantity": 52,
  "stockSource": "card",
  "rating": 4.5,
  "reviewCount": 14325,
  "supplier": "STIMAXON",
  "categoryPath": null,
  "url": "https://www.wildberries.ru/catalog/337343445/detail.aspx",
  "marketplace": "wildberries.ru",
  "country": "russia",
  "image": null,
  "description": null,
  "extractedVia": "wb-api",
  "scrapedAt": "2026-08-04T17:05:41.092Z"
}
```

Note the discount depths. Wildberries sellers routinely inflate the list price,
so a 95% markdown is the source's number, not a real saving. The Actor reports
both prices and computes the delta; judging whether the "was" price was ever real
is left to you.

### Use cases

- **Daily price monitoring** — track competitor pricing and react to changes
- **Discount and promotion tracking** — real markdown depth, since both list and
  sale price are returned
- **Stock and low-stock monitoring** — with stock verification on, real per-SKU
  quantities let you spot a competitor down to their last few units, and genuine
  out-of-stock rows instead of a placeholder
- **Repricing feeds** — push live market prices into your own pricing engine
- **Brand and seller monitoring** — watch who is selling your brand and at what price
- **Assortment research** — see what sells in a category and at what price points

### Input

Runs with **zero configuration** — press Start for a sample run.

```json
{
  "site": "wildberries",
  "searchTerms": ["наушники"],
  "maxItems": 1000
}
```

Russian keywords give the best coverage. Each term is paginated until results run
out or `maxItems` is reached. `maxItems` is a hard cap, so your bill stays
predictable.

### Reliability

Most scrapers break because they hardcode CSS class names that die on the next
redesign. This one reads a JSON endpoint and validates every field on the way out:

- **You are never billed for duplicate rows.** Wildberries' own pagination repeats
  products across pages, and overlapping search terms surface the same items. Rows
  are deduplicated by SKU within a run before anything is emitted or charged. If
  you ask for 1,000 products and a keyword only has 600 unique ones, you get 600
  real rows — not 1,000 rows with 400 repeats.
- Items that do not parse are **skipped, never emitted half-filled**
- A drop in the map-success rate raises an explicit warning naming the likely cause
- Rate-limit responses (429) are retried against fresh sessions automatically
- Every run reports its **yield percentage**, so a partially-degraded run is
  visible instead of silently returning half the data
- **Fabricated responses are rejected, not sold to you.** Wildberries does not
  only answer 429. A flagged request can also receive valid-looking JSON
  containing invented products — observed live: a search for `наушники`
  (headphones) that came back carrying a single evening dress while the envelope
  claimed 100 results. Such a payload is discarded and retried against a fresh
  session, so no decoy row is ever emitted or charged. Every run reports
  `decoysRejected=N`.
- **Anti-bot wrappers are recovered, not thrown away.** Some valid responses
  arrive wrapped in an injected `<script>` block. A 127KB response prefixed with
  183 bytes of script still held 100 correct products, so the wrapper is stripped
  and the data kept rather than costing you a retry. Reported as
  `interstitialsStripped=N`.
- **A blocked run fails instead of reporting success.** If every request is
  rate-limited and nothing comes back, the run exits `FAILED` with the reason,
  because an empty dataset marked SUCCEEDED is indistinguishable from a healthy
  run. A search term with genuinely no matches still succeeds — the two cases are
  told apart by whether any request actually failed.
- Prices are validated: a "discount" is only reported when the list price is
  genuinely above the sale price

Wildberries' search endpoint is the one its own storefront uses. It is
unauthenticated and stable in practice, but it is not a documented public API and
carries no compatibility guarantee. The mapping layer is written to degrade into
"zero products plus a clear warning" rather than to emit corrupted rows.

### Also supports noon.com

Set `site` to `noon` for noon.com (UAE / Saudi Arabia / Egypt) in HTML mode. It
works and yields 100% on retry, but it is far slower and more expensive per
product because noon only exposes prices on detail pages and blocks aggressively —
expect residential proxies to be necessary for volume. Wildberries is the
recommended mode.

### No personal data

Product and pricing fields only. No buyer, reviewer, or private-individual data
is collected. `supplier` is a business trading name. `robots.txt` is fetched and
enforced per origin before any request.

### Аналитика и парсер Вайлдберриз — описание на русском

**Парсер Вайлдберриз (Wildberries, ВБ)** — аналитика товаров и продавцов, сбор
данных с wildberries.ru через API. Поиск по
ключевому слову возвращает готовую таблицу: название, бренд, **текущая цена, цена
до скидки, размер скидки в процентах, количество на складе, рейтинг, число
отзывов и продавец**. Также возвращаются SKU (артикул) и ссылка на карточку товара.

Что важно для аналитики Wildberries:

- **Реальные остатки по каждому SKU** — опция «Verify real stock per product».
  В поисковой выдаче Wildberries поле количества для 65–95% товаров возвращает
  одно и то же значение (46–48 в наших замерах) — это не остатки. С включённой
  проверкой мы дополнительно запрашиваем карточку товара: на тех же 100 товарах
  получилось 22 разных значения вместо 7 и 10 реально нулевых остатков вместо 0.
  То есть видно, у кого товар заканчивается или уже закончился.
  **Важно про потолок:** Wildberries собирает значительную часть строк на одном
  высоком значении, и это значение меняется между запусками — в одном прогоне
  максимум был 46 (61 строка из 100), в другом 52 (157 из 300). Значения ниже
  максимума меняются нормально (42 разных значения на 300 строк) и включают
  реальные нули. Поэтому строку, равную максимуму прогона, читайте как
  «не меньше этого», а всё, что ниже — как точное число. Максимум каждого
  прогона пишется в лог.
  Стоимость: одно событие `fetch-detail` на каждые 50 товаров, не за каждый товар.
- **Реальная глубина скидки** — возвращаются обе цены, поэтому скидка считается,
  а не берётся из баннера.
- **Без дублей.** Пагинация Wildberries повторяет товары между страницами.
  Строки дедуплицируются по SKU до оплаты: запросили 1000 товаров, а по запросу
  их всего 600 — получите 600 реальных строк, а не 1000 с повторами.
- **Цена $0.001 за товар.** 1000 товаров ≈ $1.
- Русские ключевые слова дают лучшее покрытие, чем английские.

Типовые задачи: мониторинг цен конкурентов, отслеживание скидок и акций,
контроль остатков, репрайсинг, поиск нишевых товаров, проверка кто продаёт ваш
бренд и по какой цене.

Официального публичного API продаж у Wildberries нет — динамика цен и остатков
остаётся рабочим косвенным сигналом того, как торгует конкурент.

### Local development

```bash
npm install
npm test          # 37 unit tests, no network required
npx apify run
npx apify push
```

# Actor input Schema

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

Keywords to search on Wildberries. Each term is paginated until results run out or Max products is reached. Russian keywords give the best coverage — e.g. наушники (headphones), смартфон (smartphone), кроссовки (sneakers).

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

Hard cap on products returned, so your cost stays predictable. Duplicate products are removed and never counted or charged. 0 = no limit.

## `fetchStockDetail` (type: `boolean`):

Off by default. When on, each search page triggers one extra batched request to the Wildberries product-card endpoint, which is the only source of real per-SKU stock -- the search listing returns a near-constant placeholder for most products. Rows gain a stockSource field: "card" means the quantity was verified, "search-fallback" means the lookup did not cover that SKU. Charged as one fetch-detail event per batch of 50 products, not per product.

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

Parallel requests. Defaults to 1 because Wildberries returns HTTP 429 on parallel requests from datacenter IPs. Raise it only with residential proxies.

## `maxRequestsPerMinute` (type: `integer`):

Throttle to stay under the site's rate limit. 0 = unthrottled. 6 is the measured safe rate for Wildberries from Apify datacenter IPs; 60 reliably triggers HTTP 429 and returns nothing.

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

Datacenter proxies by default, which work on every plan. Residential proxies are not required for this source.

## `debugSaveHtml` (type: `boolean`):

Stores each raw response in the key-value store. Only useful when diagnosing a problem — leave off for normal runs.

## Actor input object example

```json
{
  "searchTerms": [
    "наушники"
  ],
  "maxItems": 100,
  "fetchStockDetail": false,
  "maxConcurrency": 1,
  "maxRequestsPerMinute": 6,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "debugSaveHtml": false
}
```

# Actor output Schema

## `products` (type: `string`):

One row per unique product: price, original price, discount %, stock quantity, rating, reviews and seller.

## `productsCsv` (type: `string`):

Same rows as CSV, for spreadsheets and BI tools.

## `datasetOverview` (type: `string`):

Browse and filter this run's dataset.

# 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 = {
    "searchTerms": [
        "наушники"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mattnews17/wildberries-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 = { "searchTerms": ["наушники"] }

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

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

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

```

## CLI example

```bash
echo '{
  "searchTerms": [
    "наушники"
  ]
}' |
apify call mattnews17/wildberries-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mattnews17/wildberries-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/0J8vLYBCvbmnshAOo/builds/46D9mJRZJR2g9deQi/openapi.json
