# OLX Scraper - Listings Across 9 Countries (`abotapi/olx-scraper`) Actor

Fast OLX scraper across 9 countries, including Poland, Romania, Portugal, Ukraine, Bulgaria, Kazakhstan, Uzbekistan, India and Brazil. Search or paste OLX URLs to extract structured listing data with pagination and multi-market support.

- **URL**: https://apify.com/abotapi/olx-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Lead generation, E-commerce, Automation
- **Features:** Limited permissions
- **Stats:** 101 total users, 15 monthly users, 90.9% runs succeeded, 4 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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?

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

## OLX Scraper: Classifieds Listings, Prices, Sellers & Photos

OLX Scraper turns OLX classifieds into structured data. It covers OLX country sites (Poland, Romania, Portugal, Ukraine, Bulgaria, Kazakhstan, Uzbekistan, plus India and Brazil, which are currently limited, see Plan Requirement), and extracts title, price, category, full location with coordinates, seller profile, contact options, images, attributes and posting dates for every listing. Search by keyword and filters, or paste OLX search-result links and let the actor auto-paginate them. Export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Two ways to collect listings.** Build a search from country, category, city, query and price filters, or paste OLX search-result URLs and the actor auto-paginates each one forward.
- **Many country sites from one actor.** Poland, Romania, Portugal, Ukraine, Bulgaria, Kazakhstan and Uzbekistan each run on their local OLX domain with the same output shape.
- **55+ named fields per listing.** Price, category path, full location with latitude and longitude, seller profile, contact toggles, images, key attributes and posting dates, not just the search-result summary.
- **Optional detail enrichment.** Turn on "Fetch full details per listing" to fill in the complete description, full attribute list and any fields the search results leave empty, with one extra request per listing.
- **Built for large catalogues.** Resume a paused run without re-scraping listings you already have, or narrow with an advanced filter passthrough for site-specific query parameters.
- **Built for schedules.** Incremental mode returns only new, updated and reappeared listings on recurring runs, so a daily monitor does not re-bill what has not changed.
- **Honest failures.** If a site refuses every connection, the run fails with a message saying so instead of returning a silent empty dataset. Brazil (olx.com.br) is currently blocked by the site; see Plan Requirement.

### Use Cases

- **Price and market monitoring:** track asking prices for a model, category or city over time across one or more OLX countries.
- **Vehicle listings aggregation:** pull car, motorcycle or parts listings by make, model and filters into your own marketplace or comparison site.
- **Real estate lead sourcing:** collect apartment and rental listings by city and price range for agents or lead-generation tools.
- **Resale and dropshipping research:** watch a category or search for newly posted items to act on quickly.
- **Business intelligence:** compare seller types, tags and pricing across categories and countries for competitive analysis.

### Data You Get

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

| Field | Example |
| --- | --- |
| `listingId` | `"789456123"` |
| `country` | `"pl"` |
| `title` | `"iPhone 13 Pro 128GB, jak nowy"` |
| `price` / `priceCurrency` | `3200` / `"PLN"` |
| `priceDisplay` | `"3 200 zł do negocjacji"` |
| `priceNegotiable` | `true` |
| `categoryName` / `categoryPath` | `"Telefony i Akcesoria"` / `"Elektronika / Telefony i Akcesoria"` |
| `city` / `region` | `"Warszawa"` / `"Mazowieckie"` |
| `fullLocation` | `"Mokotów, Warszawa, Mazowieckie"` |
| `latitude` / `longitude` | `52.1978` / `21.0224` |
| `sellerName` / `sellerType` | `"Kamil"` / `"private"` |
| `sellerTag` | `"Approved dealer"` (when present) |
| `businessFlag` | `false` |
| `hasPhone` / `hasChat` / `hasCourier` | `true` / `true` / `true` |
| `images` / `imageCount` | list of photo URLs / `6` |
| `keyParams` | `["Używany"]` |
| `status` | `"active"` |
| `isPromoted` | `true` |
| `createdAt` / `validToTime` | posting date / expiry date, both ISO |
| `url` | link to the listing on its OLX country site |
| `condition` / `previousPrice` | `"used"` / earlier price after a price drop (classic OLX sites) |
| `isHighlighted` / `isUrgent` / `isTopAd` / `promotionOptions` | paid-promotion flags and the options bought |
| `sellerCompany` / `sellerAbout` / `sellerIsOnline` / `shopSubdomain` | business seller profile details |
| `phones` | seller phone numbers, only with **Fetch seller phone number** (otherwise `null`) |

