# OLX Poland Scraper & New-Listing Monitor (OLX.pl) (`neverempty/olx-poland-listings-monitor`) Actor

OLX.pl scraper for resellers, deal hunters and price trackers: listings by search term, category, city or pasted OLX.pl URL with price, condition, location, dates, photos and business/private seller. Watch mode returns only listings created since your last check, and price drops.

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

## Pricing

from $0.90 / 1,000 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

## OLX Poland Scraper & New-Listing Monitor (OLX.pl)

This OLX.pl scraper returns listings from OLX Poland as clean JSON, CSV or Excel rows: search by words, category, city or voivodeship, price range, condition and private/business seller, or paste any OLX.pl search address. It also works as an OLX new-listing alert: run it on a schedule in **watch mode** and each run returns only the listings created since your last check, plus price drops.

No login, no cookies, no API key needed. One row per listing with price in PLN, condition, city, district and voivodeship, created and refreshed time, all photos, category attributes and a business-or-private flag.

### What you can do with it

- **Deal alerts on OLX.pl**: watch "iphone 13" in Warszawa under 2,000 zł, or a whole category, every 10 minutes and get only listings that are really new. Old listings that a seller pushes back to the top are not sent again as new.
- **Reselling and sourcing**: pull up to 1,000 listings per search with price, condition and photos into a sheet, a database or a Telegram/Slack bot.
- **Price tracking**: watch mode returns a `price-drop` row with the previous price and the difference. Every row also carries the earlier price OLX.pl itself shows when a seller lowered it (`previousPriceShown`).
- **Market research**: how many listings OLX.pl shows for a search (`listingsInSearch`), price spread, condition mix, and how many sellers are businesses.

### Why this one

- **Search exactly like the site**: categories and cities are turned into OLX.pl's own IDs, and a pasted search address is translated to the same request the OLX.pl page makes (checked against the page's own data). Price, condition and seller filters are applied by OLX.pl, so every page you pay for is in range.
- **Honest "new"**: new means *created after your watch started*, using the listing's creation time, not "appeared near the top".
- **No silent surprises**: when OLX.pl finds nothing for your words it shows unrelated listings instead; this Actor recognises that and returns a free `no-results` row rather than selling them. An unknown category or city, or a filter OLX.pl refuses, also comes back as a free row that says why.
- **Every row says whether its title names what you searched** (`queryInTitle`), and `requireWordsInTitle` keeps only those.
- **Private sellers stay private**: no seller names, user IDs or phone numbers. For businesses, the public business name is included.

### Input

