# 🧪 Avito Scraper — Listings by City and Category (`thenetaji/avito-listings-scraper`) Actor

Export any Avito city-category — cars, flats, phones — as rows with the title, the price as a number and as Avito writes it, the address and map coordinates, the photos, the seller's name and rating, and Avito's own attribute line. Repeated listings are dropped as the walk goes.

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

## Pricing

from $4.25 / 1,000 listings

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

## Avito Scraper

Every listing in one Avito city-category, exported as a spreadsheet. Each row carries the
headline, the price both as a number and in Avito's own wording, the city and the
street-level address, the map coordinates, the photographs, the seller's name, page and
star rating, and Avito's own one-line attribute summary for the category — mileage, engine
and body for a car, area and floor for a flat.

A city-category is the unit. `/moskva/avtomobili` is cars in Moscow and
`/sankt-peterburg/avtomobili` is cars in St Petersburg; both can be listed in one run and
each gets its own allowance of results. The walk pages forward through the category and
drops listings it has already saved, because a live classifieds inventory reorders itself
while you are reading it.

### Accepted input

`categoryUrls` is required and takes one Avito category address per line, either as a full
link such as `https://www.avito.ru/moskva/avtomobili` or as its path, `/moskva/avtomobili`.
A pasted link is reduced to its path and any `?context=` query on it is removed — that is a
search token bound to the search that produced the link, it expires, and the bare path
serves the same page.

A per-listing address is refused rather than attempted, with a message saying so. Avito
answers individual listing pages with a stricter refusal than it uses on category pages, so
there is nothing useful to fall back to and a request that was never going to resolve is not
worth making on this source.

`startPage` defaults to `1` and accepts `1` to `100`. It is where each category's walk
begins, not the page it fetches — the run continues forward from there. Use it to resume a
harvest without re-collecting what you already have.

`sort` takes Avito's own sort code. `101` is the site's default order, `1` is cheapest
first, `2` is most expensive first and `104` is newest first; cars additionally offer
`164669_desc` for newest model year and `2687_asc` for lowest mileage. The codes are Avito's
rather than friendly names of this Actor's choosing, and that is deliberate: the vocabulary
differs per category, and a friendly name that mapped to nothing on some other category
would not fail — it would silently sort by something else.

`maxItems` defaults to `100` and caps **each category separately**, so a run over five
categories with a cap of 100 returns up to 500 rows rather than 100 shared between them.
`0` means no limit, which on this site means 5,000 rows per city-category. Why that is the
ceiling is answered under Frequently asked questions.

```json
{
  "categoryUrls": [
    "https://www.avito.ru/moskva/avtomobili",
    "/sankt-peterburg/telefony"
  ],
  "startPage": 1,
  "sort": "1",
  "maxItems": 100
}
```

### Response fields

```json
{
  "requested_url": "/moskva/avtomobili",
  "page": 1,
  "id": "8082366222",
  "url": "https://www.avito.ru/moskva/avtomobili/toyota_camry_2.5_at_2013_201_463_km_8082366222",
  "title": "Toyota Camry 2.5 AT, 2013, 201 463 км",
  "price": 1200000,
  "price_text": "1 200 000 ₽",
  "price_was_lowered": false,
  "category": "Автомобили",
  "category_slug": "avtomobili",
  "location": "Москва",
  "address": "Москва, Варшавская",
  "latitude": 55.664456,
  "longitude": 37.627059,
  "sort_timestamp_ms": 1787324360000,
  "is_new": false,
  "is_verified": true,
  "is_reserved": false,
  "rating": 4.5,
  "description": "1 собственник. ПТС оригинал.",
  "attributes": "201 463 км, 2.5 AT (181 л.с.), седан, передний, бензин",
  "images": ["https://00.img.avito.st/image/1/1.DjHNe…"],
  "images_count": 32,
  "seller_name": "ТОРГМАШ",
  "seller_url": "https://www.avito.ru/brands/i116890677",
  "seller_rating": 4.5,
  "seller_reviews_summary": "298 отзывов"
}
```

Fields absent from a listing are returned as null rather than omitted, so every row has the
same shape.

`price` and `price_text` are both published and they are not duplicates of each other.
`price_text` is the only field that separates a sale from a rent: a Moscow flat reads
`65 000 ₽ в месяц`, and a row carrying only the number `65000` presents a monthly rent as a
purchase price. Read the number for arithmetic and the text for meaning.

`sort_timestamp_ms` is named for what it is. It is Avito's own ordering key in milliseconds,
not a publication time, and the two diverge the moment a seller bumps a listing. Calling it
`published_at` would be a wrong date that reads like a right one.

`attributes` is Avito's own one-line summary for the category, kept as the single string the
site renders. The fields inside it differ per category — mileage and engine for a car, area
and floor for a flat — so splitting them into columns would invent a schema that is right
for cars and null for everything else.

`images` holds the largest rendition of each photograph on the listing snippet. Avito ships
six widths of every image; the widest is chosen by reading the width rather than by taking
the last key, because key order is Avito's and not a contract. `images_count` can exceed the
length of `images`, because the listing's own page carries more photographs than its snippet
does.

`id` is the key to de-duplicate on if you merge several runs. Within a single run this Actor
already drops repeats, and the run log says how many it dropped.

### Behaviour on partial results

