# otomoto, Standvirtual & Autovit Monitor: New Cars, Price Drops (`neverempty/otomoto-standvirtual-autovit-monitor`) Actor

For car dealers, flippers and import brokers: new used-car listings and price drops (with the old price) from otomoto.pl, standvirtual.com and autovit.ro, plus removed cars on otomoto.pl and standvirtual.com. By make, model, price and year. Schedule hourly or daily. JSON, CSV, Excel.

- **URL**: https://apify.com/neverempty/otomoto-standvirtual-autovit-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.30 / 1,000 car listing returneds

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

## otomoto.pl, Standvirtual & Autovit Monitor - New Used Cars & Price Drops

**For car dealers, flippers and import brokers: new used-car listings and price drops (with the old price) from otomoto.pl, standvirtual.com and autovit.ro, plus removed cars on otomoto.pl and standvirtual.com.** By make, model, price and year. Schedule hourly or daily. JSON, CSV, Excel.

This Actor watches used-car searches on **otomoto.pl (Poland)**, **standvirtual.com (Portugal)** and **autovit.ro (Romania)** and returns only what changed since the last run: **new cars**, **price drops and rises** (with the previous price), and on otomoto.pl and standvirtual.com **removed cars**. On autovit.ro a new car is one listed after the watch started. Each row has the price **with its currency** (PLN or EUR), year, mileage, fuel, power, region and city, **dealer or private seller**, dealer name, photo, listing date and link, plus VAT flags, gearbox, engine size and the site's **price evaluation** (below, in or above the market range) when the site shows them. Search by **make and model**, **price**, **year**, **mileage**, **fuel**, **body type**, **gearbox**, **seller type** and **region or city**, or paste a search URL with filters already set on the site (the filter fields are not applied to a pasted URL). No login, no API key. Export as JSON, CSV or Excel, or send rows to Google Sheets, Slack, Make, Zapier or n8n with Apify integrations.