Some fields are country-specific: `score`, `kycVerified` and `sellerTag` come from India, `safedeal` and `delivery` from countries that support those programs. With Incremental mode on, each row also gets `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`.

### How to Use

1. Pick a **mode**: `search` (filters) or `url` (paste OLX search-result links).
2. For search mode, pick a **country** and fill in the filters you need; for URL mode, paste one or more links. Fields that belong to the other mode are ignored.
3. Set **Maximum listings** and, optionally, **Maximum pages** 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 by keyword and price in Poland:**

```json
{
  "mode": "search",
  "country": "pl",
  "query": "iPhone 13",
  "priceMax": 2500,
  "maxListings": 50
}
```

**Search a vehicle category in Romania with advanced filters:**

```json
{
  "mode": "search",
  "country": "ro",
  "categoryId": 9,
  "extraFilters": { "filter_enum_make": ["bmw"], "filter_float_year:from": 2018 },
  "sortBy": "price_asc",
  "maxListings": 100
}
```

**Paste search-result URLs and auto-paginate:**

```json
{
  "mode": "url",
  "urls": ["https://www.olx.pl/motoryzacja/samochody/bmw/"],
  "fetchDetails": true,
  "maxListings": 200
}
```

#### URL mode: supported link shapes

Paste any of these and the actor resolves what the page shows (category, city, search, filters, sort order) through the site itself, rather than guessing from the link text:

| Shape | Example |
|---|---|
| Category / subcategory | `https://www.olx.pl/motoryzacja/samochody/bmw/` |
| Top-level listing | `https://www.olx.pl/motoryzacja/samochody/` |
| Category + city | `https://www.olx.pl/nieruchomosci/mieszkania/warszawa/` |
| Free-text search | `https://www.olx.pl/oferty/q-iphone/` |
| Paginated (starts on that page) | `https://www.olx.pl/motoryzacja/samochody/bmw/?page=2` |
| Filtered and sorted | `https://www.olx.pl/motoryzacja/samochody/bmw/?search%5Bfilter_float_price%3Afrom%5D=10000&search%5Border%5D=filter_float_price%3Aasc` |
| Single listing | `https://www.olx.pl/d/oferta/ksiazka-encyklopedia-popularna-olesiejuk-CID751-IDRFFqw.html` |
| Seller page | `https://www.olx.pl/oferty/uzytkownik/1VRzN/` |
| Mobile host | `https://m.olx.pl/motoryzacja/samochody/bmw/` |
| Romania, Portugal, Ukraine (`/uk/`), Kazakhstan, Bulgaria | `https://www.olx.ro/auto-masini-moto-ambarcatiuni/autoturisme/` |

olx.uz category links also work. olx.in links are attempted but the site was not reachable on our last check, and olx.com.br links are blocked by the site. A link the actor cannot resolve (a page that does not exist, or one that is not a listing page) is skipped with a warning naming the link, never silently.

**Behaviour change:** URL mode with the `urls` field left untouched now scrapes one example of each shape above instead of failing with "no URLs". Also, category, listing, filtered and seller links used to be turned into a free-text keyword search (for example `/bmw/` became the keyword "bmw"); they now return the real category, search or seller listings, so output for saved URL-mode tasks can differ. Resolving a link reads the page once before paging through its results.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/olx-scraper").call(run_input={"mode": "search", "country": "pl", "query": "iPhone 13"})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(listing["title"], listing["price"], listing["priceCurrency"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/olx-scraper').call({ mode: 'search', country: 'pl', query: 'iPhone 13' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

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

