# Ozon Category Scraper — Full Listing Export (`thenetaji/ozon-category-scraper`) Actor

Point it at any Ozon category link and export the whole listing, not just the first page. Each product carries its price, discount, star rating and review count as numbers. Enrichment adds the seller, the non-card price, the spec table and every image, per product.

- **URL**: https://apify.com/thenetaji/ozon-category-scraper.md
- **Developed by:** [The Netaji](https://apify.com/thenetaji) (community)
- **Categories:** E-commerce, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.13 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

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

## Ozon Category Scraper

The Actor takes an Ozon category link and exports the products in that listing as structured rows:
`sku`, `title`, `url`, `image_url`, `price`, `original_price`, `currency`, `on_sale`,
`percent_discount`, `rating`, `review_count` and `is_adult`.

It walks the listing rather than reading a single page. Ozon serves its grid eight products at a
time, and the Actor keeps requesting pages until `maxItems` is reached or the listing ends, up to a
ceiling of 300 pages — 2,400 products from one category.

Prices arrive as numbers. Ozon renders them for a Russian storefront — `194 632 ₽`, with a no-break
space between the thousands and a currency glyph rather than a code — and those strings are parsed
before they reach the dataset, so `price` is `194632` and `currency` is `RUB`.

### Accepted input

`category` is required and accepts an Ozon category link, for example
`https://www.ozon.ru/category/smartfony-15502/`. A path such as `/category/smartfony-15502/` is
accepted as well. `maxItems` caps how many products are saved and defaults to 100; setting it to `0`
removes the cap and lets the run continue to the end of the listing or the 300-page ceiling,
whichever comes first.

```json
{
  "category": "https://www.ozon.ru/category/smartfony-15502/",
  "maxItems": 100
}
```

Category links are visible in the address bar while browsing Ozon. Running
[Ozon Search Scraper](https://apify.com/thenetaji/ozon-search-scraper) also prints the category a
keyword resolved to, which can be pasted here directly.

### Response fields

```json
{
  "sku": 3755501937,
  "title": "Apple Смартфон iPhone 17 Pro Max Global 12/1 ТБ, темно-синий",
  "url": "https://www.ozon.ru/product/apple-smartfon-iphone-17-pro-max-3755501937/",
  "image_url": "https://ir.ozone.ru/s3/multimedia-1-4/8542532020.jpg",
  "price": 194632,
  "original_price": 1083750,
  "currency": "RUB",
  "on_sale": true,
  "percent_discount": 82,
  "rating": 4.7,
  "review_count": 128,
  "is_adult": false
}
```

Fields absent from the listing are returned as null rather than omitted, so every row has the same
shape. `percent_discount` is published as a positive number: a product cut by 82% returns `82`, and
`original_price` alongside it is the pre-discount price Ozon struck through.

`sku` is Ozon's own product identifier. `url` has Ozon's per-response tracking token removed, so the
same product produces the same link on every run — which is what makes a category snapshot
comparable against the same category a week later.

### Behaviour while walking a listing

Progress through the listing is driven by what the previous page reports, not by counting rows. A
page returning fewer products than requested does not end the run; only the end of the listing does.
This matters for a run whose last page happens to land exactly on `maxItems`, which a count-based
walk would treat as the end of a category that in fact continues.

Neighbouring pages sometimes overlap, repeating a handful of products. Duplicates are removed within
a run by product URL, so a run's row count can be lower than the number of pages requested times
eight.

### Questions

**Why did the run save fewer products than the category shows on the website?**
Two limits apply. The listing itself ends, and the Actor stops there. Above that, 300 pages — 2,400
products — is the ceiling, so a category larger than that is truncated rather than continuing.
Duplicate products removed across overlapping pages also reduce the total slightly.

**Does the order match what the website shows?**
The products are returned in Ozon's own listing order for that category. Filters and sort options
applied in a browser are part of the category link, so a link copied with them applied is honoured;
a bare category link returns Ozon's default ordering.

**Are sponsored products included?**
The Actor returns the product tiles from the category grid. Banners, filter rails, menus and the
other widgets that surround the grid on the website are not products and are not returned.

**What happens when Ozon interrupts a run?**
Ozon challenges a minority of new sessions. A challenged request is retried on a fresh session, and
a run that still cannot get past it ends with the products already collected rather than failing —
the dataset is short, not empty. The page it stopped at is where a rerun resumes from.

**Why is `price` lower than the price Ozon shows me?**
It is not — it is the price Ozon shows by default, which assumes payment with an
Ozon card. The standard price is higher and lives only on a product's own page,
so it arrives as `list_price` when enrichment is on.

**Can several categories be exported in one run?**
No. One run takes one category link. Multiple categories are separate runs, which also keeps their
datasets separable for comparison.

### Enriching with product details

`enrichProductDetails` opens each product's own page and merges what a listing
cannot show onto its row. It is off by default, costs one extra request per
product, and is billed per product enriched — only after the lookup succeeds.

Eleven fields are added: `seller_name`, `seller_url`, `seller_id`,
`list_price`, `brand`, `description`, `in_stock`, `images`, `characteristics`,
`category_path` and `variants`.

The seller is the reason most runs turn it on. Ozon is a marketplace, several
shops sell the same product, and a listing tile names none of them — so a
listing export alone cannot answer who is selling what.

**`list_price` is the other reason, and it is easy to miss.** Ozon quotes two
live prices: the discounted price for paying with an Ozon card, and the standard
price everyone else pays. Search and category listings show only the first, so
`price` on an unenriched row understates what most buyers are charged.
`list_price` is the standard price, and `price` is left untouched so the two can
be compared rather than one silently replacing the other.

`rating` and `review_count` are taken from the product page when it has them.
A listing tile rounds the review count and frequently omits it, so the product
page is the better source for the same two fields.

A product whose page cannot be reached, or that has been delisted, keeps its
listing row unchanged and is not billed for the enrichment.

### Related Actors

[Ozon Search Scraper](https://apify.com/thenetaji/ozon-search-scraper) starts from a keyword instead
and prints the category it resolved to. It is the better starting point when the category is not yet
known.

# Actor input Schema

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

An Ozon category link, for example https://www.ozon.ru/category/smartfony-15502/. Run a search first and the run log prints the category it resolved to, which you can paste here to go straight to the listing.

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

Maximum number of products to save. Set 0 for no limit.

## `enrichProductDetails` (type: `boolean`):

Open every product's own page and add what a listing cannot show: the seller and their storefront, the standard price paid without an Ozon card, the brand, the full description, stock status, every gallery image, the specification table, and the other sizes and colours of the same product. One extra request per product — slower, and billed per enriched product.

## Actor input object example

```json
{
  "category": "https://www.ozon.ru/category/smartfony-15502/",
  "maxItems": 24,
  "enrichProductDetails": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

All records scraped by this run

# 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 = {
    "category": "https://www.ozon.ru/category/smartfony-15502/",
    "maxItems": 24
};

// Run the Actor and wait for it to finish
const run = await client.actor("thenetaji/ozon-category-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 = {
    "category": "https://www.ozon.ru/category/smartfony-15502/",
    "maxItems": 24,
}

# Run the Actor and wait for it to finish
run = client.actor("thenetaji/ozon-category-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 '{
  "category": "https://www.ozon.ru/category/smartfony-15502/",
  "maxItems": 24
}' |
apify call thenetaji/ozon-category-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thenetaji/ozon-category-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/FTD0wWfy3OdxGaDiL/builds/Qx0DindS4UwdwJief/openapi.json
