# Auto.ru Scraper - Car Listings, Prices, Checks & Dealers (`abotapi/auto-ru-scraper`) Actor

Scrape Auto.ru car listings: search by make, model, year, price, mileage, body, engine, gearbox, drive, seller and region, or paste result, dealer and listing links. Get price history, vehicle checks, owners, valuation, dealer details and photos, plus price-change tracking.

- **URL**: https://apify.com/abotapi/auto-ru-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 listing 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?

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

## Auto.ru Scraper: Car Listings, Prices, History Checks & Dealers

This scraper turns auto.ru, one of Russia's two big car marketplaces, into a clean listings API. Search by make, model, region, year, price, mileage, body, engine, gearbox, drive and seller type, or paste result pages, dealer pages and listing links. Every listing comes back as one row with the full specification, asking price and price history, owners, title and vehicle-number checks, the site's own market and trade-in estimates, seller or dealer details, equipment and photos, plus (on demand) the seller's description and the view and favourite counters. Export to JSON, CSV or Excel, or pull the results straight into your app through the API.

### Why This Scraper?

- **Filters applied by the site itself.** Make, model, generation, region and radius, year, price, mileage, engine volume and power, body, fuel, gearbox, drive, seller type, steering wheel, number of owners, damaged and customs-cleared cars, new or used, and nine sort orders are sent to the site, so you read only the pages you need instead of downloading everything and filtering afterwards.
- **One scope, stated out loud.** The site quietly narrows a search without a region to the region of whoever is connecting. This actor always sends an explicit region (all of Russia when you name none), so the same input returns the same listings whatever address the run uses.
- **Price history, valuation and checks in every row.** Each row carries the price changes since the listing went up, the site's market-price range and trade-in range, the owner count, the title type, customs status and the results of the site's vehicle-number, owner, legal and accident checks.
- **Dealers handled properly.** Dealer rows carry the dealer name, id, address, map position, opening hours, rating and the business phone numbers the dealer page prints; a dealer id or a dealer page link returns that dealer's whole stock.
- **Honest about what the site hides.** The site masks the vehicle number after the first three characters and every seller phone behind a call-back flow; both stay masked here. A private seller's name, street address and map position are not collected at all.
- **Built for monitoring.** Incremental mode returns only new, changed and, optionally, delisted listings on a schedule, a price-and-availability mode keeps description edits out of your bill, and a run that could not read the site fails loudly instead of returning an empty dataset.
- **Reliable on a flaky connection.** Every fresh connection is tested with a tiny request before it is asked for a megabyte of results, refused or unreachable addresses are dropped immediately, and a search the site cannot fully show (it stops at 99 pages) says so instead of pretending to be complete.

### Use Cases

- **Dealers and resellers:** watch competitors' stock and price drops for a make, a model or a whole region on a schedule.
- **Price analysts and valuation teams:** build market-price series from asking prices, price history and the site's own estimates.
- **Importers and exporters:** screen the Russian market by make, year, mileage, owners and customs status before buying.
- **Lead and inventory teams:** pull a dealer's complete stock with addresses and opening hours into your CRM.
- **Researchers and journalists:** study the used-car market by region, body type, fuel and age.

### Data You Get

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