#### Resume and recurring updates

- **Resume from a previous run** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset ID and already-saved listings are skipped, so you do not pay to re-scrape them.
- **Incremental mode** (`incrementalMode`) is for repeating the same search on a schedule. 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 search, and only with `emitExpired` on). `stateKey` names or shares the tracked state; leave it empty and the actor derives one from the search itself. Change detection ignores volatile fields (`scrapedAt`, `displayDate`, `score`, `searchUrl`, `isPromoted`, `isHighlighted`, `isUrgent`, `isTopAd`, `promotionOptions`, `pushupTime`, `sellerIsOnline`, `sellerLastLogin`, `vasTags`, `images`), which stay in the output but never cause `UPDATED`; add more with `ignoreFieldsForChanges` (plain name such as `sellerName`, or a dotted path to a nested value such as `params.value`). A pasted URL starting at `?page=N` with N above 1 is a partial scan and never produces `EXPIRED` rows. Only URL mode looks at pasted URLs for this; Search mode is never blocked by the example URL list. Category and brand URLs are now read as that category (not a text search across all categories), so the first Incremental run after this change on a state saved by an older version skips `EXPIRED` detection once, drops listings the new reading no longer returns from tracking without an `EXPIRED` row (nothing billed for them), and resumes normal `EXPIRED` tracking from the next run.

### 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 record, 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` (filters) or `url` (paste links). Only fields belonging to the chosen mode are used. |
| `urls` | array | one example of each supported shape (14 links) | OLX links to scrape (URL mode). Country is auto-detected per URL; each URL auto-paginates and a `?page=N` link starts on that page. Ignored in search mode. See "URL mode: supported link shapes" below. |
| `condition` | string | `any` | `new`, `used` or `damaged` (all countries except India and Brazil). |
| `sellerFilter` | string | `any` | `private` or `business` sellers only (all countries except India and Brazil). |
| `fetchPhone` | boolean | `false` | Also read the phone number the seller shows publicly on the listing. 1 extra request per listing; not for India and Brazil; hidden numbers stay empty. Billing is unchanged: one result per listing. |
| `country` | string | `pl` | Country to search: `pl`, `ro`, `pt`, `ua`, `bg`, `kz`, `uz`, `in` or `br`. Search mode only. |
| `query` | string | (none) | Free-text search keyword. Combined with category and city filters when present. |
| `categoryId` | integer | (none) | OLX numeric category ID. Find it by browsing OLX and reading the category page's URL or metadata. Not used for Brazil searches. |
| `categoryName` | string | (none) | Plain category name or path (for example `Samochody osobowe` or `motoryzacja/samochody`), looked up in the site's own category list. The run stops with a clear error if nothing matches. Ignored when `categoryId` is set. Not available for India and Brazil. |
| `cityId` | integer | (none) | Numeric city ID for classic countries (PL, RO, PT, UA, BG, KZ, UZ). |
| `cityName` | string | (none) | Plain city name (for example `Kraków`), looked up on the site's own city pages. The run stops with a clear error if the city is not found. Ignored when `cityId` is set. Not available for India and Brazil. |
| `locationId` | integer | (none) | Numeric location ID for India (state/city). Ignored for non-India searches. |
| `priceMin` | integer | (none) | Lower bound for price, in the country's local currency. |
| `priceMax` | integer | (none) | Upper bound for price, in the country's local currency. |
| `sortBy` | string | `default` | Result order: `default` (relevance), `created_desc`, `created_asc`, `price_asc` or `price_desc`. |
| `extraFilters` | object | (none) | Escape hatch for site-specific filters not exposed above. Keys are passed verbatim into the request URL; not validated. |
| `fetchDetails` | boolean | `false` | Fetch each listing's detail endpoint to fill in the complete description, full attribute list and any fields the search results leave empty. One extra request per listing. Each delivered listing whose detail endpoint is read is charged as one detail-page event on top of the per-result charge. |
| `maxListings` | integer | `20` | Stop after this many listings across the whole run. `0` means unlimited, capped only by `maxPages`. |
| `maxPages` | integer | (none, unlimited) | Per-search page cap. Leave empty to walk every result page until end of results or the site's own last-page signal. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run without re-scraping listings already saved there. |
| `incrementalMode` | boolean | `false` | Turn on for recurring monitoring of the same search: later runs return only new, updated and reappeared listings. |
| `stateKey` | string | (none) | Names or shares an incremental-mode state. Left empty, the actor derives one from the search itself. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) listings that have not changed since the last run, marked `UNCHANGED`. |
| `emitExpired` | boolean | `false` | Also return (and bill) listings no longer found after a complete scan, marked `EXPIRED`. |
| `ignoreFieldsForChanges` | string\[] | `[]` | Extra fields (name or dotted path) that never make a listing `UPDATED`. |
| `proxyConfiguration` | object | Apify Proxy | Connection settings. Apify Proxy is the default; if it is refused the actor escalates through backup connections (see Plan Requirement). |
| `mcpConnectors` | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. Does not affect the dataset. |

