# Otodom Scraper: Poland Housing Prices, zł/m² & Market Report (`precious_bathmat/otodom-housing-market`) Actor

Scrape Otodom.pl flats and houses for sale or rent in any Polish city, district or region: price, price per m², m², rooms, floor, district, days listed, private or agency. Plus a market report with best-value homes. No login.

- **URL**: https://apify.com/precious_bathmat/otodom-housing-market.md
- **Developed by:** [Mariam Ahmed](https://apify.com/precious_bathmat) (community)
- **Categories:** Real estate, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 listings

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 Scraper: Poland Housing Prices, zł/m² & Market Report

Scrape **Otodom.pl**, Poland's biggest property portal, with no login and no API key, and get a **housing market report for every city or district**:

- **Every listing**: price, **price per m²**, m², rooms, floor, monthly fee (czynsz), street, district, neighbourhood, city, region, days listed, private owner / agency / developer, development name, features (balcony, parking, lift...), photos, link
- **Flats, houses, rooms, plots or commercial**, for sale or to rent
- **Any Polish place**: cities ("Warszawa", "Kraków"), districts ("Mokotów"), regions ("małopolskie"), or **paste any Otodom search link**
- **Filters**: price range, number of rooms, Otodom's order or newest first
- **Market report per search**: median price and zł/m² with the middle half, by rooms, district, neighbourhood and floor, days listed, monthly fee, private vs agency vs developer share, **best-value homes** (under 80% of their neighbourhood's zł/m²), top agencies

### What does this Otodom scraper do?

```
Otodom · Warszawa · flats for sale · 20,236 listings
 Median 1,099,000 zł (818,247 – 1,635,000 zł) · 18,980 zł/m² · 57 m²
 Mokotów 19,979 zł/m² · Wola 23,028 zł/m²
 Typical listing 54 days old · 27% from developers · 15% private owners
 28 best-value flats under 80% of their neighbourhood's zł/m²
```

1. Enter **Polish cities, districts or regions**, or paste **Otodom search links**.
2. Pick **for sale or to rent**, **property type**, **price range**, **rooms**.
3. You get **one row per listing** and **one market report per search**.

```mermaid
flowchart LR
    A[Cities, districts,<br/>regions] --> C[Otodom<br/>search]
    B[Otodom links<br/>any filters] --> C
    C --> D[Listings<br/>price, zł/m², rooms,<br/>district, days listed]
    D --> E[Market report<br/>zł/m² by district,<br/>sellers, best value]
```

### Who is it for?

- **Property investors**: zł/m² by district and neighbourhood, homes priced under their area, rental levels for yields
- **Developers and agencies**: how the primary and secondary markets compare, how long listings sit, who lists the most
- **Buyers and renters**: a fair price for each district before you view
- **Banks, valuers and proptech**: fresh asking prices and rents for any Polish city
- **Analysts and journalists**: housing prices across Polish cities, by size and district

### What data do you get?

**Listings** (dataset, one row per listing)

| Field | What it tells you |
|---|---|
| `id`, `url`, `title`, `operation`, `propertyType` | The listing |
| `price`, `currency`, `pricePerSqm`, `monthlyFee` | Price in złoty (monthly when renting), per m², and the monthly fee (czynsz) |
| `areaSqm`, `plotAreaSqm`, `rooms`, `floor` | Size and layout |
| `street`, `district`, `neighborhood`, `city`, `voivodeship` | Where |
| `sellerType`, `agency`, `development`, `isDevelopmentProject` | Private owner, agency or developer |
| `firstListed`, `daysListed`, `lastBumped` | Time on market |
| `features`, `photos`, `isPromoted`, `isExclusive` | Features |
| `vsAreaPercent`, `comparedWith`, `areaMedianPricePerSqm`, `bestValue` | zł/m² against the neighbourhood (or district) median |
| `description` | With **Descriptions** on |

**Report** (`REPORTS`, one per search)

| Field | What it tells you |
|---|---|
| `totalListings`, `listingsRead` | Otodom's count for the search, and how many were read |
| `medianPrice`, `price25`, `price75`, `medianPricePerSqm`, `pricePerSqm25`, `pricePerSqm75`, `medianAreaSqm`, `medianMonthlyFee` | Prices |
| `byRooms`, `byDistrict`, `byNeighborhood`, `byFloor`, `byPropertyType` | Median price and zł/m² by group |
| `medianDaysListed`, `newLast7Days`, `listedOver90DaysPercent` | Time on market |
| `privateOwnerPercent`, `agencyPercent`, `developerPercent`, `topAgencies` | Who sells |
| `featuresPercent` | Balcony, parking, lift... |
| `bestValue`, `bestValueCount` | Listings under 80% of their area's zł/m² |

### Example: 4 October 2026

Five cities, flats for sale, 300 listings each: **1,500 listings and 5 reports in 100 seconds**.

| City | Flats for sale | Median price (middle half) | zł/m² | Days listed | From developers |
|---|---|---|---|---|---|
| Warszawa | 20,236 | 1,099,000 zł (818,247 – 1,635,000) | 18,980 | 54 | 27% |
| Kraków | 13,412 | 799,000 zł (660,000 – 1,043,183) | 15,886 | 101 | 55% |
| Gdańsk | 8,599 | 798,500 zł (608,622 – 1,090,500) | 15,198 | 88 | 45% |
| Wrocław | 9,890 | 696,000 zł (571,996 – 849,000) | 13,652 | 55 | 39% |
| Poznań | 7,570 | 652,800 zł (534,705 – 832,843) | 12,916 | 37 | 35% |

- **Kraków to rent** (2–3 rooms): median **3,060 zł a month**, 64 zł/m²; **Mokotów to rent**: 4,097 zł, 82 zł/m².
- **Warszawa, 3,000 flats in 2 minutes 40 seconds**, led by Mokotów, Białołęka, Wola and Praga-Południe.

### Pricing

**$0.001 per listing** and **$0.02 per search report**.

- 300 listings with a report: **$0.32**
- The run above (1,500 listings, 5 reports): **$1.60**
- 3,000 listings with a report: **$3.02**

Turn off reports to pay for listings only.

### Good to know

- **Order**: by default listings come in Otodom's own order, a mix of new and older ones, so the report is a fair sample of the market. **Newest first** gives the latest listings for a daily feed.
- **The report covers the listings read**; `totalListings` is Otodom's count for the whole search. Read more listings for a fuller picture (up to 10,000 per search).
- **Best value** = zł/m² under 80% of the median of the same neighbourhood (or district, or the whole search) with at least 5 listings to compare. Development projects (several units, no single price) are left out of prices.
- **Places**: cities, districts and regions work by name, with or without Polish letters ("Krakow", "Mokotow"). Unknown names are skipped and listed in `RUN_SUMMARY`. A pasted link keeps its own filters.
- **Privacy**: agents' personal names and private owners are not collected; agencies and developers are named. Emails and phone numbers in descriptions are hidden.
- Data comes from **Otodom.pl's public search pages**. **Not affiliated with Otodom or OLX Group.**

### Input

| Field | Meaning |
|---|---|
| **Locations** | Polish cities, districts or regions |
| **For sale or to rent** | Sprzedaż or wynajem |
| **Property type** | Flats, houses, rooms, plots, commercial |
| **Price from / to** | zł (monthly when renting) |
| **Rooms** | 1 to 6+ |
| **Order** | Otodom's order (default) or newest first |
| **Otodom search links** | Any search link from otodom.pl |
| **Listings per search** | 1 to 10,000 (default 300) |
| **Descriptions** | Add the short description |
| **Reports** | On by default |

### Integrations

Export to JSON, CSV, Excel or Google Sheets, or connect through the Apify API, webhooks, Make, Zapier and n8n. **Schedule it weekly** to track prices in your cities.

# Actor input Schema

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

Polish cities, districts or regions, e.g. "Warszawa", "Kraków", "Wrocław", "Gdańsk", "Mokotów", "małopolskie". One search each.

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

Homes for sale or to rent.

## `propertyType` (type: `string`):

What to search for.

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

Only listings priced at least this (monthly rent when renting).

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

Only listings priced at most this.

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

Optional: only these room counts.

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

Otodom's own order mixes new and older listings; newest first gives the latest.

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

Optional: paste search links from otodom.pl (any place, filters, drawn area). The link's own filters win.

## `maxListingsPerSearch` (type: `integer`):

Most listings to read per location or link (36 per page).

## `includeDescriptions` (type: `boolean`):

Add each listing's short description (contact details hidden).

## `includeReports` (type: `boolean`):

One market report per search: median price and price per m², by rooms, district, neighbourhood and floor, days listed, private vs agency vs developer, features, best-value homes, top agencies.

## Actor input object example

```json
{
  "locations": [
    "Warszawa"
  ],
  "operation": "sale",
  "propertyType": "flat",
  "sort": "relevance",
  "maxListingsPerSearch": 300,
  "includeDescriptions": false,
  "includeReports": true
}
```

# Actor output Schema

## `listings` (type: `string`):

One row per listing: price, price per m², m², rooms, floor, monthly fee, district and neighbourhood, days listed, seller type, agency, features, link.

## `reports` (type: `string`):

Per search: median price and price per m², by rooms, district, neighbourhood and floor, days listed, sellers, best-value homes, agencies.

## `reportsCsv` (type: `string`):

One flat row per search.

## `summary` (type: `string`):

Requests, problems and the limits of the data.

# 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 = {
    "locations": [
        "Warszawa"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("precious_bathmat/otodom-housing-market").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 = { "locations": ["Warszawa"] }

# Run the Actor and wait for it to finish
run = client.actor("precious_bathmat/otodom-housing-market").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 '{
  "locations": [
    "Warszawa"
  ]
}' |
apify call precious_bathmat/otodom-housing-market --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,precious_bathmat/otodom-housing-market"
        }
    }
}
```

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/DVc9waC2mVFJ80sk8/builds/PWSKN1S5LVpI2HfdI/openapi.json