A category that exists and holds nothing returns a successful run with no rows. A category
address the site serves nothing for is skipped with a line in the run log and the rest of the
list still runs — losing four good categories to one that has been renamed is the wrong
trade. An address that is not a category at all stops the run before any request is made,
with a message naming the shape an address takes.

`description` is usually null on most rows, and that is Avito's rendering rather than a
listing without a description. The site fills in the seller's text for a leading prefix of
each page and sends nothing for the rest, and how long that prefix is changes between
renders — 50 of 50, 20 of 50 and 6 of 50 were all measured on the same category on one day.
The run log states how many rows on each page carried one. `seller_name` and the other
seller columns are null on exactly the rows `description` is null on, for the same reason.

Expect this Actor to be slower than most. Avito judges the route a request arrives on rather
than the client that made it, and refuses roughly three quarters of fresh ones outright, so a
page is served only after a working route is found and then paced deliberately so it is not
lost again. A run that looks idle is usually waiting on purpose.

### Frequently asked questions

**Why does a category stop at 5,000 listings when Avito says it has half a million?**
Because Avito's own pager stops there. It states a category total in its page and then links
exactly one hundred pages of fifty regardless of it. Four categories measured on 2026-08-23
— 79,551 cars in Moscow, 115,498 flats, 533,873 phones and 49,681 cars in St Petersburg —
every one of them linked one hundred pages. The remaining listings are not behind a page
this Actor declines to fetch; they are not reachable from the pager at all. The run log
states both numbers on the first page of every category so a harvest is planned against the
one that can be walked to.

**So how do I collect more than 5,000 listings from one city?**
Narrow the category. Avito subdivides — `/moskva/kvartiry` becomes `/moskva/kvartiry/prodam`
and `/moskva/kvartiry/sdam`, and those subdivide again — and each subdivision is a full
allowance of its own with its own hundred pages. Sorting differently does not help: `sort`
reorders the same reachable five thousand rather than reaching past them.

**Why did the same listing appear twice, and why does the row count not match the pages?**
Because the inventory reorders while the walk is reading it. A twelve-page walk returned 554
distinct listings across 600 rows — 7.7% repeated. Consecutive pages did not overlap at all,
so this is drift in a live marketplace rather than a broken pager. This Actor drops the
repeats before they reach the dataset and says how many it dropped, so a run that fetched
twelve pages can legitimately save fewer than 600 rows. If you merge several runs, or run the
same category on different days, de-duplicate on `id` yourself.

**Can individual listing pages be collected — the full description, the phone number?**
No, and that is a measurement rather than a decision about scope. Avito answers per-listing
pages with a third refusal distinct from the two it uses on category pages, and on working
routes at the pacing that serves category pages twelve times in twenty, listing pages came
back real once in eight attempts. Publishing an Actor for that would be publishing a coin
toss. Every row carries its `url` so you can open one yourself, and the category page already
carries the price, address, coordinates, photographs and seller.

**Is `sort` guaranteed to work on my category?**
The first four codes are. `101`, `1`, `2` and `104` were present on every category measured.
The last two are cars-only: passing them elsewhere gets you Avito's default order rather than
an error. If a category offers an ordering none of the six covers, it is one Avito publishes
on that category's own page.

**Is an Avito account, cookie or API key required?**
No. No account, session cookie or key of any kind is supplied to the Actor or needed by it.
Everything it returns is what the category page shows an anonymous visitor.

**The prices are in roubles. Is there a currency field?**
`price_text` carries the symbol Avito renders, which is `₽` throughout. `price` is the bare
number in roubles. No conversion is performed — a converted figure would be a rate this Actor
picked on a day it did not tell you about.

# Actor input Schema

## `categoryUrls` (type: `array`):

Avito category pages to walk, one per line, as full links or as paths — https://www.avito.ru/moskva/avtomobili or /moskva/avtomobili. A city-category is the unit here: cars in Moscow and cars in St Petersburg are two entries. Paste the address from Avito's own URL bar. Any `?context=` token on it is removed: it is bound to the search that produced the link and works nowhere else.

## `startPage` (type: `integer`):

Which page of 50 to begin each category at, from 1 to 100. Useful for resuming a harvest; leave it at 1 to start from the top of the category.

## `sort` (type: `string`):

How Avito should order the listings. These are Avito's own codes rather than names of this Actor's choosing, because the vocabulary differs per category — the last two exist on cars and nowhere else.

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

Maximum listings to save for each category in the list. Set 0 for no limit. Listings come 50 to a page and a category stops at page 100, so 5,000 is everything one city-category can hand over.

## Actor input object example

```json
{
  "categoryUrls": [
    "/moskva/avtomobili"
  ],
  "startPage": 1,
  "sort": "101",
  "maxItems": 50
}
```

# 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 = {
    "categoryUrls": [
        "https://www.avito.ru/moskva/avtomobili"
    ],
    "startPage": 1,
    "sort": "101",
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("thenetaji/avito-listings-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 = {
    "categoryUrls": ["https://www.avito.ru/moskva/avtomobili"],
    "startPage": 1,
    "sort": "101",
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("thenetaji/avito-listings-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 '{
  "categoryUrls": [
    "https://www.avito.ru/moskva/avtomobili"
  ],
  "startPage": 1,
  "sort": "101",
  "maxItems": 50
}' |
apify call thenetaji/avito-listings-scraper --silent --output-dataset

```

## MCP server setup

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