### Output Example

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

```json
{
  "listingId": "789456123",
  "country": "pl",
  "url": "https://www.olx.pl/d/oferta/iphone-13-pro-128gb-jak-nowy-CID123.html",
  "title": "iPhone 13 Pro 128GB, jak nowy",
  "description": "Sprzedam iPhone 13 Pro 128GB w kolorze grafitowym. Stan idealny, komplet akcesoriów w oryginalnym pudełku.",
  "createdAt": "2026-09-20T08:15:00+02:00",
  "lastRefreshTime": "2026-09-25T10:00:00+02:00",
  "validToTime": "2026-10-25T08:15:00+02:00",
  "price": 3200,
  "priceCurrency": "PLN",
  "priceDisplay": "3 200 zł do negocjacji",
  "priceNegotiable": true,
  "offerType": "offer",
  "categoryId": "1518",
  "categoryName": "Telefony i Akcesoria",
  "categoryPath": "Elektronika / Telefony i Akcesoria",
  "city": "Warszawa",
  "region": "Mazowieckie",
  "district": "Mokotów",
  "fullLocation": "Mokotów, Warszawa, Mazowieckie",
  "latitude": 52.1978,
  "longitude": 21.0224,
  "sellerId": "44556677",
  "sellerName": "Kamil",
  "sellerType": "private",
  "businessFlag": false,
  "hasPhone": true,
  "hasChat": true,
  "hasNegotiation": true,
  "hasCourier": true,
  "images": ["https://ireland.apollo.olxcdn.com/v1/files/sample/image;s=1000x700"],
  "imageCount": 6,
  "params": [{ "key": "state", "name": "Stan", "value": "Używany", "type": "select" }],
  "keyParams": ["Używany"],
  "status": "active",
  "isPromoted": true,
  "searchUrl": "https://www.olx.pl/api/v1/offers/?category_id=1518&offset=0&limit=40",
  "scrapedAt": "2026-09-27T20:00:00.000Z"
}
```

#### Output views

The dataset has five views: **Overview** (price, title, location), **Seller info**, **Media** (images), **Changes (incremental)** (`changeType`, `changedFields`, first and last seen) and **Listing detail** (description and full attributes).

### Plan Requirement

The default Apify Proxy setting is what the actor starts on. Poland (olx.pl) accepts it only intermittently: some connections get a bare 403 refusal and the next one is served, so the actor retries on a fresh connection. Romania (olx.ro) was served on it in our checks, and the other classic countries are read the same way.

