# Otodom Scraper — Poland Property, Agent Phones & Monitor (MCP) (`amygraphics/otodom-poland-scraper`) Actor

Otodom.pl listings for all Poland: price, price/m2, area, rooms, photos + the agency or owner phone number (100% hit rate in tests), GPS and full descriptions. Monitor mode: new listings, price drops & removed ads. $1.5 per 1 000 listings.

- **URL**: https://apify.com/amygraphics/otodom-poland-scraper.md
- **Developed by:** [Zakaria el bouami](https://apify.com/amygraphics) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Otodom Poland Property Scraper — Listings, Agent Phones & Monitor (MCP)

Scrape **Otodom.pl**, Poland's #1 property portal: flats, houses, plots, commercial units, garages
and rooms — for sale or for rent, in every voivodeship — one clean row per listing, with the
**agency or owner phone number**, full descriptions and GPS.

**Find → Compare → Monitor → Alert.**

| What you get | Why it matters |
|---|---|
| **Phone number** (`phone`, `phones`) + agent name + agency profile | Verified on **100 % of listings tested** — agency, developer and private ads alike. No browser, no click-through, no login |
| Price in PLN, **price per m²**, monthly rent / charges, hidden-price flag | Real market comparison in one column |
| Area, terrain area, rooms, floor, **build year**, building floors, ownership, extras (balcony, basement, lift…) | Property-level detail without opening the ad |
| Seller block: **agency / developer / private owner**, agency id and slug, advert owner | Lead generation and agency mapping |
| Location to the **district and street**, plus **GPS coordinates** | Maps, heat-maps, catchment analysis |
| Photos (large), floor plans, tags, full description, dates (`createdAt`, `daysOnMarket`) | Content, freshness and “how long has this been on the market” |
| **Monitor mode**: new · price dropped / increased (with delta & %) · updated · closed | Daily market intelligence that only bills the deltas |
| Whole-market reach | Poland-wide flats-sale scope = **152 000+ listings**, reachable in one run |

### Hard problems this Actor solves

1. **The deep-pagination wall.** Otodom serves 36 listings per page — **72 with `limit=72`** (verified)
   — and silently re-serves the last page beyond **page ~2 118**. This Actor knows the wall, detects
   page repetition by listing-id overlap, and says so honestly instead of looping.
2. **Location names → Otodom paths.** Otodom rejects plain city names; it wants its own paths
   (`mazowieckie/warszawa`). The Actor ships a built-in map of the 16 voivodeships and 160+ Polish
   cities, accepts explicit paths too, and for anything else falls back to **Otodom's own locations
   sitemap** (cached between runs). Every candidate path is verified live before use.
3. **Filters that actually work** (verified against the live site): price min/max, area min/max,
   rooms, primary vs secondary market, agency vs private owner, and sorting by price or by price per m².
4. **Contacts without a browser.** One plain HTTP request per listing returns the phone number, the
   agent's name, the agency profile, the full description, building attributes and coordinates —
   no headless browser, no residential proxy, no login.

### Quick examples

- **Warsaw flats up to 800 000 PLN** — `locations: ["warszawa"]`, `priceMax: 800000`, `rooms: [2,3]`.
- **Kraków rentals** — `operation: rent`, `locations: ["kraków"]`, `priceMax: 3500`.
- **New developments only** — `market: primary`, `locations: ["gdansk"]`.
- **Private owners (no agency)** — `ownerType: private` — phones included for those too.
- **Whole voivodeship** — `locations: ["mazowieckie"]` (or any explicit path like `slaskie/katowice`).
- **Daily deal watch** — `mode: monitor` on a schedule: only new listings, price cuts and closed ads
  come back (and are billed).
- **Your own search** — paste any Otodom search URL into `searchUrl`.

### Inputs that matter

- `locations`: city (`warszawa`, `kraków`, `łódź`), voivodeship (`mazowieckie`), or an Otodom path
  (`mazowieckie/warszawa`, `slaskie/katowice`). Accents are fine — they are normalised.
- `estateType`: `mieszkanie`, `dom`, `dzialka`, `lokal`, `garaz`, `pokoj`.
- `limit`: 72 (recommended) or 36 listings per request.
- `maxPages` / `maxResults`: your cost and volume guard-rails.
- `includeDetails`: on by default — it is what brings the phone number, description and coordinates.
- `mode: monitor` + optional `stateKey` for delta runs.

### Automation, MCP & scheduling

- **Schedule** it daily; monitor mode returns (and charges) only the deltas.
- **Integrations**: Google Sheets, Airtable, Slack, n8n, Zapier or your own API — one click.
- **MCP-ready for AI agents** — exposed through Apify's Model Context Protocol server as
  `amygraphics/otodom-poland-scraper`, so agents (Claude, Cursor, VS Code, custom agents) can find it,
  run it and pay autonomously (this Actor is whitelisted for agentic payments).

```json
{
  "mcpServers": {
    "apify": { "url": "https://mcp.apify.com?tools=amygraphics/otodom-poland-scraper" }
  }
}
```

Ask it things like:

- *"Find every 3-room flat for sale in Warsaw under 1 200 000 PLN and give me the agencies' phone numbers."*
- *"Which Kraków rentals dropped their price this week? Run it every morning."*
- *"List all new-build apartments in Gdańsk above 60 m² with the developer's sales-office phone."*

### Pricing

Pay-per-event: **$0.001** start + **$0.0015 per listing** (`property-extracted`) — **$1.50 per
1 000 listings**, which is below every comparable Otodom scraper in the Store (see below) while
including phone numbers, full descriptions and the monitor. Monitor mode only charges for new,
changed and closed listings.

### How it compares (honest table, checked 2026-10-08)

| Actor | Price / 1 000 | Phones | Monitor | Notes |
|---|---|---|---|---|
| **this Actor** | **$1.50** | ✅ 100 % (agency, developer, private) | ✅ new / price-drop / closed | 84 fields, whole-market reach, GPS, floor plans |
| trev0n/otodom-scraper (leader, 42 users/30 d) | $4.00 | — | — | 30+ fields, “seller info” |
| highbrow_fame/otodom-properties | $1.20 | ❌ (“No Phones” in its own title) | — | listing data only |
| fatihtahta/otodom-pl-scraper | $4.99 | — | — | “enterprise grade” |
| clearpath, scrapers_lat (1 user each) | varies | ✅ | — | same phone idea, minimal adoption |

### Fair use

Only publicly visible search results and listing pages are fetched, without login, at a polite rate
(0.6 s between search pages, 0.35 s between listings). Otodom's `robots.txt` is respected: `/ajax/`,
`/api/query`, login and admin paths are never touched. **Privacy note:** phone numbers published on
ads are business contact data for agencies and developers, but a private seller's phone number is
**personal data** — if you plan outreach, you are responsible for complying with GDPR/ePrivacy and
Polish marketing rules, and with Otodom's terms of use.

***

### Polski (skrót)

**Scraper Otodom.pl** dla całej Polski: mieszkania, domy, działki, lokale, garaże i pokoje — na
sprzedaż i wynajem. Każdy wiersz zawiera **numer telefonu agencji lub właściciela** (100 % skuteczności
w testach), cenę i cenę za m², metraż, pokoje, piętro, rok budowy, udogodnienia, opis, zdjęcia i
**współrzędne GPS**. Tryb **monitor**: nowe oferty, zmiany ceny i oferty usunięte. Obsługa nazw miast
po polsku („Łódź”, „Kraków”, „Bielsko-Biała”). **1,50 $ za 1 000 ofert.**

#### Keywords

otodom scraper · otodom api · mieszkania warszawa · nieruchomości Polska · ceny mieszkań ·
telefon agencji nieruchomości · dane rynkowe · monitor cen · MCP.

### Français (bref)

**Scraper Otodom.pl (Pologne)** : appartements, maisons, terrains, locaux, garages et chambres, à
l'achat ou à la location, dans toute la Pologne. Chaque ligne contient le **numéro de téléphone de
l'agence ou du propriétaire** (100 % de réussite lors des tests), le prix et le prix/m², la surface,
les pièces, l'étage, l'année de construction, les équipements, la description complète, les photos et
les **coordonnées GPS**. Mode **monitor** : nouvelles annonces, baisses de prix et annonces retirées.
**1,50 $ pour 1 000 annonces.**

# Actor input Schema

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

search = extract every matching listing. monitor = compare with the previous run and return only what is new, changed (price drops included) or gone — perfect for a daily schedule.

## `operation` (type: `string`):

sale = sprzedaż, rent = wynajem.

## `estateType` (type: `string`):

mieszkanie = flat, dom = house, dzialka = plot, lokal = commercial, garaz = garage, pokoj = room.

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

City, province or Otodom path: "warszawa", "kraków", "mazowieckie/warszawa", "malopolskie/krakow/krakow" or "" for all Poland (use "" only with filters, the whole country is 150 000+ listings).

## `searchUrl` (type: `string`):

Instead of the fields above, paste any Otodom search-results URL — the Actor reuses its category and location path.

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

Optional. Example: 500000.

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

Optional. Example: 900000.

## `areaMin` (type: `integer`):

Optional. Example: 40.

## `areaMax` (type: `integer`):

Optional. Example: 120.

## `rooms` (type: `array`):

Optional. One or more room counts as text, e.g. \["2", "3"].

## `market` (type: `string`):

Primary = new builds from developers, secondary = resale.

## `ownerType` (type: `string`):

Agency/developer listings, private owners, or everything.

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

newest = Otodom's default (newest first). Other options order by price or price per m².

## `limit` (type: `string`):

72 (recommended, verified working) or 36. Bigger pages mean fewer requests.

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

Each page holds `limit` listings (72 by default). 30 pages = up to 2 160 listings per location.

## `maxResults` (type: `integer`):

Global cap on rows returned (and billed).

## `includeDetails` (type: `boolean`):

One extra request per listing: adds the agency/owner phone number, agent name, full description, building attributes (year, floors, extras), floor plans and GPS coordinates. Verified to return a phone number on 100% of listings tested.

## `onlyChanges` (type: `boolean`):

In monitor mode, return only the deltas instead of the full list (unchanged listings are not billed).

## `detectClosed` (type: `boolean`):

Flag listings that disappeared from the results as closed (sold, rented or withdrawn). Only done when the scan was complete and error-free.

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

Optional name for this monitored search. Leave empty — it is derived from the search scope.

## `delayMs` (type: `integer`):

Politeness delay. Leave 0 for the built-in value (0.6 s between search pages). Otodom does not block crawlers, but please stay reasonable.

## Actor input object example

```json
{
  "mode": "search",
  "operation": "sale",
  "estateType": "mieszkanie",
  "locations": [
    "warszawa"
  ],
  "searchUrl": "",
  "rooms": [],
  "market": "any",
  "ownerType": "any",
  "sort": "newest",
  "limit": "72",
  "maxPages": 1,
  "maxResults": 10,
  "includeDetails": true,
  "onlyChanges": true,
  "detectClosed": true,
  "stateKey": "",
  "delayMs": 0
}
```

# Actor output Schema

## `results` (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",
    "operation": "sale",
    "estateType": "mieszkanie",
    "locations": [
        "warszawa"
    ],
    "rooms": [],
    "market": "any",
    "ownerType": "any",
    "sort": "newest",
    "limit": "72",
    "maxPages": 1,
    "maxResults": 10,
    "includeDetails": true,
    "onlyChanges": true,
    "detectClosed": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("amygraphics/otodom-poland-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",
    "operation": "sale",
    "estateType": "mieszkanie",
    "locations": ["warszawa"],
    "rooms": [],
    "market": "any",
    "ownerType": "any",
    "sort": "newest",
    "limit": "72",
    "maxPages": 1,
    "maxResults": 10,
    "includeDetails": True,
    "onlyChanges": True,
    "detectClosed": True,
}

# Run the Actor and wait for it to finish
run = client.actor("amygraphics/otodom-poland-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",
  "operation": "sale",
  "estateType": "mieszkanie",
  "locations": [
    "warszawa"
  ],
  "rooms": [],
  "market": "any",
  "ownerType": "any",
  "sort": "newest",
  "limit": "72",
  "maxPages": 1,
  "maxResults": 10,
  "includeDetails": true,
  "onlyChanges": true,
  "detectClosed": true
}' |
apify call amygraphics/otodom-poland-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,amygraphics/otodom-poland-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/wazwO2tYSenrRHhbe/builds/4Od1vaJUpjgxBUPQj/openapi.json