| Field | Example |
| --- | --- |
| `listingId` / `offerId` | `"1000000001-0a1b2c3d"` (the row's identity and the end of the listing link) / `"1000000001"` |
| `url` / `title` / `condition` | link to the listing / `"Sample Make Model II"` / `"used"` |
| `make` / `model` / `generation` / `complectation` | `"SAMPLEMAKE"` / `"MODEL"` / `"II"` / `"Comfort"` (codes as in auto.ru web addresses, names alongside) |
| `year` / `mileageKm` / `ownersCount` | `2021` / `45000` / `2` |
| `price` / `currency` / `priceUsd` / `priceEur` | `2100000` / `"RUB"` / `26000` / `23500` |
| `priceHistory` / `firstPrice` / `priceChange` | `[{"date": "2026-10-01T00:00:00Z", "price": 2200000}]` / `2200000` / `-100000` |
| `marketPriceFrom` / `marketPriceTo` / `tradeInPriceFrom` / `tradeInPriceTo` | the site's estimated market and trade-in ranges in roubles |
| `bodyType` / `engineType` / `engineVolumeCc` / `enginePowerHp` | `"Sedan"` / `"GASOLINE"` / `1998` / `150` |
| `transmission` / `driveType` / `steeringWheel` / `colorHex` / `color` | `"AUTOMATIC"` / `"ALL_WHEEL_DRIVE"` / `"LEFT"` / `"040001"` / the colour name in Russian |
| `acceleration0to100` / `clearanceMm` / `trunkVolumeL` / `fuelRateL100km` | factory figures when the site lists them |
| `equipment` | equipment codes of the trim (`["abs", "esp", "climate-control-2"]`) |
| `pts` / `ptsOriginal` / `customsCleared` | `"ORIGINAL"` / `true` / `true` |
| `vinMasked` / `licensePlateRegion` | `"XXX**************"` (the site shows only the first three characters) / `"77"` |
| `vinCheck` / `ownersCheck` / `legalCheck` / `accidentsCheck` | results of the site's checks (`"OK"`, `"ERROR"`, `"UNKNOWN"`) |
| `damaged` / `purchaseYear` / `warrantyType` | whether the car is marked damaged / year the owner bought it / warranty type |
| `sellerType` / `sellerName` / `dealerId` / `dealerCode` / `isOfficialDealer` | `"COMMERCIAL"` / dealer name (empty for private sellers) / dealer ids and official flag |
| `dealerAddress` / `dealerLatitude` / `dealerLongitude` / `dealerPhones` / `dealerOpenHours` / `dealerRating` | dealer business details as the dealer page prints them |
| `city` / `cityId` / `sellerSince` | city of the car and the date the seller joined the site |
| `photos` / `photoCount` / `badges` / `tags` | photo links (largest size), photo count, the site's labels |
| `createdAt` / `updatedAt` / `daysOnSale` / `exchangePossible` | when the listing was created and last edited, days on sale, part-exchange flag |
| `maxDiscount` / `discountTradeIn` / `discountCredit` / `discountInsurance` | dealer discounts on new cars |
| `description` / `views` / `viewsToday` / `favorites` / `favoritesToday` / `conditionGrade` | the seller's text, counters and the site's condition grade (listing-page read) |
| `position` / `page` / `sourceLabel` / `sourceUrl` | where the listing ranked in its search and what it was found through |
| `changeType` / `changedFields` / `firstSeenAt` / `lastSeenAt` | incremental mode: `"NEW"` / `[]` / timestamps |

Every record also carries `recordType`, `status`, `availability`, the model and make names, `transportTaxPerYear` and `scrapedAt`.

### How to Use

1. Pick a **mode**: `search` (makes, models and filters), `url` (paste links) or `listing` (listing keys).
2. Fill in the fields for that mode, then apply any filters and a sort order.
3. Set **Max listings** to control run size and cost, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Search two models in two regions, cheapest first:**

```json
{
  "mode": "search",
  "vehicles": ["bmw x5", "audi q7"],
  "regions": ["Moscow", "Saint Petersburg"],
  "regionRadiusKm": 100,
  "condition": "used",
  "yearFrom": 2018,
  "sortBy": "price_asc",
  "maxItems": 200
}
```

**Filter by body, fuel, gearbox and mileage:**

```json
{
  "mode": "search",
  "bodyTypes": ["SEDAN", "LIFTBACK"],
  "engineTypes": ["DIESEL", "HYBRID"],
  "transmissions": ["AUTOMATIC"],
  "mileageTo": 100000,
  "maxOwners": 2,
  "sellerTypes": ["PRIVATE"],
  "maxItems": 100
}
```

If you leave `urls` untouched in url mode, the actor scrapes three sample links, one per link shape. Replace them with your own links.

**Paste links, with the seller's description and counters:**

```json
{
  "mode": "url",
  "urls": [
    "https://auto.ru/moskva/cars/bmw/x5/used/?year_from=2018&sort=price-asc",
    "https://auto.ru/diler/cars/all/avilon_tenet_moskva/",
    "https://auto.ru/cars/used/sale/honda/vezel/1160165890-6216410f/"
  ],
  "fetchDetails": true,
  "maxItems": 60
}
```

**Monitor a search on a schedule, price changes only:**

```json
{
  "mode": "search",
  "vehicles": ["vaz vesta"],
  "regions": ["Kazan"],
  "incrementalMode": true,
  "changeScope": "priceAndStock",
  "emitExpired": true,
  "expireAfterMissedScans": 2,
  "maxItems": 0
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/auto-ru-scraper").call(run_input={"mode": "search", "vehicles": ["bmw x5"], "maxItems": 50})
for car in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(car["title"], car["year"], car["price"], car["city"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/auto-ru-scraper').call({ mode: 'search', vehicles: ['bmw x5'], maxItems: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

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

#### Link shapes you can paste

In url mode the actor reads these shapes: a result page with an optional region, make, model, generation and section (`/moskva/cars/bmw/x5/used/`, `/cars/all/`, `/rossiya/cars/vaz/vesta/new/`) together with the filters the site writes into the address (`year_from`, `price_to`, `km_age_to`, `body_type_group`, `engine_group`, `transmission`, `gear_type`, `seller_group`, `owners_count_group`, `displacement_from`, `power_from`, `sort` and the others the filter form uses), a dealer page (`/diler/cars/all/<dealer code>/`), a listing page (`/cars/used/sale/<make>/<model>/<number>-<hash>/`, also for new cars), the mobile host `m.auto.ru`, and a bare listing key. A result link without a region means all of Russia, never the region of the connecting address. A link that starts on a later page (`?page=3`) continues from there and is treated as a partial scan. Parameters this actor does not apply are named in the log rather than dropped silently. Motorcycle, truck and other vehicle categories, new-car group pages and every other site are skipped with a reason.

#### Search mode

Each make or model in `vehicles` is searched in every region in `regions` (up to 40 combinations per run, repeats ignored) and its result pages are read one after another to the last page the site offers, 37 listings per page, until **Max listings** is reached. The site lets sellers pay to promote a listing, so promoted listings can interrupt the order of any sorted page; apart from them the order holds from one row to the next. **Max listings** is shared evenly between the combinations, and a listing found twice is returned once. If a make or model returns nothing, the log names the closest codes the site knows.

#### Listing mode

Listing mode reads listings by key (the number, a dash and eight characters at the end of a listing link) or by link and returns the full listing page data for each one. A listing that has been sold or removed is skipped and not billed.

#### Output tab views

The Output tab offers seven views: **Listings** (vehicle, price, mileage, seller and place), **Specification**, **Price and valuation**, **Ownership and checks**, **Sellers and dealers**, **Listing page detail** and **Changes since the last run** (incremental status and changed fields; with Compact updates on, also the old and new values of each price change).

#### Keeping a run cheap

A result page already carries the full specification, price history, valuation, checks, seller and photos for 37 listings at no extra cost. **Read each listing's page** adds one request per listing for the seller's description and the counters and is billed with one Listing page detail event per listing that reaches the dataset; listing mode always reads that page, so it is billed the same way. Failed, empty or missing pages are never billed. Filters are sent to the site, so a narrowed run reads fewer pages.

#### How Max listings and the site's page limit shape a run

The site shows at most 99 result pages (3663 listings) for any search, however many it matches. When a search has more, the log says so and the run is not treated as a complete scan, so incremental mode never marks the hidden remainder as expired. To go beyond the limit, split the work: several makes or models, several regions, or year and price windows. When **Max listings** is set, every make-and-region combination and every link gets its own share of it, split evenly, so one large search cannot use up the whole run and starve the rest.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset ID and the actor skips everything already collected there, so you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same scope. Each listing is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED` (only after a run that scanned the whole tracked scope, and only with `emitExpired`). `stateKey` names or shares the stored state. With Incremental mode off, output is exactly as before.

**What counts as a change.** Compared: price, dealer discount, availability, status, specification, ownership and vehicle-check fields, seller, description, equipment and price history. Excluded by default but still returned in the row: scrape timestamps, the listing's rank and page, where it was found, view and favourite counters, days on sale and the last-edited time, the price in dollars and euros (it moves with the exchange rate), the site's own market and trade-in estimates, dealer rating and condition grade, the rotating labels and badges, and photo links. Equipment and phone lists are compared regardless of order. Switching the exclusions never causes a one-time wave of `UPDATED` rows: `UNCHANGED` is decided by re-reading the stored record under the current settings.

- `changeScope: priceAndStock` compares only price, dealer discount, availability, status and the price history, so a description, equipment or ownership edit is never a billable `UPDATED` row. Switching `changeScope`, or editing `ignoreFieldsForChanges`, keeps your existing saved state: nothing comes back as `NEW`, each stored listing is simply re-compared under the new setting.
- `compactUpdates` turns `UPDATED` and `EXPIRED` rows into slim rows (ids, link, title, current price and availability, `priceChanges` old and new, `variantChanges` for price-history entries as `ADDED`, `REMOVED` or `CHANGED`). `NEW` rows stay full. A compact row does not carry listing-page detail fields, so no detail charge applies to it.
- `expireAfterMissedScans` applies with `emitExpired`. `EXPIRED` means the listing dropped out of the scanned results; listings are sold and posted all day, so a value of 2 or more avoids false expiries.
- A run stopped by **Max listings**, **Max result pages**, the time limit or your maximum cost per run, a run that could not read some page, a search deeper than the site's 99 pages, a resumed run, or a run whose scope or filters differ from those the state key was last saved with never produces `EXPIRED` rows.

`EXPIRED` rows count against **Max listings** like every other row. They are only produced after a run that scanned the whole tracked scope, so a run that stopped because it reached **Max listings** returns none.

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

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each listing, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify, Settings, Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

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

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` to find listings with the filters, `url` to scrape pasted links, or `listing` to read listings by key. |
| `vehicles` | array | `["bmw x5"]` | Makes and models to search, written as in auto.ru web addresses (`bmw x5`, `vaz vesta`, `land_rover discovery`). One search each; empty means every make (search mode). |
| `condition` | string | `any` | `any`, `new` or `used`. |
| `regions` | array | (none) | Cities or regions, one search each: Latin names for the biggest places (`Moscow`, `Kazan`), or any place written in Russian. Empty means all of Russia. |
| `regionRadiusKm` | integer | (site default) | Search radius around each city, 0 to 1000 km. Empty uses the site's default (200 km for most cities). |
| `dealerIds` | array | (none) | Numeric dealer ids; returns each dealer's whole stock and ignores region, radius and make. |
| `yearFrom` | integer | (none) | Earliest model year. |
| `yearTo` | integer | (none) | Latest model year. |
| `priceFrom` | integer | (none) | Lowest asking price in Russian roubles. |
| `priceTo` | integer | (none) | Highest asking price in Russian roubles. |
| `mileageFrom` | integer | (none) | Lowest mileage in kilometres. |
| `mileageTo` | integer | (none) | Highest mileage in kilometres. |
| `engineVolumeFrom` | integer | (none) | Smallest engine displacement in cubic centimetres (the site rounds to a tenth of a litre). |
| `engineVolumeTo` | integer | (none) | Largest engine displacement in cubic centimetres. |
| `powerFrom` | integer | (none) | Lowest engine power in horsepower. |
| `powerTo` | integer | (none) | Highest engine power in horsepower. |
| `bodyTypes` | array | (none) | Body types: `SEDAN`, `HATCHBACK_3_DOORS`, `HATCHBACK_5_DOORS`, `LIFTBACK`, `WAGON`, `COUPE`, `CABRIO`, `ALLROAD_3_DOORS`, `ALLROAD_5_DOORS`, `MINIVAN`, `PICKUP`, `VAN`, `LIMOUSINE`. |
| `engineTypes` | array | (none) | Fuel types: `GASOLINE`, `DIESEL`, `HYBRID`, `ELECTRO`, `LPG` (cars fitted with a gas system). |
| `transmissions` | array | (none) | Gearboxes: `AUTOMATIC`, `MECHANICAL`, `ROBOT`, `VARIATOR`. |
| `driveTypes` | array | (none) | Drive layouts: `FORWARD_CONTROL` (front-wheel), `REAR_DRIVE`, `ALL_WHEEL_DRIVE`. |
| `sellerTypes` | array | (none) | `PRIVATE` or `COMMERCIAL` (dealers); empty means both. |
| `steeringWheel` | string | `any` | `any`, `LEFT` or `RIGHT`. |
| `maxOwners` | integer | (none) | Only cars with at most this many owners in the vehicle title: 1, 2 or 3. |
| `includeDamaged` | boolean | `false` | Also return cars the seller marks as damaged or not running. |
| `includeNotCleared` | boolean | `false` | Also return cars that are not cleared through customs. |
| `sortBy` | string | `relevance` | `relevance`, `newest`, `oldest`, `price_asc`, `price_desc`, `year_desc`, `year_asc`, `mileage_asc` or `mileage_desc`. A sort written into a pasted link wins. |
| `urls` | array | three sample links (one per link shape) | Result pages, dealer pages, listing pages or bare listing keys (url mode). |
| `listingIds` | array | `["1160165890-6216410f"]` | Listing keys such as `1160165890-6216410f`, or listing links (listing mode). |
| `fetchDetails` | boolean | `false` | Also read every listing's own page for the seller's description and the view and favourite counters. |
| `maxItems` | integer | `20` | Maximum number of listings across the whole run, up to 100000. `0` means the 100000 maximum. |
| `maxPages` | integer | `0` | Maximum result pages per search. `0` means as many as the search has, up to the site's limit of 99. |
| `proxy` | object | residential | Connection settings. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run without re-collecting or re-billing its listings. |
| `incrementalMode` | boolean | `false` | Return only new and changed listings on scheduled runs. |
| `stateKey` | string | (none) | Name or share an incremental-mode state. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) unchanged listings. |
| `emitExpired` | boolean | `false` | Also return (and bill) listings no longer found after a full scan, within the room left under **Max listings**. |
| `changeScope` | string | `all` | `priceAndStock` limits change detection to price, discount, availability, status and price history. |
| `compactUpdates` | boolean | `false` | Slim `UPDATED` and `EXPIRED` rows with `priceChanges` and `variantChanges`. |
| `expireAfterMissedScans` | integer | `1` | Mark `EXPIRED` only after this many consecutive complete scans missed the listing. |
| `ignoreFieldsForChanges` | array | (none) | Extra fields (or dotted paths such as `priceHistory.price`) that never count as a change. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each listing to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which listings are created. |
| `maxNotifyListings` | integer | `50` | Cap on listings written to each connector per run. |

### Output Example

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

```json
{
  "recordType": "listing",
  "listingId": "1000000001-0a1b2c3d",
  "offerId": "1000000001",
  "url": "https://auto.ru/cars/used/sale/samplemake/model/1000000001-0a1b2c3d/",
  "title": "Sample Make Model II",
  "condition": "used",
  "status": "ACTIVE",
  "damaged": false,
  "make": "SAMPLEMAKE",
  "makeName": "Sample Make",
  "model": "MODEL",
  "modelName": "Model",
  "generation": "II",
  "complectation": "Comfort",
  "year": 2021,
  "mileageKm": 45000,
  "bodyType": "Sedan",
  "bodyTypeCode": "SEDAN",
  "doors": 4,
  "seats": 5,
  "engineType": "GASOLINE",
  "engineVolumeCc": 1998,
  "enginePowerHp": 150,
  "transmission": "AUTOMATIC",
  "driveType": "ALL_WHEEL_DRIVE",
  "steeringWheel": "LEFT",
  "colorHex": "040001",
  "color": "черный",
  "equipment": ["abs", "esp", "climate-control-2"],
  "price": 2100000,
  "currency": "RUB",
  "priceUsd": 26000,
  "priceEur": 23500,
  "firstPrice": 2200000,
  "priceChange": -100000,
  "priceHistory": [{"date": "2026-10-01T00:00:00Z", "price": 2200000}, {"date": "2026-10-20T00:00:00Z", "price": 2100000}],
  "marketPriceFrom": 2000000,
  "marketPriceTo": 2300000,
  "marketPriceUncertaintyPercent": 40,
  "ownersCount": 2,
  "pts": "ORIGINAL",
  "ptsOriginal": true,
  "customsCleared": true,
  "vinMasked": "XXX**************",
  "licensePlateRegion": "77",
  "vinCheck": "OK",
  "ownersCheck": "OK",
  "legalCheck": "OK",
  "accidentsCheck": "OK",
  "sellerType": "COMMERCIAL",
  "sellerName": "Sample Dealer",
  "dealerId": "100000",
  "dealerCode": "sample_dealer",
  "isOfficialDealer": false,
  "dealerAddress": "Sample Street 1",
  "dealerPhones": ["+7 000 000-00-00"],
  "dealerOpenHours": "daily 9:00-21:00",
  "city": "Moscow",
  "photoCount": 12,
  "photos": ["https://example.invalid/photo/1200x900"],
  "createdAt": "2026-10-01T00:00:00Z",
  "daysOnSale": 31,
  "description": "Sample seller text.",
  "views": 120,
  "viewsToday": 3,
  "favorites": 4,
  "conditionGrade": "B",
  "position": 3,
  "page": 1,
  "sourceLabel": "SAMPLEMAKE MODEL in all of Russia",
  "changeType": "NEW",
  "changedFields": [],
  "firstSeenAt": "2026-11-01T12:00:00Z",
  "lastSeenAt": "2026-11-01T12:00:00Z",
  "scrapedAt": "2026-11-01T12:00:00Z"
}
```

### Plan Requirement

The site refuses some connecting addresses with a login prompt, whatever country they are in. Measured with twenty fresh addresses per kind, Apify residential addresses were usable on more than eight tries in ten, and Apify's standard datacenter pool on a little over half, so the default connection is Apify residential, without a country pin; leave it as it is for the most reliable results. Every fresh address is tested with one tiny request before the run asks it for a result page, a refused or unreachable address is dropped at once and the next one is tried, and only a long unbroken run of failures escalates, in order, to residential addresses (when you picked a datacenter pool), a backup residential connection and a backup datacenter connection, staying on the one that works for the rest of the run. A pasted dealer page link, or a pasted link with a region the actor does not know, needs a Russian residential address for one page read; the Dealer ids and Regions fields work with any connection. Proxy traffic is billed on your own Apify plan.

Apify free plans do not include proxies. If your plan does not include proxy access, the run says so in plain words rather than returning an empty dataset. These are the messages you may see in the run log:

- Warning: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." followed by an upgrade pointer.
- Info: "Served over the backup connection (the default connection was unavailable)."
- Failure: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required".
- Failure: "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Auto.ru pages."
- Failure: "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

You can supply your own proxy URLs instead; custom proxies get no automatic backup connection.

### FAQ

#### How much does it cost?

Three events can appear on a run, and the **Pricing** tab shows the current rates:

- **Listing result:** one event per listing row returned in the dataset (incremental `UNCHANGED` rows are suppressed and not billed unless **Emit unchanged** is on).
- **Listing page detail:** one event per listing row whose own page is read, through **Read each listing's page** or listing mode.
- **Actor start:** one event each time a run starts.

Use **Max listings** to cap the cost of any run. Failed, empty or missing pages are never billed, and a run that reaches your maximum cost per run stops and says so.

#### Is it legal to scrape auto.ru?

This actor collects only publicly available listing data that the site shows to any visitor. It does not log in, does not unmask seller phone numbers and does not collect private sellers' names or addresses. You are responsible for how you use the data: follow the site's terms and the laws that apply to you, including data protection rules, and get legal advice if you plan commercial redistribution. Prices and specifications are generally facts, but photos and brand names may be subject to third-party rights.

#### Can I get seller phone numbers or the full vehicle number?

No. The site masks every seller phone behind a call-back flow and shows the vehicle number only up to the first three characters; both stay masked in the output. Dealer rows do carry the business phone numbers the dealer page itself prints.

#### Which vehicles and countries are covered?

Passenger-car listings on auto.ru, in Russian roubles. Motorcycles, trucks and other vehicle categories are not covered. The connection does not need to be in Russia.

#### Why do I get fewer listings than the site says it has?

The site shows at most 99 result pages (3663 listings) per search. The log says when a search was cut off that way. Split the search by make, model, region, year or price to reach the rest.

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

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new and updated listings, and unchanged ones are not billed unless you turn on **Emit unchanged**.

#### Why do some rows have no description or counters?

Result pages carry the specification, price, checks and seller, while the seller's description and the view and favourite counters live on each listing's own page. Turn on **Read each listing's page**, or use listing mode, to get them.

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

If the site refuses every request, the run stops with a clear message so "no matches today" is never confused with "nothing could be read". Run it again in a few minutes. If only some pages could be read, you still get what was found, and the run tells you the result is incomplete.

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

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more car data?

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

<table>
<tr><td>🚗 <a href="https://apify.com/abotapi/drom-auto-listings-scraper"><b>Drom.ru Scraper</b></a><br>Scrape drom.ru vehicle listings: make/model/year/mileage, price, engine and transmission...</td><td>🚗 <a href="https://apify.com/abotapi/mobile-de-scraper"><b>mobile.de Scraper</b></a><br>Scrape mobile.de vehicle listings with 30+ fields including price, mileage, registration...</td></tr>
<tr><td>🚗 <a href="https://apify.com/abotapi/cars-com-scraper"><b>Cars.com Scraper</b></a><br>Scrape cars.com listings by make, ZIP and radius. 90+ fields per car: price, MSRP...</td><td>🚗 <a href="https://apify.com/abotapi/avito-auto-listings-scraper"><b>Avito Auto Scraper</b></a><br>Scrape Avito transport: used and new car listings with year, mileage, price, the site's...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/avito-ru-scraper"><b>Avito.ru Scraper</b></a><br>Scrape structured listings from Avito.ru by region, category, filters, or direct URL...</td><td>🚗 <a href="https://apify.com/abotapi/autoscout24-scraper"><b>AutoScout24 Scraper</b></a><br>A lean, production-grade scraper for autoscout24.com. Point it at a search, get back...</td></tr>
</table>

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

### 💬 Support

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

# Actor input Schema

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

Choose 'search' to find listings with the filters below, 'url' to scrape pasted links, or 'listing' to read listings by their key.

## `vehicles` (type: `array`):

Write the make, and optionally the model, the way they appear in auto.ru web addresses: bmw, bmw x5, vaz vesta, land_rover discovery. A generation number from the address may follow the model. One search per entry. Leave empty for every make. Read only in search mode.

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

New cars, used cars, or both.

## `regions` (type: `array`):

Cities or regions to search, one search each, for example Moscow, Saint Petersburg, Kazan or Sverdlovsk oblast. Latin names work for the biggest places; any other place can be written in Russian, for example Тверь. Leave empty for all of Russia (the site would otherwise pick the region of the connecting address).

## `regionRadiusKm` (type: `integer`):

How far around each city in Regions to look, from 0 (the city itself) to 1000. Leave empty for the site's own default (200 km for most cities). Ignored for regions that are whole oblasts or republics.

## `dealerIds` (type: `array`):

Return the stock of these dealers (the numeric dealer id is a field of every dealer row, and a dealer page link works in URL mode). A dealer search ignores the region, radius and make settings.

## `yearFrom` (type: `integer`):

Earliest model year.

## `yearTo` (type: `integer`):

Latest model year.

## `priceFrom` (type: `integer`):

Lowest asking price in Russian roubles.

## `priceTo` (type: `integer`):

Highest asking price in Russian roubles.

## `mileageFrom` (type: `integer`):

Lowest mileage in kilometres.

## `mileageTo` (type: `integer`):

Highest mileage in kilometres.

## `engineVolumeFrom` (type: `integer`):

Smallest engine displacement in cubic centimetres, for example 2000. The site compares it rounded to a tenth of a litre, so 1995 counts as 2000.

## `engineVolumeTo` (type: `integer`):

Largest engine displacement in cubic centimetres. The site compares it rounded to a tenth of a litre.

## `powerFrom` (type: `integer`):

Lowest engine power in horsepower.

## `powerTo` (type: `integer`):

Highest engine power in horsepower.

## `bodyTypes` (type: `array`):

Only these body types. Leave empty for all.

## `engineTypes` (type: `array`):

Only these fuel types. 'Gas equipment' returns cars fitted with a gas system (LPG). Leave empty for all.

## `transmissions` (type: `array`):

Only these gearboxes. Leave empty for all.

## `driveTypes` (type: `array`):

Only these drive layouts. Leave empty for all.

## `sellerTypes` (type: `array`):

Private sellers, dealers, or both (leave empty).

## `steeringWheel` (type: `string`):

Left-hand or right-hand drive.

## `maxOwners` (type: `integer`):

Only cars with at most this many owners in the vehicle title (1, 2 or 3). Leave empty for any number.

## `includeDamaged` (type: `boolean`):

Off by default, like the site: cars the seller marks as damaged or not running are left out. Turn on to include them.

## `includeNotCleared` (type: `boolean`):

Off by default, like the site. Turn on to include cars whose customs clearance is missing.

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

Order of the results. A sort order written into a pasted result link wins over this setting.

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

auto.ru links to scrape directly. A result link carries its own make, model, region and filters, and they win over the settings above. A dealer link returns that dealer's stock. A listing link reads that listing. Read only in url mode.

## `listingIds` (type: `array`):

Listing keys as they appear at the end of a listing link (the number, a dash and eight characters, for example 1160165890-6216410f), or whole listing links. Read only in listing mode.

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

Search and url scopes: also read every listing's own page for the seller's description, the view and favourite counters and the site's condition grade. This is one extra request per listing and carries a per-listing surcharge. Listing mode always reads the page.

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

Maximum number of listings to return across the whole run, EXPIRED rows from Incremental mode included. This is the run's cap. Use 0 for the maximum of 100000 listings per run. The site shows at most 99 result pages (3663 listings) per search, so use several makes, models or regions to go beyond that.

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

Maximum number of result pages to read per make, region or link. Each page carries up to 37 listings. 0 means as many as the search has, up to the site's limit of 99.

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

Connection used for every request. Leave the default (residential, any country). Apify free plans do not include proxies, so a free-plan run falls back to a backup connection and may return less.

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

Paste a previous run ID or dataset ID to continue a large pull without returning or charging for listings already collected there. Use this after an interrupted run. For recurring monitoring of the same scope, use Incremental mode below instead.

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

Turn this on for daily or recurring monitoring. The first run returns every matching listing as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED listings, which makes it a cheap way to watch price drops. What is compared: price, discount, availability, status, specification, ownership and vehicle-check fields, seller, description, equipment and price history. What is excluded by default (still returned in the row, never a change): scrape timestamps, the listing's rank and page, where it was found, view and favourite counters, days on sale and the last-edited time, exchange-rate conversions (price in dollars and euros), the site's own market-price estimates, dealer rating and condition grade, the rotating labels and badges, and photo links. Equipment and phone lists are compared regardless of order. 'Changes to watch' narrows this to price and availability only. Turn on 'Emit unchanged' or 'Emit expired' only when you also want those rows returned (and billed). State is kept separately for each scope and filter setup; use State key to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

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

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to derive a key automatically from the scope and filter settings, so two different scopes never mix state.

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

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

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

Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scope without a page that could not be read, never when Max listings or the site's 99-page limit capped it, and never when Resume was used. A sold or removed listing counts as missing; consider 'Expire after missed scans' above 1 so a brief gap does not expire a listing. EXPIRED rows count against Max listings: a run returns at most the room left under it, and the rest are returned on a later complete run. This returns, and bills, extra rows, so leave it off unless you need expiry tracking.

## `changeScope` (type: `string`):

All fields (default): any change to the compared listing data marks a listing UPDATED. Price and availability only: just the asking price, the dealer discount, the availability, the status and the price history, so description, equipment or ownership edits never produce a billable UPDATED row. Changing this never starts a new baseline: the existing saved state is kept (under an automatic or a named State key) and each stored listing is simply compared under the new setting.

## `compactUpdates` (type: `boolean`):

Off by default. When on, UPDATED and EXPIRED rows are slim: ids, link, title, current price and availability, plus priceChanges (old and new value of each field that moved) and variantChanges (price-history entries ADDED, REMOVED or CHANGED). NEW rows stay full. A compact row does not carry listing-page detail fields, so no detail charge applies to it.

## `expireAfterMissedScans` (type: `integer`):

Used with 'Emit expired'. A listing is marked EXPIRED only after it was missing from this many CONSECUTIVE complete scans (default 1). Listings come and go as sellers publish and sell, so a value of 2 or more avoids false expiries.

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

Field names that must not count as a change, for example description or equipment. Use a dotted path to ignore a key inside every element of a list, for example priceHistory.price. Ignored fields are still returned in the row. Rank, where the listing was found, counters, exchange-rate conversions, the site's estimates and photo links are already ignored by default. Changing this list never starts a new baseline: the saved state is kept and each stored listing is re-compared under the new list, so no listing comes back as NEW.

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

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a page per listing; other connectors get a best-effort write or digest. Each connector receives a condensed summary per listing, not the full record; the complete record always stays in the dataset. Leave empty to skip; this never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

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

URL or id of the Notion page under which listing pages are created. Required to enable the Notion export; ignored by other connectors.

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

Cap on listings written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "vehicles": [
    "bmw x5"
  ],
  "condition": "any",
  "steeringWheel": "any",
  "includeDamaged": false,
  "includeNotCleared": false,
  "sortBy": "relevance",
  "urls": [
    "https://auto.ru/moskva/cars/bmw/x5/used/?year_from=2018&sort=price-asc",
    "https://auto.ru/diler/cars/all/avilon_tenet_moskva/",
    "https://auto.ru/cars/used/sale/honda/vezel/1160165890-6216410f/"
  ],
  "listingIds": [
    "1160165890-6216410f"
  ],
  "fetchDetails": false,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "changeScope": "all",
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `specs` (type: `string`):

No description

## `pricing` (type: `string`):

No description

## `history` (type: `string`):

No description

## `sellers` (type: `string`):

No description

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

No description

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

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "vehicles": [
        "bmw x5"
    ],
    "condition": "any",
    "sortBy": "relevance",
    "urls": [
        "https://auto.ru/moskva/cars/bmw/x5/used/?year_from=2018&sort=price-asc",
        "https://auto.ru/diler/cars/all/avilon_tenet_moskva/",
        "https://auto.ru/cars/used/sale/honda/vezel/1160165890-6216410f/"
    ],
    "listingIds": [
        "1160165890-6216410f"
    ],
    "fetchDetails": false,
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "compactUpdates": false
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "mode": "search",
    "vehicles": ["bmw x5"],
    "condition": "any",
    "sortBy": "relevance",
    "urls": [
        "https://auto.ru/moskva/cars/bmw/x5/used/?year_from=2018&sort=price-asc",
        "https://auto.ru/diler/cars/all/avilon_tenet_moskva/",
        "https://auto.ru/cars/used/sale/honda/vezel/1160165890-6216410f/",
    ],
    "listingIds": ["1160165890-6216410f"],
    "fetchDetails": False,
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "compactUpdates": False,
}

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

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

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

```

## CLI example

```bash
echo '{
  "mode": "search",
  "vehicles": [
    "bmw x5"
  ],
  "condition": "any",
  "sortBy": "relevance",
  "urls": [
    "https://auto.ru/moskva/cars/bmw/x5/used/?year_from=2018&sort=price-asc",
    "https://auto.ru/diler/cars/all/avilon_tenet_moskva/",
    "https://auto.ru/cars/used/sale/honda/vezel/1160165890-6216410f/"
  ],
  "listingIds": [
    "1160165890-6216410f"
  ],
  "fetchDetails": false,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "compactUpdates": false
}' |
apify call abotapi/auto-ru-scraper --silent --output-dataset

```

## MCP server setup

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