**Brazil (olx.com.br) is currently blocked by the site on every connection type we tested, including residential with country BR.** A Brazil run therefore fails with: "No results: olx.com.br refused every connection tried this run, including residential, so no Brazilian listings could be read. This is a block by the site, not an empty search. Other OLX countries are unaffected; try again later or use a different country." India (olx.in) was not reachable on our last check.

If the default connection is refused, the actor escalates through backup connections before giving up, and the run log says so when it happens, so a successful run never silently hides which connection carried it. On a free Apify plan (no residential access) the log warns: "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." If a free-plan run then loads nothing, it fails with: "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 OLX pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again."

If your plan has no proxy access at all and every connection is refused, the run stops with: "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: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid." Any other run where no results page loads fails with: "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."

### FAQ

#### How much does it cost?

You pay per listing returned, plus a small actor start fee, on the pay per event model. With **Fetch full details** on, each delivered listing whose detail endpoint is read is also charged as one detail-page event; a pasted single-listing URL is itself read from its detail endpoint, so its row carries the detail charge too. Phone numbers and connector exports do not add to the price. Listings whose detail endpoint is refused, and listings incremental mode suppresses as unchanged, are never charged. The **Pricing** tab always shows the current rates. Use **Maximum listings** and **Maximum pages** to cap the cost of any run.

#### Is it legal to scrape OLX?

This actor collects only publicly available classifieds data. You are responsible for how you use it: follow OLX's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Do not use scraped contact details to spam sellers.

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

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Later runs then return only new, updated and reappeared listings by default, and unchanged ones are not billed unless you opt in.

#### Why are some of my filter fields being ignored?

Search fields (country, query, category, city, price, sort, extra filters) only apply in search mode, and the URLs field only applies in URL mode. Filling in fields from the other mode has no effect; check **Mode** first if a run isn't behaving as expected.

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

The actor fails when scraping cannot start or nothing could be read: your custom proxy URLs could not be set up, URL mode was chosen with no URLs, an unsupported country was set, the `resumeFromRunId` you pasted could not be loaded, Incremental mode was combined with `resumeFromRunId` on a search that already has saved state, or not a single results page could be read (a connection problem, which the message says is not an empty search). If a results page loads and simply has no listings, that is a genuine "nothing matched" and the run succeeds with an empty dataset. If some pages load and a later one keeps failing after retries, the run keeps what it collected and warns that the results may be 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 classifieds data?

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

<table>
<tr><td>🏷️ <a href="https://apify.com/abotapi/marktplaats-nl-scraper"><b>Marktplaats.nl Scraper</b></a><br>From $1/1K. Scrape Marktplaats.nl listings by keyword or URL. Extract title, price...</td><td>🏷️ <a href="https://apify.com/abotapi/2dehands-2ememain-scraper"><b>2dehands &amp; 2ememain</b></a><br>Scrape classifieds from 2dehands.be and 2ememain.be by keyword, filters, or URLs. Returns...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/avito-ru-scraper"><b>Avito.ru Scraper</b></a><br>From $1/1K. Scrape structured listings from Avito.ru by region, category, filters, or...</td><td>🏷️ <a href="https://apify.com/abotapi/gumtree-au-scraper"><b>Gumtree AU Scraper</b></a><br>Scrape gumtree.com.au classifieds across every vertical: for-sale goods, motors, real...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/finn-no-jobs-scraper"><b>FINN.no Jobs Scraper</b></a><br>Scrape active FINN.no job listings by search filters or URL. Extract 30+ fields including...</td><td>🏷️ <a href="https://apify.com/abotapi/kleinanzeigen-de-scraper"><b>Kleinanzeigen.de Scraper</b></a><br>From $1/1K. Scrape Kleinanzeigen.de ads by keyword, category, location, price, seller...</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/olx-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 how to find listings. Only fields belonging to the chosen mode are used.

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