- **Only the changes, every run.** Turn on **Monitor mode** and schedule the Actor. The first run returns up to `maxResultsPerSearch` newest cars per search (50 by default) as the starting point, charged as listings; later runs return only the changes: `new`, `price_drop`, `price_up` and `removed` rows (each kind can be switched off), plus a small run start fee per check.
- **Price drops with the old price.** A car you already got whose price changed comes back as `price_drop` or `price_up` with `previousPrice` and `priceChange` (for example `-2000`).
- **Removed cars.** On otomoto.pl and standvirtual.com, a car you got before that is gone from the part of the list the watch reads (sold, withdrawn or no longer matching) comes back once as `removed`, with its last title, link and price.
- **No fake new cars.** Promoted or re-listed cars are not charged as new.
- **3 countries, one run.** Up to 10 searches per run (makes/models × sites + pasted URLs). Make names that a site writes differently (for example `vw` on standvirtual.com and `volkswagen` on otomoto.pl and autovit.ro) are matched for you. The same car is returned once, even when two of your searches overlap.
- **Clean numbers.** `price` is a number in `currency`, `mileageKm`, `powerHp`, `engineCapacityCm3` and `year` are numbers, `fuel` and `gearbox` are the same English words on all three sites (the site's own word is kept in `fuelLocal`), `createdAt` is the listing date.

### What you get

One row per car. Example (the first check of a watch on otomoto.pl, `volkswagen/golf`, 2026-10-28):

```json
{
  "status": "ok",
  "changeType": "new",
  "isFirstCheck": true,
  "listingId": "6151399744",
  "url": "https://www.otomoto.pl/osobowe/oferta/volkswagen-golf-ID6IiE4U.html",
  "title": "Volkswagen Golf e-Golf",
  "make": "Volkswagen",
  "model": "Golf",
  "modelFamily": null,
  "version": "e-Golf",
  "price": 39900,
  "currency": "PLN",
  "previousPrice": null,
  "priceChange": null,
  "priceIsGross": true,
  "priceGross": null,
  "priceNet": null,
  "vatInvoice": false,
  "priceEvaluation": "in",
  "year": 2016,
  "mileageKm": 118296,
  "fuel": "electric",
  "fuelLocal": "Elektryczny",
  "gearbox": "automatic",
  "powerHp": 115,
  "engineCapacityCm3": null,
  "bodyType": null,
  "originCountry": null,
  "region": "Wielkopolskie",
  "city": "Turek",
  "sellerType": "dealer",
  "dealerName": "AUTO HANDEL",
  "photoUrl": "https://.../image;s=640x480",
  "createdAt": "2026-10-28T07:24:21.000Z",
  "isPromoted": false,
  "firstSeenAt": "2026-10-28T08:03:00.000Z",
  "site": "otomoto.pl",
  "positionInSearch": 2,
  "listingsInSearch": 3788,
  "searchUrl": "https://www.otomoto.pl/osobowe/volkswagen/golf?search%5Border%5D=created_at_first%3Adesc",
  "watchName": "readme",
  "checkedAt": "2026-10-28T08:03:23.943Z"
}
```

| Field | What it is |
|---|---|
| `changeType` | Monitor mode: `new`, `price_drop`, `price_up`, `removed` or `unchanged`. Empty in a normal search. |
| `isFirstCheck` | `true` on the rows of a watch's first run (the starting point). |
| `price`, `currency` | Price as a number and its currency (`PLN` on otomoto.pl, `EUR` on standvirtual.com and autovit.ro; a seller can list in another currency, and the row then says so). Empty when the listing shows no price. |
| `previousPrice`, `priceChange` | The price at the previous check and the difference (negative = cheaper). |
| `priceIsGross`, `priceGross`, `priceNet`, `vatInvoice` | Whether the price includes VAT, the gross and net amounts when the site shows both, and whether the seller issues a VAT invoice. |
| `priceEvaluation` | The site's price evaluation: `below`, `in` or `above` the market range, or empty when the site shows none. |
| `year`, `mileageKm`, `powerHp`, `engineCapacityCm3` | Numbers. |
| `fuel`, `gearbox` | `petrol`, `diesel`, `electric`, `hybrid`, `plugin-hybrid`, `lpg`, `cng`, `hydrogen`; `automatic` or `manual`. `fuelLocal` is the site's own word. The autovit.ro list does not show the gearbox, so there `gearbox` is filled only when the search is filtered by `transmission`. |
| `bodyType` | Filled when the search is filtered to one body type. |
| `region`, `city` | Where the car is, as shown in the list. |
| `sellerType`, `dealerName` | `dealer`, `private` or `crosslisted`, and the dealer name shown in the list. |
| `createdAt` | When the listing was published on the site. |
| `firstSeenAt` | When this watch first saw the car. |
| `positionInSearch`, `listingsInSearch` | Position in the list and how many cars the search has. |
| `status` | `ok` for a car; otherwise a free row with a `note` that says why. |

### Input

```json
{
  "makeModels": ["bmw/seria-3", "volkswagen/golf"],
  "sites": ["pl", "ro"],
  "priceTo": 90000,
  "yearFrom": 2018,
  "mileageTo": 150000,
  "fuelTypes": ["diesel", "petrol"],
  "transmission": "automatic",
  "maxResultsPerSearch": 50,
  "monitorMode": true
}
```

- `makeModels`: `make/model` as in the site's search address (`bmw/seria-3`, `audi/a4`), or a make alone (`toyota`).
- `sites`: `pl` (otomoto.pl), `pt` (standvirtual.com), `ro` (autovit.ro). Empty = otomoto.pl.
- `searchUrls`: paste a car search from any of the three sites with any site filter (colour, equipment, model family ...).
- Filters: `priceFrom` / `priceTo` (PLN on otomoto.pl, EUR on the other two), `yearFrom` / `yearTo`, `mileageFrom` / `mileageTo` (km), `fuelTypes`, `bodyTypes`, `transmission`, `sellerType`, `region`.
- `maxResultsPerSearch`: 1 to 5,000 (default 50).
- Monitor mode: `monitorMode`, `includePriceChanges` (on), `includeRemoved` (on), `includeUnchanged` (off), `watchName`, `resetMonitoringState`.

### Monitor mode

1. Schedule the Actor (Schedules tab, for example every hour or every morning) with `monitorMode: true`.
2. The first run returns the newest `maxResultsPerSearch` cars of each search with `changeType: new` and `isFirstCheck: true`, and remembers them.
3. Every later run reads the list again and returns:
   - `new`: cars that appeared since the previous check (on autovit.ro: listed after the watch started),
   - `price_drop` / `price_up`: cars you already got whose price changed, with `previousPrice`,
   - `removed`: cars you already got that are gone from the part of the list the watch reads (otomoto.pl and standvirtual.com).
4. A run where nothing changed returns one free `no-changes` row.
5. New cars that did not fit into `maxResultsPerSearch` or your maximum charge are not charged and come in the next run.
6. Use `watchName` to keep separate watches for the same search (one per client or per alert channel).

### Pricing

Pay per event:

- **Run start**: once per run that returns cars, and once per monitor-mode run that read and compared the list, even when nothing changed (it pays for the check).
- **Listing returned**: per car row delivered (`new`, `price_drop`, `price_up`, `removed`, `unchanged` or a normal search row). Free rows are never charged.

If your maximum total charge has no room for the run start fee plus one car, the run requests nothing and is charged nothing.

### Good to know

- Model words can differ between the sites (for example the BMW 3 Series is `seria-3` on otomoto.pl and autovit.ro, while standvirtual.com groups it as a model family). When a model is not found, the run returns a free row; set the search on that site and paste its address into `searchUrls`.
- On otomoto.pl and standvirtual.com the list is read newest first, and a watch reads the newest `maxResultsPerSearch` cars plus one page. On autovit.ro the list cannot be read newest first, so a watch there reads the whole search, up to 1,280 cars; for a longer search it returns a free row asking you to narrow it (model, year, fuel, region or seller type). The autovit.ro list also changes its order from page to page, so one read can miss some cars: there a watch reports a car as `new` only when it was listed after the watch started, and it does not report `removed` cars.
- On autovit.ro the price filter is applied to the cars' EUR prices; cars listed only in another currency are left out of a price-filtered search.
- `removed` is judged on the part of the list the watch reads. A car you got long ago that has slid further down is not reported as removed.
- The list does not mark whether a price is negotiable, so there is no negotiable flag.
- One search pages through at most 500 pages (16,000 cars) in a normal run; narrow a larger search to reach the rest.
- Dealer names are returned as shown in the search list. Nothing is collected from listing pages, and no private seller names or phone numbers are returned.
- Two schedules running the same search with the same `watchName` at the same moment can return the same new car twice; give each schedule its own `watchName`.
- No cars, nothing changed, an unknown make or model, or a page that could not be read come back as free rows that say why.
- 256 MB by default; a monitor check of one otomoto.pl or standvirtual.com search with the default 50 cars reads 3 pages.
- Unofficial. Reads the public search pages a person sees without logging in.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `makeModels` (type: `array`):

Schedule it hourly or daily with Monitor mode on to get only new listings and price drops. Write make/model as in the site's search address, for example bmw, bmw/seria-3 or audi/a4; each one is searched on each site, and make names such as volkswagen and vw are matched to each site. Model words can differ between the sites: if a model is not found, paste that site's search address into Search URLs. Leave this and Search URLs empty to run the example bmw on otomoto.pl.

## `sites` (type: `array`):

Which sites to search with the makes and models above. Empty = otomoto.pl (Poland).

## `searchUrls` (type: `array`):

Optional. Paste a car search address from otomoto.pl, standvirtual.com or autovit.ro with your filters already set on the site, for example https://www.otomoto.pl/osobowe/bmw/seria-3?search\[filter_float_year:from]=2018. On otomoto.pl and standvirtual.com the list is read newest first. The filters below do not change a pasted URL.

## `region` (type: `string`):

Optional. A region or city as it appears in the site's search address, for example mazowieckie or warszawa (otomoto.pl), lisboa (standvirtual.com), bucuresti (autovit.ro). Empty = the whole country.

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

Optional. Lowest price in the site's currency: PLN on otomoto.pl, EUR on standvirtual.com and autovit.ro.

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

Optional. Highest price in the site's currency: PLN on otomoto.pl, EUR on standvirtual.com and autovit.ro.

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

Optional. Earliest production year (otomoto.pl, autovit.ro) or first registration year (standvirtual.com), for example 2018.

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

Optional. Latest production or first registration year.

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

Optional. Lowest mileage in kilometres.

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

Optional. Highest mileage in kilometres, for example 150000.

## `fuelTypes` (type: `array`):

Optional. Only cars with these fuels. Empty = all fuels.

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

Optional. Only these body types. Empty = all body types.

## `transmission` (type: `string`):

Optional. Only automatic or only manual cars. Empty = both.

## `sellerType` (type: `string`):

Optional. Only dealers or only private sellers. Empty = both.

## `maxResultsPerSearch` (type: `integer`):

How many cars to return per search (empty = 50). On otomoto.pl and standvirtual.com these are the newest cars. In Monitor mode this is the most new cars returned per search and run; price changes and removed cars are always returned.

## `monitorMode` (type: `boolean`):

Schedule it hourly or daily and get only what changed. On: the first run returns the current cars (changeType new, isFirstCheck true) as the starting point; every later run returns only new cars, price drops and rises (price_drop / price_up, with previousPrice) and removed cars. When nothing changed, one free row says so.

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

On (default): also return cars returned before whose price changed since the previous check, with previousPrice and priceChange. Off: no price change rows.

## `includeRemoved` (type: `boolean`):

On (default): also return cars returned before that are gone from the part of the list this watch reads (sold, withdrawn or no longer matching the search), with their last known title, link and price. Not on autovit.ro, whose list changes order between pages. Off: no removed rows.

## `includeUnchanged` (type: `boolean`):

Off (default): cars that did not change are not returned. On: return them too, with changeType unchanged (each is a charged row).

## `watchName` (type: `string`):

Optional. Keeps separate memories for Monitor mode, for example one per client (letters, digits, dot, dash, underscore; up to 40). Runs with the same watch name share what has already been returned.

## `resetMonitoringState` (type: `boolean`):

On: forget what this watch has seen for these searches before running, so this run is a first check again.

## Actor input object example

```json
{
  "makeModels": [
    "bmw"
  ],
  "sites": [
    "pl"
  ],
  "maxResultsPerSearch": 50,
  "includePriceChanges": true,
  "includeRemoved": true,
  "includeUnchanged": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per car: listing ID, link, title, make, model, price with currency, gross/net price, price rating, year, mileage, fuel, gearbox, power, engine size, region, city, seller type, dealer name, photo and listing date. In Monitor mode each row has changeType new, price_drop, price_up, removed or unchanged, with previousPrice. A search with no cars, nothing changed, an unknown make or model, a page that could not be read or a run that hit its maximum charge comes back as a free row that says why.

# 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 = {
    "makeModels": [
        "bmw"
    ],
    "sites": [
        "pl"
    ],
    "maxResultsPerSearch": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/otomoto-standvirtual-autovit-monitor").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 = {
    "makeModels": ["bmw"],
    "sites": ["pl"],
    "maxResultsPerSearch": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/otomoto-standvirtual-autovit-monitor").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 '{
  "makeModels": [
    "bmw"
  ],
  "sites": [
    "pl"
  ],
  "maxResultsPerSearch": 50
}' |
apify call neverempty/otomoto-standvirtual-autovit-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/otomoto-standvirtual-autovit-monitor"
        }
    }
}
```

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/A99aB3HOSpwWU3wua/builds/B12zPFD4BXj9bauBB/openapi.json
