# Otodom Scraper — Poland Property, Prices & Agencies (`logiover/otodom-scraper`) Actor

Scrape Otodom, Poland's largest property portal: sale price or rent, price per m², area, rooms, floor, full address with district, and the advertiser split into agency name or private owner. Flats, houses, land. Keyless. JSON, CSV or Excel.

- **URL**: https://apify.com/logiover/otodom-scraper.md
- **Developed by:** [Logiover](https://apify.com/logiover) (community)
- **Categories:** Real estate, Lead generation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 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

## 🇵🇱 Otodom Scraper — Poland Property Data, Prices & Agent Leads

> **Export every Otodom listing — price, area, rooms, district and the agency or private owner — to JSON, CSV or Excel. Keyless, no login.**

![data](https://img.shields.io/badge/data-JSON·CSV·Excel-2b8a3e?style=flat-square) ![login](https://img.shields.io/badge/login-not%20required-0b7285?style=flat-square) ![pricing](https://img.shields.io/badge/pricing-pay%20per%20result-5f3dc4?style=flat-square) ![proxy](https://img.shields.io/badge/proxy-built----in-1864ab?style=flat-square)

**Otodom Scraper** turns Poland's largest property portal into a structured dataset. Scrape thousands of apartments, houses and land plots for sale or rent across Warsaw, Kraków, Wrocław, Gdańsk and the whole country — with typed prices, price per m², and the advertiser split cleanly into **estate agency** or **private owner**. A no-code Otodom API alternative that runs on Apify.

### 📋 At a glance

| | |
|---|---|
| **Source** | otodom.pl (Poland's #1 property portal) |
| **Coverage** | All Poland — every city & voivodeship |
| **Volume** | Tens of thousands of listings per search |
| **Data** | Sale & rent · flats, houses, land, commercial |
| **Lead field** | Agency name **or** private-owner flag |
| **Output** | JSON, CSV, Excel, HTML table |
| **Login** | Not required |

### ❓ What is Otodom Scraper?

Otodom is Poland's leading real-estate marketplace. This scraper reads Otodom's own structured search data, so prices arrive as real numbers with a currency, area as a number, and the listing's advertiser is separated into agency versus private owner. The room count is translated from Otodom's internal wording to an integer, and the district is lifted out of the nested location tree into its own column — so you can filter by district without parsing an object.

### ⭐ Why use this scraper?

- ✅ **Agency vs private owner** on every row — build agency lead lists or filter to owner-direct deals
- ✅ **Typed prices** — total price and price per m² as numbers, not text
- ✅ **District, city and province** broken out for clean filtering
- ✅ **Sale and rent**, across flats, houses, land and commercial
- ✅ **Huge volume** — tens of thousands of listings per region
- ✅ **Keyless & browserless** — no login, no API key, low cost per row

### 📦 What data can you extract?

Every row is a clean, structured record you can push straight into a spreadsheet, database or AI pipeline:

| Field | Description |
|---|---|
| `listingId` | Unique Otodom listing ID |
| `title` | Listing headline |
| `listingUrl` | Direct link to the offer |
| `transactionType` | sale or rent |
| `propertyType` | FLAT / HOUSE / land / commercial |
| `priceAmount` | Price as a number |
| `currency` | PLN |
| `pricePerSqm` | Price per square metre |
| `areaSqm` | Floor area in m² |
| `rooms` | Room count (integer) |
| `floor` | Floor |
| `street / district / city / province` | Full address hierarchy |
| `isPrivateOwner` | True for owner-direct listings |
| `agencyName` | Estate agency name (lead field) |
| `shortDescription` | Advert summary |
| `imageUrl` | Cover photo |
| `dateCreated` | When it was listed |

#### Additional output fields

These fields are also present in the dataset:

| Field | Type | Description |
|---|---|---|
| `landAreaSqm` | number | Land m² |
| `roomsLabel` | string | Rooms label |
| `advertiserType` | string | Advertiser |
| `agencyId` | string | Agency ID |
| `isPromoted` | boolean | Promoted |
| `isExclusive` | boolean | Exclusive |
| `developmentTitle` | string | Development |
| `imageCount` | number | Images |
| `firstListed` | string | First listed |
| `scrapedAt` | string | Scraped at |

### 🎯 Use cases

- Build an estate-agency lead list for a Polish city, with agency names
- Compare asking price per m² across Warsaw districts
- Track owner-direct (no-agency) listings for direct outreach
- Monitor new-development supply and pricing
- Feed a property portal or valuation model with a Poland feed

### 🚀 How to use it

1. Choose **sale** (sprzedaz) or **rent** (wynajem) and a property type.
2. Paste a **region path** from an Otodom search URL — `cala-polska` for all Poland, or a city path like `mazowieckie/warszawa/warszawa/warszawa`.
3. Set **Maximum results** and click **Start**.
4. Export the dataset to JSON, CSV or Excel, or pull it via the Apify API.

#### Input example

```json
{
  "transaction": "sprzedaz",
  "propertyType": "mieszkanie",
  "regionPath": "mazowieckie/warszawa/warszawa/warszawa",
  "maxResults": 1000
}
```

### 💳 Pricing

This scraper runs on a **pay-per-result** model with **Apify Store volume discounts** — the more your plan tier, the lower the price per row. You only pay for the records actually delivered. Start on the **free tier**, no credit card needed to try it.

### ⚖️ Is it legal?

This scraper collects **publicly available** data only. You are responsible for how you use the exported data, including under GDPR and local regulations when the data concerns businesses or individuals. Nothing behind a login is accessed.

This Actor is an independent tool. It is not affiliated with, endorsed by, or sponsored by Otodom; Otodom and all other trademarks mentioned here are the property of their respective owners.

### ❔ FAQ

<details>
<summary><b>Do I need an Otodom account or API key?</b></summary>

No. Otodom listings are public and this scraper needs nothing from you — no login, no key.

</details>

<details>
<summary><b>How many listings can it return?</b></summary>

Tens of thousands per region. A single Warsaw flats search exposes ~20,000 listings; the scraper paginates until your Maximum results is met.

</details>

<details>
<summary><b>Can I get the estate agent / agency?</b></summary>

Yes. Agency listings carry the agency name; owner-direct listings are flagged with `isPrivateOwner`.

</details>

<details>
<summary><b>Does it cover rentals as well as sales?</b></summary>

Yes — set the transaction to `wynajem` for rentals, with monthly price.

</details>

<details>
<summary><b>What's the difference from the Otodom API?</b></summary>

There is no public Otodom API. This is a keyless, no-code alternative that exports ready-to-use rows.

</details>

***

<sub>Keywords: otodom scraper, otodom api, poland real estate data, polish property listings, otodom crawler, scrape otodom, warsaw property data, poland real estate api, otodom export csv, real estate lead generation poland</sub>

***

\*Maintained: last verified 2026-11-01 — checked end-to-end against live run history (reliability, output fields and types, and time/memory budgets).

***

### Related scrapers

Other Actors from the same collection:

- [Immoweb](https://apify.com/logiover/immoweb-scraper)
- [reality.idnes.cz](https://apify.com/logiover/reality-idnes-cz-scraper)
- [Lamudi](https://apify.com/logiover/lamudi-philippines-property-scraper)
- [Rightmove](https://apify.com/logiover/rightmove-scraper)

### Scheduling and integrations

Run this Actor on a schedule — hourly, daily or weekly — and have every run append to the same dataset, which is how you turn a one-off export into a time series. Results can be pushed straight into **Google Sheets**, **Slack**, **Zapier**, **Make**, **Airtable** or any **Webhook**.

### How much Otodom data can you extract in one run?

A single run returned **70 listings** in 5 seconds on the example input. That example is deliberately narrow; the result limit and the input list are both yours to widen. That is roughly **700 listings a minute**.

There is no hard ceiling built into this Actor. What bounds a run is the result limit you set, the breadth of the input you give it, and how much Otodom itself publishes. Because billing is per result, a wider run costs proportionally more and a narrow one costs almost nothing — so it is worth starting small and scaling once the output looks right.

### Otodom scraping — frequently asked questions

#### Do I need a Otodom API key, account or login?

No. This Actor reads publicly available pages and feeds, so there is nothing to register for and no credentials to manage.

#### How do I export Otodom data to CSV, Excel, JSON or Google Sheets?

Open the **Storage** tab after a run and pick the format you want — CSV, Excel, JSON, XML or an HTML table. For Google Sheets, Airtable or a database, use the dataset URL or the Apify API so the sheet refreshes itself on every run.

#### Can I schedule this Otodom scraper to run daily or hourly?

Yes. Use the **Schedules** tab and pick a cadence. Daily is enough for most tracking work; pick hourly only if the listings you care about really do change that fast.

#### How many listings can I get, and what does it cost?

See the volume section above for a measured figure. Billing is per result, so you pay for the rows you actually receive and a run that finds nothing costs nothing.

#### Is scraping Otodom legal?

This Actor collects only information that Otodom publishes publicly. It does not log in, does not defeat access controls and does not touch private data. You remain responsible for how you use the output, including under GDPR and similar rules where personal data is involved.

#### What happens if Otodom blocks the run or changes its layout?

A refused exit IP is swapped for a fresh one within a quarter of a second and retried, and rate limiting is waited out rather than fought. If nothing gets through, the run **fails with the reason** instead of reporting success with zero rows — so an empty result always means "no matches", never "the source would not answer".

#### Can I use this with an AI agent, LangChain or an MCP client?

Yes. The Actor is callable over the Apify API and through Apify's MCP server, so an agent can run it and read the dataset back as a tool call.

### 📝 Changelog

#### 2026-11-01

- Monthly health check (2026-11-01): a live run on the current build completed successfully and returned data. Input options, output fields and pricing are unchanged.
- README refreshed: independence disclaimer added; documented 10 additional output fields.

#### 2026-10-15

- Every request now has a hard time limit, so a proxy connection that stalls can no longer hold up the run or push it past its timeout.
- A run that reaches its maximum cost per run now stops cleanly with its results saved and a status message, instead of ending as aborted.
- A proxy group your account cannot use no longer stops the run at start-up; it falls back to the default Apify Proxy.
- Runs with a short timeout (API and AI-agent calls) now finish inside it with what they collected, instead of being cut off.
- Memory is managed within the run's own limit, which removes a rare out-of-memory crash on large pages.

#### 2026-09-23

- Fleet-wide quality audit. Verified end to end against live data and re-checked the input schema, the output columns and the run configuration.
- Run reliability reviewed: 100.0% of public runs succeeded in the last 30 days.
- Input schema, output schema and pricing configuration reviewed.

#### 2026-09-01

- Fleet-wide health check. Verified against this Actor's real run history: 30-day success rate, output row counts, per-field fill rates, and peak memory against the configured memory limit.
- Reviewed for the failure patterns that have cost this fleet runs — unguarded proxy setup, retry loops that can outlast the run's own time budget, and full-page HTML parsing that can exhaust a small container.
- No change to input, output fields or scraping logic.

#### 2026-08-11

- Maintenance release: refreshed the build and dependencies.
- Re-verified live execution, non-empty structured output and dataset field/type integrity.
- Reviewed reliability (retries, pagination) and output quality as part of a full-fleet QA pass.

# Changelog

This Actor's version history is a separate document: https://apify.com/logiover/otodom-scraper/changelog.md

# Actor input Schema

## `transaction` (type: `string`):

Sale (sprzedaz) or rent (wynajem).

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

Which property type to search.

## `regionPath` (type: `string`):

Otodom region slug from the search URL, e.g. cala-polska (all Poland), mazowieckie/warszawa/warszawa/warszawa (Warsaw), malopolskie/krakow/krakow/krakow (Krakow).

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

Stop after this many listings. Otodom renders 36 per page and the Actor walks pages until the target is met.

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

Otodom blocks datacenter traffic, so residential Poland proxy is required and preselected. Changing it away from residential will return no results.

## Actor input object example

```json
{
  "transaction": "sprzedaz",
  "propertyType": "mieszkanie",
  "regionPath": "cala-polska",
  "maxResults": 200,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "PL"
  }
}
```

# Actor output Schema

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

All records from this run.

# 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 = {
    "transaction": "sprzedaz",
    "propertyType": "mieszkanie",
    "regionPath": "cala-polska",
    "maxResults": 200,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "PL"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("logiover/otodom-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 = {
    "transaction": "sprzedaz",
    "propertyType": "mieszkanie",
    "regionPath": "cala-polska",
    "maxResults": 200,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "PL",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("logiover/otodom-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 '{
  "transaction": "sprzedaz",
  "propertyType": "mieszkanie",
  "regionPath": "cala-polska",
  "maxResults": 200,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "PL"
  }
}' |
apify call logiover/otodom-scraper --silent --output-dataset

```

## MCP server setup

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