OLX URLs to scrape, auto-paginating forward until Maximum Pages or Maximum Listings is reached. Supported shapes: category/subcategory pages, category + city pages, free-text /oferty/q-<term>/ searches, filtered/sorted search URLs (search\[...] parameters are honoured), ?page=N (starts on that page), seller pages (/oferty/uzytkownik/<code>/), single listing pages (/d/oferta/...-ID<code>.html), the m. mobile host, and olx.pl / .ro / .pt / .ua (incl. /uk/) / .kz / .bg / .uz. Country is auto-detected. Ignored in Search mode. Left untouched, the field scrapes one example of each shape.

## `country` (type: `string`):

Country to search. Each country runs on the local OLX site (olx.pl, olx.ro, etc.). India runs on a separate API endpoint with the same output schema. (Search mode only — ignored in URL mode.)

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

Free-text search keyword (e.g. "BMW X5", "iPhone 15", "apartment Warsaw"). Combined with category and city filters when present. Leave empty to browse a category without keyword filtering.

## `categoryId` (type: `integer`):

OLX numeric category ID. Optional. Examples (Poland): 1605=Apartments, 5=Auto parts, 84 (India)=Cars. To find a category ID, browse OLX, open the category page, and look at the URL or the metadata.

## `categoryName` (type: `string`):

Plain category name or path as shown on the site, e.g. "Samochody osobowe" or "motoryzacja/samochody". Resolved through OLX's own category list; the run stops with a clear error if nothing matches. Ignored when Category ID is set. Poland, Romania, Portugal, Ukraine, Bulgaria, Kazakhstan, Uzbekistan.

## `cityId` (type: `integer`):

Numeric city ID for filtering by city (PL, RO, PT, UA, BG, KZ, UZ). Look up city IDs by inspecting an OLX search URL after picking a city. Ignored for India (use locationId instead).

## `cityName` (type: `string`):

Plain city name, e.g. "Warszawa" or "Kraków". Resolved through OLX's own city pages; the run stops with a clear error if the city is not found. Ignored when City ID is set.

## `locationId` (type: `integer`):

Numeric location ID for olx.in (state/city). Example: 2001173=Tamil Nadu. Ignored for non-India searches.

## `priceMin` (type: `integer`):

