# Ozon Products Scraper (`nice_dev/ozon-products-scraper`) Actor

Scrape Ozon.ru products by keyword, category, seller or brand URL: card price, price without Ozon card, old price, rating, reviews, seller, stock. Optional full product page, reviews, Q\&A, other sellers and price tracking. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/ozon-products-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** E-commerce, MCP servers
- **Stats:** 3 total users, 1 monthly users, 92.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

### 🛒 What is Ozon Products Scraper?

**Ozon Products Scraper** extracts **products from [Ozon.ru](https://www.ozon.ru)**, Russia's largest marketplace: **title, Ozon Card price, old price and discount, rating, reviews count, stock, delivery date and brand** (and, with **Full product page**, the seller, the price without the card, price per unit, instalment, promotion and number of questions), for any keyword, category, seller or brand page, or a list of product SKUs. It works as an **Ozon API alternative**: run it on a schedule, call it from your code, or plug it into Make, Zapier or n8n.

Type a **keyword** (`наушники` or `iphone 15`), paste any Ozon URL or SKU, click **Start**, and download the products in JSON, CSV or Excel. No login, no Ozon account and nothing to set up. Turn on **Full product page** for the seller's legal details, all characteristics, the description, variants and delivery options, and add the customer reviews, questions, other sellers or price tracking only when you need them.

### 📋 What data can you extract from Ozon?

One item per product, 89 fields:

| Category | What you get |
| --- | --- |
| 🏷️ **Product** | name, link, SKU, brand, category, Ozon labels (`Распродажа`, `Оригинал`), 18+ flag |
| 💰 **Prices** | Ozon Card price, old price, discount %, `1338 RUB`; with **Full product page**: price with any other card, price per unit, instalment offer, running promotion |
| ⭐ **Ratings** | average rating, number of reviews, rank in the search; number of questions with **Full product page** |
| 📦 **Stock and delivery** | in stock or not, units left, most units per order, delivery date for your city, express delivery, every way of delivery with its cost, warehouse it ships from, return terms |
| 🏪 **Seller** | shop name, link, rating, orders count, logo, legal name, registration number, legal address, working hours |
| 📝 **Product page** | full description, rich description and its pictures, all characteristics, nutrition facts of food, photos and videos, hashtags, variants (colour / size with their SKUs and prices), "bought together" and "recommended" products |
| 💬 **Customer reviews** (option) | rating, text, pros, cons, date, author first name, photos, videos, likes, comments and the seller's answer; number of reviews per star |
| ❓ **Questions** (option) | question, author, date, likes, the answers shown under it (seller or buyers) |
| 🛍️ **Other sellers** (option) | the other offers of the same product: seller, rating, price, delivery date, link |
| 📈 **Price tracking** (option) | average price of last month when Ozon shows it, price changes between your runs |

Every field, with an example, is listed in the **Output** section below.

The search card gives the prices, rating, stock, delivery date and labels. The seller, the description, the characteristics and the other columns of the product page are filled when **Full product page** (`extractDetails`) is on (off by default). A product you paste as a URL or a SKU is always read from its product page.

The options all need **Full product page** and are off by default; each one is charged only when it delivers something:

- 💬 **Customer reviews** (`includeReviews`): the reviews of each product, up to **Max reviews per product**, with the number of reviews per star and the number of customer photos and videos. Charged per review (`review`).
- 🗨️ **Review comments** (`includeReviewComments`, with Customer reviews): the comments under each review (up to 50 per review, on up to 30 requests per product), the seller's answer included. Charged per review whose comments are delivered (`review-comment`).
- ❓ **Customer questions** (`includeQuestions`): questions and their answers, up to **Max questions per product**. Charged per question (`question`).
- 🛍️ **Other sellers** (`includeOtherSellers`): the other offers of the same product, the 100 cheapest offers Ozon lists, with each seller's rating and legal details. Charged per product that has other offers (`other-sellers`).
- 🏪 **Seller profile** (`includeSellerProfile`): followers, reviews count, Premium mark, badges, time on Ozon and verified brands of the shop. Charged per seller read (`seller-profile`): each seller is read only once in a run, the next products of the same seller get its profile free.
- 📉 **Price history** (`includePriceHistory`): for a product Ozon marks `Стало дешевле`, the price now and the average price of the month before. Ozon shows no full price curve, so there is none. Charged per product that has it (`price-history`).

### ✅ Why use Ozon Products Scraper?

- 💰 **Every price Ozon shows**: Ozon Card price, price with any other card, old price and discount, side by side, in roubles or in 25 other currencies.
- 🎯 **Any starting point**: keywords, or search, category, seller, brand and product URLs (a start URL replaces the keywords), plus a plain list of SKUs, with a cap per search.
- 🏪 **Seller details for B2B**: legal name, registration number, legal address and working hours of each shop, plus its full profile as an option.
- 🔍 **Ozon's own filters in the form**: price range, discount, sale, verified brand, official stores, sellers, brands, delivery time, instalments — and filters Ozon does not have: minimum rating, reviews, discount, required or excluded keywords.
- 🔔 **Monitoring built in**: tick **Only new products**, schedule the Actor, and each run returns (and charges) only what it has never delivered before — plus, if you want, the products whose price changed.
- 🌍 **Your city, your language**: prices, delivery dates and stock for the delivery city you choose; product texts in 10 languages.
- 🔌 API, scheduling, monitoring, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape Ozon

1. Create a free Apify account.
2. Open **Ozon Products Scraper** and type a **Search keyword** (e.g. `наушники`), or more in **More search keywords**.
3. Or paste your own Ozon URLs into **Start URLs** (search, category, seller, brand or product pages: the filters and sorting set on the site are kept, pagination is automatic), or SKUs into **Product SKUs**.
4. Set **Max products** (100 by default, 0 = no limit) — and **Max products per search** when you run several searches — tick **Full product page** and the extras you need, then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape Ozon?

This Actor uses **pay per event** pricing: you pay for the products saved, and for each extra only when you turn it on and it delivers something. The prices of each event are in the **Pricing** tab:

- `product`: a product saved from its search card;
- `product-details`: a product saved with its product page read (with **Full product page**, or a product pasted as a URL or a SKU) — charged instead of `product`, not on top of it;
- `review`, `review-comment`, `question`, `other-sellers`, `seller-profile`, `price-history`: the extras, as described above;
- `price-change`: a product that comes back because its price moved (on top of `product`);
- `filter-check`: a product the Actor's own filters (minimum rating, reviews, discount, keywords, and the price range when Ozon lets a product outside it through) read and drop — a dropped product costs this small fee instead of its normal price, so filtering never costs more than taking everything;
- a small fee per run start (one per GB of memory: two at the default 2 GB).

Platform usage (compute, proxy) is included in the price. Cap a run with **Max products** and, through the API, with the run option `maxTotalChargeUsd`.

### ⚙️ Input

```json
{
    "query": "наушники",
    "maxItems": 200,
    "sortBy": "rating",
    "minRating": 4.5
}
```

Several searches, a cap per search, the product page with its reviews, in English and US dollars, for Novosibirsk:

```json
{
    "searchQueries": ["наушники", "iphone 15"],
    "maxItemsPerQuery": 50,
    "extractDetails": true,
    "includeReviews": true,
    "maxReviewsPerProduct": 30,
    "language": "en",
    "currency": "USD",
    "deliveryCity": "Новосибирск"
}
```

Your own URLs and SKUs, scheduled, with price changes:

```json
{
    "startUrls": [
        { "url": "https://www.ozon.ru/category/naushniki-15547/" },
        { "url": "https://www.ozon.ru/brand/apple-26303000/?sorting=new" }
    ],
    "productIds": ["244466211"],
    "maxItems": 500,
    "onlyNew": true,
    "includePriceChanges": true,
    "stateKey": "headphones-watch"
}
```

| Field | Notes |
| --- | --- |
| `query`, `searchQueries` | Keywords as typed in the Ozon search bar, Russian or English (`наушники`, `iphone 15`): one search per keyword. A product found by several searches is saved once. Ignored when `startUrls` is set. |
| `startUrls` | Ozon search, category, seller, brand or product pages (`https://www.ozon.ru/category/naushniki-15547/`, `https://www.ozon.ru/brand/apple-26303000/`). Filters and sorting set on the site are kept, pagination is automatic. The Site filters of the form (price, discount, delivery…) are added to search and category pages, never over a filter already in the URL; seller, brand and product pages take none. Up to 1,000 URLs. |
| `productIds` | Ozon SKUs, the `Артикул` of a product page (`244466211`): each one is read like a product URL. Up to 1,000. |
| `maxItems` | Products to save for the whole run, after deduplication and filters (`0` = no limit). |
| `maxItemsPerQuery` | Cap for EACH search (keyword, or search / category / seller / brand URL), so that the first search cannot use up the whole budget (`0` = no per-search cap). |
| `sortBy` | Order of the results: `score` (Ozon's default), `rating`, `price` (cheapest first), `price_desc`, `new` (newest first), `discount`. Ignored for URLs that already carry a sorting. |
| `minPrice`, `maxPrice` | Ozon Card price range, in roubles. Each product is also checked one by one: a product Ozon lets through whose price is outside the range is dropped (charged as a filter check), and so is one with no price (sold out). |
| `hasDiscount`, `onSale`, `brandCertified`, `officialStoresOnly`, `isInstallment` | Ozon's own filters: discounted products, sale products, verified brand, official brand stores, instalments. |
| `sellerIds`, `brandIds` | Only the products of these sellers or brands: the number at the end of a seller or brand URL (`822`; for sellers `0` = Ozon itself). |
| `delivery` | Delivery time to the delivery city: `any` (default), `express`, `today`, `tomorrow`, `3days`, `7days`. |
| `language` | Language of the product texts: `ru` (default), `en`, `zh-Hans`, `kk`, `uz-Latn`, `tr`… Ozon translates what it can. |
| `currency` | Currency of the prices: `RUB` (default), `USD`, `KZT`, `BYN`, `CNY`… Ozon gives approximate amounts in any currency but the rouble. |
| `deliveryCity` | City whose pickup point Ozon uses for the prices, delivery dates and stock, as written on Ozon (`Новосибирск`). Empty = Moscow (set on every IP, whatever city the IP is in). |
| `extractDetails` | **Full product page**: seller and its legal details, all characteristics, description, variants, delivery options, return terms and the other columns of the product page. Charged per product read this way, instead of the search-card price. |
| `includeOtherSellers` | Needs **Full product page**. The other offers of the same product, the 100 cheapest Ozon lists. |
| `includeReviews`, `maxReviewsPerProduct`, `reviewSort` | Needs **Full product page**. Customer reviews, up to the max per product (20 by default), in this order: `usefulness_desc` (default), `score_desc`, `score_asc`. |
| `includeReviewComments` | Needs **Customer reviews**. The comments under each review, with the seller's answer. |
| `includeQuestions`, `maxQuestionsPerProduct` | Needs **Full product page**. Customer questions and their answers, up to the max per product (10 by default). Ozon shows questions in Russian only: with another **Language** the option returns none (a warning says so). |
| `includeSellerProfile` | Needs **Full product page**. The profile of each seller, read only once in a run. |
| `includePriceHistory` | Needs **Full product page**. The average price of the month before, for the products Ozon marks as cheaper. |
| `excludeKeywords`, `includeKeywords` | Drop the products whose title contains one of these words (`чехол`), or keep only those that contain one of them (case and accents ignored). |
| `minRating`, `minReviews`, `minDiscount` | Drop the products rated below this (`4.5`), with fewer reviews, or with a smaller discount. Products with no rating (or no discount) yet are dropped too. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the products never delivered under this memory key (`headphones-watch`); give each schedule its own key. `resetState` forgets the memory once. |
| `includePriceChanges`, `minPriceChangePct` | Need **Only new products**. Bring back a product already delivered when its price moved by at least this percent. |
| Advanced | `proxyConfiguration` (keep the default: the proxy is included in the price), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

A real item, read with **Full product page** on and shortened (the full item has all 89 fields; the extras are empty here because their options were off):

```json
{
    "id": "244466211",
    "sku": 244466211,
    "url": "https://www.ozon.ru/product/kofe-v-zernah-italco-crema-italiano-1-kg-244466211/",
    "title": "Кофе в зернах Italco Crema Italiano 1 кг",
    "brand": "Italco",
    "brandId": "35151272",
    "price": 1338,
    "priceWithoutCard": 1487,
    "originalPrice": 2387,
    "discountPercent": 43,
    "currency": "RUB",
    "isAvailable": true,
    "rating": 4.8,
    "reviewCount": 56789,
    "questionCount": 107,
    "deliveryText": "Завтра",
    "badges": ["Вау-цены"],
    "isSponsored": null,
    "images": [
        "https://ir-20.ozone.ru/s3/multimedia-1-1/14554676665.jpg",
        "https://ir-20.ozone.ru/s3/multimedia-1-9/15105056769.jpg"
    ],
    "categoryId": "31009",
    "categoryPath": ["Продукты питания", "Чай, кофе и какао", "Кофе", "В зернах"],
    "sellerName": "Фирменный магазин Италко",
    "sellerId": "822",
    "sellerUrl": "https://www.ozon.ru/seller/ooo-italko-riteyl/",
    "sellerRating": 4.8,
    "shortCharacteristics": {
        "Тип": "Кофе в зернах",
        "Степень обжарки": "Средняя",
        "Состав кофе": "Арабика/Робуста"
    },
    "position": 1,
    "searchUrl": "https://www.ozon.ru/brand/italco-35151272/?page=1",
    "status": "active",
    "sellerLegalName": "ООО \"ИТАЛКО РИТЕЙЛ\"",
    "sellerRegistryNumber": "1097746499190",
    "sellerOrdersCount": 1500000,
    "stockQuantity": 187,
    "maxOrderQuantity": 2000,
    "expressDelivery": true,
    "deliveryRegion": "Москва",
    "installment": "без % до 1 января",
    "promoLabel": "Распродажа",
    "pricePerUnit": 134,
    "unitMeasure": "за 100 гр",
    "nutrition": { "basis": "на 100 г", "kcal": 200.6, "protein": 13.9, "fat": 14.4, "carbs": 4.1 },
    "scrapedAt": "2026-10-15T12:00:00.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 89 fields

| Fields | What you get |
| --- | --- |
| `id`, `sku`, `url`, `title` | **Product**: the SKU as text and as a number, the clean product link, the name |
| `brand`, `brandId`, `brandUrl`, `brandLogo` | brand name, Ozon brand id, brand page, brand logo |
| `categoryId`, `categoryPath`, `categoryUrl`, `categoryTags` | Ozon category id, breadcrumbs, category page, tag links under the product |
| `badges`, `isAdult`, `isSponsored` | Ozon labels of the card (`Распродажа`, `Стало дешевле`), 18+ flag; `isSponsored` is always `null` (Ozon does not expose paid placements) |
| `price`, `priceWithoutCard`, `originalPrice`, `discountPercent`, `currency` | **Prices**: Ozon Card price, price with any other card, price before discount, discount %, currency code (`1338`, `1487`, `2387`, `43`, `RUB`) |
| `pricePerUnit`, `unitMeasure` | price of one unit of measure and that unit (`134`, `за 100 гр`) |
| `installment`, `promoLabel`, `promoEndsIn`, `promoUrl` | pay-in-parts offer, running promotion, time left in it, its page |
| `rating`, `reviewCount`, `questionCount`, `position` | **Ratings**: average rating 1-5, reviews and questions count, rank in the search |
| `isAvailable`, `stockLeft`, `stockQuantity`, `maxOrderQuantity` | **Stock**: in stock or not, units left as the card shows it, units available for the offer (`null` from 800 up: Ozon sends no larger figure), most units per order |
| `deliveryText`, `expressDelivery`, `deliveryRegion`, `deliveryOptions`, `shipsFrom` | **Delivery**: delivery date (`Завтра`), express delivery, city used, every way of delivery with its date and cost, warehouse it ships from |
| `returnPolicyText`, `returnPolicyUrl` | return terms and the page that explains them |
| `sellerName`, `sellerId`, `sellerUrl`, `sellerLogo`, `sellerRating` | **Seller**: shop name, id, page, logo, rating |
| `sellerOrdersText`, `sellerOrdersCount` | orders of the shop as Ozon prints them (`1.5 M`) and as a number |
| `sellerLegalName`, `sellerRegistryNumber`, `sellerAddress`, `sellerWorkingHours` | legal name, state registration number, legal address and country, working hours |
| `sellerFollowers`, `sellerFollowersText`, `sellerReviewCount`, `sellerReviewCountText` | **Seller profile** option: followers and reviews of the shop, as numbers and as Ozon prints them |
| `sellerIsPremium`, `sellerBadges`, `sellerOnOzonSince`, `sellerConfirmedBrands` | Premium mark, badges, time on Ozon (`8 лет`), brands Ozon checked for the shop |
| `images`, `videos` | photo URLs (all gallery photos with Full product page), seller video URLs |
| `description`, `richDescription`, `descriptionImages` | **Product page**: full text, the seller's rich content, its pictures |
| `characteristics`, `shortCharacteristics`, `nutrition`, `hashtags` | all characteristics (name → value), the `О товаре` block, nutrition facts of a food product, seller hashtags |
| `variants`, `recommendedProducts` | other colours / sizes with their SKU, price and stock; "bought together" and "recommended" products |
| `otherSellers`, `otherSellersCount` | **Other sellers** option: each offer of another seller, how many |
| `otherSellersMinPrice` | **Full product page**: the cheapest price of another seller, when the product page says one sells it for less (empty otherwise, whatever the Other sellers option) |
| `reviews`, `reviewsDelivered`, `ratingBreakdown`, `reviewMediaCount` | **Customer reviews** option: the reviews (with comments and the seller's answer when asked), how many, reviews per star, customer photos and videos count |
| `questions`, `questionsDelivered` | **Questions** option: questions with their answers, how many |
| `priceHistory` | **Price history** option: the price now, the average price of the month before, the saving |
| `previousPrice`, `priceChange`, `priceChangePct`, `priceChangedAt`, `status` | **Monitoring**: earlier price, change and change %, date of the run that saw it; `status` is always `active` |
| `searchQuery`, `searchUrl`, `scrapedAt` | keyword or results page that found the product, ISO timestamp of the scrape |

### 💡 Tips

#### How to get more results

Set `maxItems` to `0`, use a broad keyword or a category URL, and add more keywords in `searchQueries` or more URLs in `startUrls`: each one is a search of its own, read page after page, and a product found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap: without it the first searches can use up the whole `maxItems` budget.

#### How to reduce costs

The levers are `maxItems`, `maxItemsPerQuery`, the filters (a dropped product costs only the filter fee, lower than a product) and `onlyNew` for recurring runs (you never pay twice for the same product). Leave **Full product page** off when the search card is enough: prices, rating, reviews count, stock and delivery date are already there. Turn on only the extras you use, and keep **Max reviews per product** and **Max questions per product** low.

#### Delivery city, language and currency

Ozon gives prices, delivery dates and stock for a city: leave **Delivery city** empty for Moscow (set on every IP, whatever city the IP is in), or type the city as written on Ozon (`Новосибирск`). A city Ozon does not find is not guessed: no product is read for another city, the requests fail at once and the run's last message says "Delivery city … not found on Ozon — try the city name in Russian, e.g. Новосибирск". **Language** changes the product texts (Ozon translates what it can, some texts stay in Russian), **Currency** the prices (approximate amounts; the order itself is charged in roubles).

#### Monitoring: only the new products

Tick **Only new products** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the products already delivered: they are not saved, not charged, and their page is not even opened. The memory lives in a named key-value store of your account (`ozon-products-scraper-seen`, up to 150,000 products per key) and is only updated with products that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's products), and tick `resetState` once to start over. With **Sort by** `new`, a search stops once it meets 180 products in a row you already have (fewer under a small cap); with another order, it stops after 20 results pages in a row with nothing new.

#### Price changes

With **Only new products**, tick **Report price changes** (`includePriceChanges`): a product already delivered comes back when its search-card price moved since the run that delivered it, with `previousPrice`, `priceChange`, `priceChangePct` and `priceChangedAt` filled. It comes back as its search card: its product page and the extras are not read or charged again. The first run after you tick it learns the prices and reports no change; so does the first run after you change the **Delivery city** or the **Currency** (prices are compared for the same city and currency only). A search that stops early because it found nothing new (see above) does not read its deeper pages, so a price change there is seen by a later run that goes that far, not by this one.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per Ozon product. Actor id: `nice_dev/ozon-products-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/ozon-products-scraper`.

Smallest input, for a cheap first call:

```json
{
    "query": "наушники",
    "maxItems": 10
}
```

Key output fields: `url`, `title`, `price`, `priceWithoutCard`, `originalPrice`, `rating`, `reviewCount`, `deliveryText`, and `sellerName` with `extractDetails`.

Cost: pay per event, per product saved (see the **Pricing** tab). Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Ozon?

The Actor only reads what Ozon shows publicly to any anonymous visitor. It logs in to nothing. Results can contain personal data (first names of reviewers, names and addresses of sole-trader shops), which is protected by data protection laws such as the GDPR and Russia's personal data law: do not store it without a legitimate reason. You are responsible for using the data in compliance with Ozon's Terms of Use and applicable law. This Actor is not affiliated with Ozon.

#### Does it need a login or a proxy?

No login and no Ozon account. The proxy is included in the price: leave the default setting (your own proxies, if you give some, must be Russian). A request Ozon turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds).

**Apify free plan:** the included proxy is for paying Apify plans only. On the free plan, add your own proxy URLs in **Proxy configuration**; without them the run stops at once with a message saying so.

#### Is the data safe to open in Excel or to show on a web page?

Titles, descriptions, reviews and answers are the sellers' and buyers' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@` (a title such as `-20% today`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. `richDescription` can hold the seller's own HTML, which the Actor does not clean: on a web page, escape every field like any text written by a stranger.

#### Known limitations

- A field Ozon does not show for a product is empty (`null` or `[]`): many products have no old price, no units-left figure, no video or no rich description; `shipsFrom` stays empty on the English site.
- The columns of the product page are empty when **Full product page** is off.
- `priceHistory` is the average price of the month before, only for the products Ozon marks `Стало дешевле`: Ozon gives no dated price curve.
- The date of a question is the day only, with no time.
- Reviews: Ozon serves at most 660 reviews per product and sort order (22 pages of 30). Ask for more and you get 660, the run summary says so; another `reviewSort` shows other reviews.
- Review comments: at most 50 comments per review and 30 comment requests per product; the reviews or comments left are saved without them and the run summary says so.
- Other sellers: at most the 100 cheapest offers Ozon lists (a popular product can have more than 250).
- With **Currency** other than `RUB`, the prices are Ozon's approximate amounts; the **Min price** and **Max price** filters are in roubles.
- `position` is approximate on seller and brand pages.
- Two runs sharing the same `stateKey` at the same time may both return the same new product.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the products already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the products it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the products of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the products saved, filtered out and no longer on Ozon (removed while the run was reading them), and the requests that failed after every retry. Those requests and the removed products are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If Ozon changes its pages, you are told instead of paying for blank rows. A results page that counts products but gives none the Actor can read is an error (listed in `FAILED_REQUESTS`), never a quiet "No products found". If the first 20 products read all lack their title, their price (the search card's, or the product page's of a product in stock), their rating or reviews count (when the other one is shown), or — next to a crossed-out price — their discount, the run saves nothing more, stops and fails, and its last message names the missing field: at most those first products are charged. A run of fewer products (a scheduled `onlyNew` run that finds a few new ones, a small **Max products**) is checked at its end, from 5 products: its products are already saved, and the run fails with the missing field instead of ending green.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

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

Ozon.ru URLs: search (`https://www.ozon.ru/search/?text=...`), category (`/category/...`), seller (`/seller/...`), brand (`/brand/...`), or product (`/product/...`). Filters and sorting set on the site are kept, pagination is automatic. When this list is not empty, **Search keyword** and **More search keywords** are ignored; caps, post-filters and monitoring still apply. Max 1 000 URLs.

## `query` (type: `string`):

Search text, as typed in the Ozon search bar (Russian or English, e.g. `наушники` or `iphone 15`).

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

Several keywords in one run: one search per keyword, added to **Search keyword**. A product found by several searches is saved once.

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

Ozon product numbers (SKU, the `Артикул` shown on a product page, e.g. `1234567890`). Each one is read like a product URL. Max 1 000.

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

Maximum number of products to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (keyword, or search / category / seller URL), so that the first search cannot use up the whole **Max products** budget. 0 = no per-search cap.

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

Order of the search results on Ozon. Ignored for the URLs that already carry a sorting. **Only new products** does not change it: with *Newest first* a monitoring run stops reading once it meets a long run of products it already delivered.

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

Lowest Ozon Card price, in roubles.

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

Highest Ozon Card price, in roubles. Leave empty for no maximum.

## `hasDiscount` (type: `boolean`):

Ozon filter `Товары со скидкой`: only products with a crossed-out price.

## `onSale` (type: `boolean`):

Ozon filter `Распродажа`: only products in the current sale.

## `brandCertified` (type: `boolean`):

Ozon filter `Бренд проверен`: the seller guarantees original products of this brand.

## `officialStoresOnly` (type: `boolean`):

Ozon filter `Официальные магазины бренда`.

## `sellerIds` (type: `array`):

Only products sold by these sellers (Ozon seller id, the number at the end of a `/seller/...` URL; `0` = Ozon itself).

## `brandIds` (type: `array`):

Only products of these brands (Ozon brand id, the number at the end of a `/brand/...` URL).

## `delivery` (type: `string`):

Ozon filter `Срок доставки`: only products delivered within this time (to the **Delivery city**).

## `isInstallment` (type: `boolean`):

Ozon filter `Рассрочка`: only products that can be paid in parts.

## `language` (type: `string`):

Language Ozon shows the product texts in (title, description, characteristics). Ozon translates what it can.

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

Currency the prices are given in. Ozon shows approximate amounts in any currency but the rouble (the order itself is charged in roubles).

## `deliveryCity` (type: `string`):

City whose pickup point Ozon uses for the prices, delivery dates and stock, as written on Ozon (e.g. `Новосибирск`). Empty = Moscow (set on every IP, whatever city the IP is in).

## `extractDetails` (type: `boolean`):

Open each product page (2 extra requests) for the seller, stock left, all characteristics, description, category path, brand, variants (colour / size with their SKUs), video, questions count and the cheapest offer of another seller. Charged per product read this way (event `product-details`).

## `includeOtherSellers` (type: `boolean`):

Needs **Full product page**. The offers of the same product by other sellers (the 100 cheapest the site lists): seller with rating and legal details, price, delivery date, link. 1 extra request per product; charged per product that has other offers (event `other-sellers`).

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

Needs **Full product page**. Customer reviews of each product: rating, text, pros, cons, date, author first name, photos, videos, likes, star counts. 1 extra request per 30 reviews. Charged per review delivered (event `review`).

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

Used with **Customer reviews** (30 reviews per page read).

## `reviewSort` (type: `string`):

Used with **Customer reviews**: which reviews come first (Ozon sorting).

## `includeReviewComments` (type: `boolean`):

Needs **Customer reviews** (ignored without it). Adds the comments under each review, with the seller's answer: `sellerResponse` and `comments`. At most 30 comment requests per product and 50 comments per review (the rest of a product's reviews, or of a review, is saved without them; the run summary says so). 1 extra request per review that has comments; charged per review whose comments are delivered (event `review-comment`).

## `includeQuestions` (type: `boolean`):

Needs **Full product page**. Customer questions of each product with the answers shown under them (seller or buyers): text, author, date, likes, best answer. 1 extra request per 10 questions; charged per question delivered (event `question`). Ozon shows questions in Russian only: with another **Language** there are none.

## `maxQuestionsPerProduct` (type: `integer`):

Used with **Customer questions**.

## `includeSellerProfile` (type: `boolean`):

Needs **Full product page**. Opens the page of each seller once per run for its profile: followers and reviews count (`sellerFollowers`, `sellerReviewCount`), Premium mark, badges, time on Ozon and verified brands. The next products of the same seller get the profile at no charge. Charged per seller read (event `seller-profile`).

## `includePriceHistory` (type: `boolean`):

Needs **Full product page**. For a product Ozon marks `Стало дешевле`: the price now and the average price of the month before (`priceHistory`). Ozon gives no full price curve. 1 extra request per product; charged per product that has it (event `price-history`).

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

Drop the products whose title contains one of these words (case and accents ignored).

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

Drop the products rated below this (1-5 stars). Products with no rating yet are dropped too.

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

Drop the products with fewer reviews than this.

## `minDiscount` (type: `integer`):

Drop the products whose discount is below this percent (crossed-out price vs Ozon Card price). Products with no discount are dropped too.

## `includeKeywords` (type: `array`):

Keep only the products whose title contains at least one of these words (case and accents ignored).

## `onlyNew` (type: `boolean`):

Skip the products that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and their page is not even opened. First run = everything is new.

## `includePriceChanges` (type: `boolean`):

Needs **Only new products**. A product already delivered under this **Memory key** comes back when its search-result price has changed since the run that delivered it: `previousPrice`, `priceChange`, `priceChangePct` and `priceChangedAt` are filled. It comes back as its search-result card: its product page and the extras (reviews, questions, other sellers, seller profile, price history) are not read or charged again. Charged per product as **Price change**, on top of the product; a product whose price did not move is skipped and costs nothing. The first run after you tick it learns the prices and reports no change.

## `minPriceChangePct` (type: `number`):

Used with **Report price changes**: smaller moves (up or down, in percent) are ignored. 0 = every change.

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

Name of the memory used by **Only new products**. Give each schedule / task its own key (e.g. `headphones-watch`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every product again. Untick it afterwards.

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

Keep **Apify Proxy**: the Actor reaches Ozon through Russian IP addresses, included in the price (the proxy group, the country and **No proxy** chosen here are ignored). Your own proxies (`proxyUrls`), if you give some, must be Russian. On the Apify free plan the included proxy is not available: add your own proxy URLs, or the run stops at once with a message.

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

Ozon pages read in parallel.

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

Most requests to Ozon in any 60 seconds, all of them counted: results pages, product pages, reviews and other sellers. A budget, not an even pace (spread them with the minimum delay below). Lower it if the log shows Ozon turning requests away.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. A request Ozon turns away is also retried on a new proxy session up to 10 times without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "query": "наушники",
  "searchQueries": [],
  "productIds": [],
  "maxItems": 50,
  "maxItemsPerQuery": 0,
  "sortBy": "score",
  "hasDiscount": false,
  "onSale": false,
  "brandCertified": false,
  "officialStoresOnly": false,
  "sellerIds": [],
  "brandIds": [],
  "delivery": "any",
  "isInstallment": false,
  "language": "ru",
  "currency": "RUB",
  "extractDetails": false,
  "includeOtherSellers": false,
  "includeReviews": false,
  "maxReviewsPerProduct": 20,
  "reviewSort": "usefulness_desc",
  "includeReviewComments": false,
  "includeQuestions": false,
  "maxQuestionsPerProduct": 10,
  "includeSellerProfile": false,
  "includePriceHistory": false,
  "excludeKeywords": [],
  "includeKeywords": [],
  "onlyNew": false,
  "includePriceChanges": false,
  "minPriceChangePct": 0,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 4,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (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 = {
    "query": "наушники",
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "query": "наушники",
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

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

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

```

## CLI example

```bash
echo '{
  "query": "наушники",
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/ozon-products-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/MoFDd4LrU2jm9guCW/builds/7hzb0LnYALYG0482h/openapi.json