| Field | What it does |
|---|---|
| `searchQueries` | Search words, one per line (`iphone 13`, `rower szosowy`, `kanapa`). |
| `categories` | OLX.pl category path from the address: `elektronika/telefony`, `motoryzacja/samochody`, `sport-hobby/rowery`. |
| `locations` | City or voivodeship: `warszawa`, `Kraków`, `gdansk`, `mazowieckie`. |
| `searchUrls` | Or paste an OLX.pl search address, such as `https://www.olx.pl/elektronika/telefony/warszawa/q-iphone-13/?search[filter_float_price:to]=2000`. Its category, place, words, price range, condition, seller type and sort order are kept, so you get the same results as on the page (no sort order in the address means OLX.pl's default order). |
| `minPrice` / `maxPrice` | Price range in PLN, applied by OLX.pl. |
| `condition` | `any`, `new`, `used`, `almost-new`, `refurbished`, `damaged`. |
| `sellerType` | `any`, `private` or `business`. |
| `sort` | `newest` (default), `relevance`, `price-asc`, `price-desc`, for searches built from the fields above. |
| `maxResultsPerSearch` | Listings per search, 1 to 1,000 (default 40). |
| `requireWordsInTitle` | Only listings whose title contains every search word. |
| `includePromoted` | Also return OLX.pl's paid promoted listings (about 12 per page, outside the sort order). Off by default. |
| `onlyNew` | Watch mode (below). |
| `includePriceDrops`, `watchPages`, `watchName`, `resetMonitoringState` | Watch mode settings. |

Each search word is combined with each category and each location; up to 10 searches per run.

Example: `{ "searchQueries": ["iphone 13"], "maxResultsPerSearch": 40, "requireWordsInTitle": true }` (only listings whose title names every search word). Add `"locations": ["warszawa"], "maxPrice": 2000, "condition": "used"` to narrow it.

### Output

| Field | Meaning |
|---|---|
| `listingId`, `url`, `title` | OLX.pl listing ID, link and title. |
| `price`, `currency`, `priceType`, `priceLabel`, `negotiable` | Price in PLN. `priceType` is OLX.pl's own word: `price`, `arranged` (negotiable), `free` (price 0), `exchange` (swap, price `null`). |
| `previousPriceShown` | The earlier price OLX.pl shows next to a lowered price. |
| `condition`, `conditionLabel` | `new`, `used`, … and the Polish label (`Nowe`, `Używane`). |
| `description` | Listing text as plain text (emails and phone numbers removed). |
| `city`, `district`, `region` | Location (region is the voivodeship). |
| `categoryId`, `categoryType` | OLX.pl category of the listing. |
| `createdAt`, `refreshedAt`, `validTo` | When the listing was created, last refreshed by the seller, and when it expires. |
| `imageUrl`, `imageUrls`, `photoCount` | Photos, full size. |
| `attributes` | Category fields such as model, memory, size, brand, as shown on OLX.pl. |
| `isBusiness`, `businessName` | Business or private seller; the public business name for businesses only. |
| `olxDelivery` | `true` when the listing offers OLX delivery (Przesyłka OLX). |
| `isPromoted`, `queryInTitle` | Paid promoted slot; title contains all search words. |
| `positionInSearch`, `listingsInSearch` | Position in the results and the total OLX.pl shows. |
| `changeType`, `previousPrice`, `priceChange` | Watch mode: `new` or `price-drop`, with the remembered price and the difference. |
| `searchQuery`, `searchCategory`, `searchCategoryId`, `searchLocation`, `searchUrl`, `watchName`, `checkedAt` | Which search the row came from. |

In the pages read while building this Actor (283 listings, 9 pages, October 2026), 98% had a price and a condition, 100% had a city and photos, 63% a district, 35% came from businesses and 75% offered OLX delivery.

### Watch mode: new OLX.pl listings only

Turn on `onlyNew` and schedule the run (every 5 to 60 minutes is typical).

1. **First run**: remembers what is listed now and returns nothing. It is free: no rows, no charge.
2. **Every later run** returns:
   - `new`: a listing **created after the newest listing your first check saw**, that you have not received yet. A listing that a seller refreshes or promotes back to the top keeps its old creation time and is not called new.
   - `price-drop`: a listing seen before whose price went down, with `previousPrice` and `priceChange`. Price rises are remembered, not returned.
3. A check that finds nothing returns one free `no-new-listings` row and is charged as one small `search-checked` event.

Watch mode always reads newest first and stops once it reaches listings older than the previous check (with a 30-minute overlap, because OLX.pl adds new listings to its search results a few minutes after they are created). Listings that could not be returned because of your run's maximum charge are not marked as delivered; the next run returns them. Use `watchName` to keep separate watches for the same search.

### Rows that explain instead of data (never charged)

`no-results`, `not-found` (unknown category or place, a filter OLX.pl refused, or an answer for a different search), `watch-started`, `no-new-listings`, `watch-coverage`, `incomplete` (a later page could not be read), `blocked` (OLX.pl showed a check page; this Actor does not solve it), `unreadable`, `not-allowed-by-robots-txt`, `bad-input`, `budget-reached`, `more-not-returned`. Each has a `note` that says what happened.

### FAQ

**Is there an OLX Poland API I can use instead of writing a scraper?**
This Actor is that API: send a search (words, category, city, price, condition) and get JSON rows back, or call it from the Apify API, Make, Zapier or n8n on a schedule.

**How do I get alerts for new OLX.pl listings?**
Run it in watch mode on a schedule and connect the dataset to email, Slack, Telegram or a webhook. Each run returns only listings created since the last check.

**Can I scrape a whole OLX.pl category or city?**
Yes. Leave `searchQueries` empty and set `categories` and/or `locations`. OLX.pl serves up to 1,000 listings per search, so split very large categories by city or price range.

**Does it return seller phone numbers?**
No. This Actor returns no personal data of private sellers, and phone numbers or emails written into titles and descriptions are replaced with `[phone removed]` / `[email removed]`.

### Good to know

- OLX.pl serves at most 1,000 listings per search (25 pages of 40). For more, split by city, voivodeship or price range.
- OLX.pl's search also matches listing descriptions and close words, so not every result names your words in its title; use `queryInTitle` or `requireWordsInTitle`.
- `newest` is OLX.pl's newest-first order, which also moves refreshed listings up; `createdAt` tells you which ones are really new.
- OLX.pl shows a new listing in its search results roughly 10 to 15 minutes after it is created (measured October 2026), so a watch sees it from then on.
- Price drops are seen for listings on the pages a watch reads (newest first).
- Pasted addresses with filters this Actor cannot pass on (for example `search[photos]`) are refused with a message instead of being silently dropped. Districts in the path are not supported; use the city.
- Unofficial: this Actor is not affiliated with OLX Group. It reads public search results only, and checks OLX.pl's robots.txt on every run.

### 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

## `searchQueries` (type: `array`):

What to search for on OLX.pl, one term per line (for example iphone 13, rower szosowy, kanapa). Each term is searched in every category and location below. Leave empty to list whole categories or locations instead; if everything is empty, the example term iphone 13 is searched.

## `categories` (type: `array`):

OLX.pl categories as they appear in the site's address, one per line: elektronika/telefony for https://www.olx.pl/elektronika/telefony/, or motoryzacja/samochody. A whole address of a category page also works. Leave empty to search all categories.

## `locations` (type: `array`):

Polish cities or voivodeships, one per line, as written in OLX.pl addresses or normally: warszawa, Kraków, gdansk, mazowieckie. Leave empty to search all of Poland.

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

Or paste the address of an OLX.pl search page, such as https://www.olx.pl/elektronika/telefony/warszawa/q-iphone-13/?search\[filter_float_price:to]=2000. Its category, location, search words, price range, condition, seller type and sort order are kept. Filters this Actor cannot pass on to OLX are refused with a message instead of being dropped.

## `minPrice` (type: `integer`):

Only listings priced at or above this many złoty. OLX.pl applies it, so every page returned is in range. Empty means no minimum.

## `maxPrice` (type: `integer`):

Only listings priced at or below this many złoty. Empty means no maximum.

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

Only listings in this condition (the Stan field on OLX.pl). Any returns all. Some categories, such as jobs or real estate, have no condition field and OLX.pl refuses this filter there; the row then says so.

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

Only listings from private people or only from businesses (firms). Any returns both.

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

Order of the results for searches built from search terms, categories and locations (a pasted address keeps its own order). Newest is OLX.pl's newest first (listings refreshed by their seller also move up). Watch mode always reads newest first.

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

How many listings to return for each search, from 1 to 1000. OLX.pl shows 40 per page and at most 1,000 per search. In watch mode, the most rows one check returns per search.

## `requireWordsInTitle` (type: `boolean`):

OLX.pl's search also matches descriptions and similar words, so some results do not name what you searched for. Turn on to return (and pay for) only listings whose title contains every search word. Off by default; every row says queryInTitle either way.

## `includePromoted` (type: `boolean`):

OLX.pl adds about 12 paid promoted listings to each page of results, outside the sort order. Off by default, so you get the search results in the order you chose. Turn on to get them too, marked isPromoted.

## `onlyNew` (type: `boolean`):

Run the same search on a schedule. The first run only remembers what is listed now and returns nothing, free of charge. Every later run returns only listings created after the watch started, and listings whose price went down. A check that finds nothing is charged as one small search-checked event instead of rows.

## `includePriceDrops` (type: `boolean`):

In watch mode, also return listings seen before when their price went down, with the previous price and the difference. Price rises are remembered but not returned.

## `watchPages` (type: `integer`):

The most pages (40 listings each, newest first) a watch reads per search on each check, from 1 to 25. A check stops early once it reaches listings older than the previous check, so this only matters for busy searches checked rarely.

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

Optional. A name for this watch (letters, digits, dot, dash, underscore). Two schedules with the same search but different names keep separate memories. Empty uses one shared default watch.

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

Start this watch over: the run is treated as the first check again and only remembers what is listed now. Off by default.

## Actor input object example

```json
{
  "searchQueries": [
    "iphone 13"
  ],
  "condition": "any",
  "sellerType": "any",
  "sort": "newest",
  "maxResultsPerSearch": 40,
  "requireWordsInTitle": true,
  "includePromoted": false,
  "onlyNew": false,
  "includePriceDrops": true,
  "watchPages": 3,
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per OLX.pl listing: listing ID, title, price in PLN with the price type (fixed, negotiable, free, exchange), the earlier price OLX.pl shows, condition, city, district, voivodeship, created and refreshed time, photos, category, attributes, business or private seller (business name for businesses only), OLX delivery flag, link and position. Watch mode marks rows new or price-drop (with the previous price). A search with no listings, nothing new, an unknown category or place, a refused filter 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 = {
    "searchQueries": [
        "iphone 13"
    ],
    "requireWordsInTitle": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/olx-poland-listings-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 = {
    "searchQueries": ["iphone 13"],
    "requireWordsInTitle": True,
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/olx-poland-listings-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 '{
  "searchQueries": [
    "iphone 13"
  ],
  "requireWordsInTitle": true
}' |
apify call neverempty/olx-poland-listings-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/olx-poland-listings-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/etQoB9Rgk4Tmv8lln/builds/NktS2TCLCHHvJDi5q/openapi.json