Lower bound for price (in the country's local currency). Optional.

## `priceMax` (type: `integer`):

Upper bound for price (in the country's local currency). Optional.

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

How to order results. Default uses the country's relevance ranking.

## `extraFilters` (type: `object`):

Optional escape hatch for site-specific filters not exposed above. Keys are passed verbatim into the request URL. Examples (Pattern A countries): {"filter_enum_make": \["bmw"], "filter_enum_fuel": \["diesel"], "filter_float_year:from": 2018}. Use sparingly; keys are not validated.

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

Only listings in this condition (Poland, Romania, Portugal, Ukraine, Bulgaria, Kazakhstan, Uzbekistan). Ignored for India and Brazil. Same as the site's own condition filter.

## `sellerFilter` (type: `string`):

Only listings from private sellers or from businesses (same countries as Condition). Ignored for India and Brazil.

## `fetchPhone` (type: `boolean`):

Also read the phone number the seller shows publicly on the listing (what any visitor sees after tapping show phone). Adds 1 HTTP call per listing and fills the phones field. Not available for India and Brazil; listings with a hidden phone stay empty.

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

Fetch each listing's detail endpoint for the freshest revision, complete description, and full attribute list. Adds 1 extra HTTP call per listing. Each delivered listing whose detail endpoint is read is charged as one detail-page event on top of the per-result charge; listings whose detail endpoint is refused, and listings incremental mode suppresses as unchanged, are not charged.

## `maxListings` (type: `integer`):

Stop after scraping this many listings across the whole run. 0 = unlimited (capped only by maxPages).

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

Per-search page cap. Each page returns ~10 (Pattern A) or ~40 (India) listings. Leave empty to walk every result page (stopped only by maximum listings, end of results, or the site's own last-page signal). Pattern A countries cap server-side at offset 1000 — narrow your filters to see further.

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

The default Apify Proxy setting is sufficient for 8 of 9 countries (PL, RO, PT, UA, BG, KZ, UZ, IN); if your plan does not include proxy access, the run stops with a message that a paid plan with proxy access is required. For Brazil (olx.com.br) you MUST switch the proxy field to Apify Residential with country=BR — the upstream edge rejects datacenter IPs there. Residential also helps if you hit transient rate limits on a high-traffic country. Custom proxy URLs are accepted as well.

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

Paste a previous run ID (or its dataset ID) to continue a full-catalogue scrape without re-scraping listings already saved. Already-seen listings (by listing ID) are skipped; new ones are appended. Leave empty for a fresh run. For recurring daily/weekly monitoring of the same search, use Incremental mode below instead.

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

Turn this on for daily or recurring monitoring of the same search. The first run returns every matching listing as NEW. Later runs return only NEW, UPDATED, and REAPPEARED listings by default. A listing is UPDATED when its content changed (title, price, description, seller, parameters, location, status and similar); the changed field names are listed in changedFields. These never trigger UPDATED, although they stay in the output: scrapedAt, displayDate, score, searchUrl, isPromoted, isHighlighted, isUrgent, isTopAd, promotionOptions, pushupTime, sellerIsOnline, sellerLastLogin, vasTags and image URLs (images). Add more with "Ignore fields when detecting changes" below. Turn on "Emit unchanged" or "Emit expired" below only when you also want those listings returned (and billed). State is kept separately per country/query/filters/URL and per fetchDetails setting - 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 let the actor derive a key automatically from the country/query/filters/URL and fetchDetails settings — different searches then never mix state with each other.

## `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 specifically want the 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 search — not when Maximum listings capped it, a page failed to load, or Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

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

Field names that must NOT make a listing count as UPDATED, in addition to the built-in volatile fields. Use a plain name (sellerName) or a dotted path to a nested value (params.value). The fields still appear in the output. Leave empty to use only the defaults.

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

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. 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 item pages are created. Required to enable the Notion export; ignored by other connectors.

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

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

## Actor input object example

```json
{
  "mode": "search",
  "urls": [
    "https://www.olx.pl/motoryzacja/samochody/bmw/",
    "https://www.olx.pl/motoryzacja/samochody/",
    "https://www.olx.pl/nieruchomosci/mieszkania/warszawa/",
    "https://www.olx.pl/oferty/q-iphone/",
    "https://www.olx.pl/motoryzacja/samochody/bmw/?page=2",
    "https://www.olx.pl/motoryzacja/samochody/bmw/?search%5Bfilter_float_price%3Afrom%5D=10000&search%5Border%5D=filter_float_price%3Aasc",
    "https://www.olx.pl/d/oferta/ksiazka-encyklopedia-popularna-olesiejuk-CID751-IDRFFqw.html",
    "https://www.olx.pl/oferty/uzytkownik/1VRzN/",
    "https://m.olx.pl/motoryzacja/samochody/bmw/",
    "https://www.olx.ro/auto-masini-moto-ambarcatiuni/autoturisme/",
    "https://www.olx.pt/carros-motos-e-barcos/carros/",
    "https://www.olx.ua/uk/transport/legkovye-avtomobili/",
    "https://www.olx.kz/transport/legkovye-avtomobili/",
    "https://www.olx.bg/ads/"
  ],
  "country": "pl",
  "sortBy": "default",
  "condition": "any",
  "sellerFilter": "any",
  "fetchPhone": false,
  "fetchDetails": false,
  "maxListings": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

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

No description

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

No description

## `seller` (type: `string`):

No description

## `media` (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",
    "urls": [
        "https://www.olx.pl/motoryzacja/samochody/bmw/",
        "https://www.olx.pl/motoryzacja/samochody/",
        "https://www.olx.pl/nieruchomosci/mieszkania/warszawa/",
        "https://www.olx.pl/oferty/q-iphone/",
        "https://www.olx.pl/motoryzacja/samochody/bmw/?page=2",
        "https://www.olx.pl/motoryzacja/samochody/bmw/?search%5Bfilter_float_price%3Afrom%5D=10000&search%5Border%5D=filter_float_price%3Aasc",
        "https://www.olx.pl/d/oferta/ksiazka-encyklopedia-popularna-olesiejuk-CID751-IDRFFqw.html",
        "https://www.olx.pl/oferty/uzytkownik/1VRzN/",
        "https://m.olx.pl/motoryzacja/samochody/bmw/",
        "https://www.olx.ro/auto-masini-moto-ambarcatiuni/autoturisme/",
        "https://www.olx.pt/carros-motos-e-barcos/carros/",
        "https://www.olx.ua/uk/transport/legkovye-avtomobili/",
        "https://www.olx.kz/transport/legkovye-avtomobili/",
        "https://www.olx.bg/ads/"
    ],
    "country": "pl",
    "proxyConfiguration": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/olx-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",
    "urls": [
        "https://www.olx.pl/motoryzacja/samochody/bmw/",
        "https://www.olx.pl/motoryzacja/samochody/",
        "https://www.olx.pl/nieruchomosci/mieszkania/warszawa/",
        "https://www.olx.pl/oferty/q-iphone/",
        "https://www.olx.pl/motoryzacja/samochody/bmw/?page=2",
        "https://www.olx.pl/motoryzacja/samochody/bmw/?search%5Bfilter_float_price%3Afrom%5D=10000&search%5Border%5D=filter_float_price%3Aasc",
        "https://www.olx.pl/d/oferta/ksiazka-encyklopedia-popularna-olesiejuk-CID751-IDRFFqw.html",
        "https://www.olx.pl/oferty/uzytkownik/1VRzN/",
        "https://m.olx.pl/motoryzacja/samochody/bmw/",
        "https://www.olx.ro/auto-masini-moto-ambarcatiuni/autoturisme/",
        "https://www.olx.pt/carros-motos-e-barcos/carros/",
        "https://www.olx.ua/uk/transport/legkovye-avtomobili/",
        "https://www.olx.kz/transport/legkovye-avtomobili/",
        "https://www.olx.bg/ads/",
    ],
    "country": "pl",
    "proxyConfiguration": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/olx-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",
  "urls": [
    "https://www.olx.pl/motoryzacja/samochody/bmw/",
    "https://www.olx.pl/motoryzacja/samochody/",
    "https://www.olx.pl/nieruchomosci/mieszkania/warszawa/",
    "https://www.olx.pl/oferty/q-iphone/",
    "https://www.olx.pl/motoryzacja/samochody/bmw/?page=2",
    "https://www.olx.pl/motoryzacja/samochody/bmw/?search%5Bfilter_float_price%3Afrom%5D=10000&search%5Border%5D=filter_float_price%3Aasc",
    "https://www.olx.pl/d/oferta/ksiazka-encyklopedia-popularna-olesiejuk-CID751-IDRFFqw.html",
    "https://www.olx.pl/oferty/uzytkownik/1VRzN/",
    "https://m.olx.pl/motoryzacja/samochody/bmw/",
    "https://www.olx.ro/auto-masini-moto-ambarcatiuni/autoturisme/",
    "https://www.olx.pt/carros-motos-e-barcos/carros/",
    "https://www.olx.ua/uk/transport/legkovye-avtomobili/",
    "https://www.olx.kz/transport/legkovye-avtomobili/",
    "https://www.olx.bg/ads/"
  ],
  "country": "pl",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/olx-scraper --silent --output-dataset

```

## MCP server setup

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