# Otodom.PL Scraper | Enterprise Grade (`fatihtahta/otodom-pl-scraper`) Actor

Extract structured Otodom.pl property listings from 200k+ listings across Poland with prices, locations, seller details, images, and property metadata. Built for enterprise-grade real estate intelligence, lead enrichment, market analysis and automated data pipelines.

- **URL**: https://apify.com/fatihtahta/otodom-pl-scraper.md
- **Developed by:** [Fatih Tahta](https://apify.com/fatihtahta) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 145 total users, 8 monthly users, 72.8% runs succeeded, 4 bookmarks
- **User rating**: 5.00 out of 5 stars

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

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Otodom.pl Scraper

**Slug:** `fatihtahta/otodom-scraper`

### Overview

Otodom.pl Scraper collects structured real-estate listing data from [Otodom.pl](https://www.otodom.pl), including listing identity, title, URL, pricing, location, property attributes, images, seller information, publication timestamps, and listing metadata. Otodom.pl is one of Poland's major real-estate marketplaces, making its public listing data useful for market analysis, monitoring, enrichment, and operational reporting. The actor turns repeatable search criteria such as location, deal type, property type, price range, area, room count, construction year, radius, and sorting preference into consistent JSON records. It is designed for automated, recurring data acquisition where teams need structured output that can be reviewed, exported, or synced into downstream systems. Results reflect the public data available at run time, with predictable record structure and transparent handling of optional fields.

### Why Use This Actor

- **Market research and analytics teams:** build repeatable collection workflows for pricing, availability, geography, room count, property type, and market-segment analysis.
- **Product and content teams:** monitor public real-estate inventory to support category planning, localized content, benchmarking, and operational reporting.
- **Developers and data engineers:** feed normalized listing records into downstream systems, warehouses, enrichment pipelines, and automated quality checks.
- **Lead generation and enrichment teams:** collect public listing and owner attributes for targeted prospecting, CRM enrichment, and location-aware segmentation.
- **Monitoring and competitive tracking teams:** schedule recurring runs to observe listing changes, new supply, price movement, and geographic coverage over time.

### Common Use Cases

- **Market intelligence:** monitor supply, pricing, availability, locations, property types, room counts, and market movement across Polish real-estate segments.
- **Lead generation:** build targeted prospect lists from public listings using location, deal type, property type, price, size, and ownership signals.
- **Competitive monitoring:** track changes in listing availability, promotions, owner types, prices, and listing freshness across selected markets.
- **Catalog and directory building:** populate internal property databases with structured public listing records and stable identifiers.
- **Data enrichment:** add current public listing attributes to existing CRM, BI, analytics, or investment research datasets.
- **Recurring reporting:** schedule periodic runs for dashboards, alerts, market snapshots, and historical trend analysis.

### Quick Start

1. Choose one or more `location` values such as `Warszawa`, `Kraków`, `Gdańsk`, or a district name.
2. Select the `deal_type` and `property_type` that match the market segment you want to collect.
3. Set a small `limit`, such as `25` or `50`, for the first validation run.
4. Run the actor in Apify Console.
5. Inspect the first dataset records to confirm the fields and values match your use case.
6. Increase coverage, adjust filters, or schedule the actor once the output is verified.

### Input Parameters

Configure the available filters below to define the collection scope.

| Parameter | Type | Description | Default |
|---|---:|---|---|
| `location` | array of strings | Search locations. Use one or more city, district, region, or locality names such as `Warszawa`, `Kraków`, `Gdańsk`, or `Ursynów`. Each location is treated as a separate collection scope. | – |
| `deal_type` | string | Transaction type. Allowed values: `sale`, `rent`. | – |
| `property_type` | string | Property category. Allowed values: `apartments`, `studio_apartments`, `houses`, `investments`, `rooms`, `plots`, `commercial_premises`, `halls_and_warehouses`, `garages`. | `apartments` |
| `sort_by` | string | Result ordering before collection. Allowed values: `default`, `date_latest`, `date_oldest`, `price_lowest`, `price_highest`, `area_smallest`, `area_largest`. | `default` |
| `min_price` | integer | Minimum listing price to include, in PLN. Use `0` or leave empty when no lower price boundary is needed. | – |
| `max_price` | integer | Maximum listing price to include, in PLN. Use this to focus on a specific budget or price band. | – |
| `min_area` | integer | Minimum property area to include, in square meters. | – |
| `max_area` | integer | Maximum property area to include, in square meters. | – |
| `min_construction_year` | integer | Earliest construction year to include, such as `2010` for properties built in 2010 or later. | – |
| `max_construction_year` | integer | Latest construction year to include. Useful for focusing on older stock or excluding newer properties. | – |
| `radius` | string | Search radius around each selected location, in kilometers. Allowed values: `0`, `5`, `10`, `15`, `25`, `50`, `75`. | `0` |
| `rooms_number` | array of strings | Room counts to include. Allowed values: `1`, `2`, `3`, `4`, `5`, `6+`. Leave empty to include any room count. | – |
| `limit` | integer | Maximum number of listings to save for each location or input search. Minimum value: `1`. Leave empty to collect as many matching listings as available within the run scope. | – |
| `proxyConfiguration` | object | Optional request routing configuration available in Apify Console. | `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}` |

### Choosing Inputs

Use `location` as the main discovery input. Broader location values can improve discovery, while district-level or neighborhood-level values produce more targeted datasets.

Use `deal_type` and `property_type` to keep each run aligned with a single market segment, such as apartments for rent or houses for sale. Price, area, construction year, room count, and radius filters narrow the result set; leaving optional filters empty creates broader coverage for exploratory analysis.

Use `sort_by` when the order of collection matters, such as prioritizing newest listings, lowest prices, highest prices, smallest areas, or largest areas. Start with a small `limit` for validation, then increase it after confirming that the records match your expected shape and coverage.

### Example Inputs

#### Example: Apartment Rental Monitoring

```json
{
  "location": ["Warszawa"],
  "deal_type": "rent",
  "property_type": "apartments",
  "sort_by": "date_latest",
  "rooms_number": ["2", "3"],
  "limit": 50
}
```

#### Example: Targeted Sale Search

```json
{
  "location": ["Kraków"],
  "deal_type": "sale",
  "property_type": "apartments",
  "min_price": 500000,
  "max_price": 900000,
  "min_area": 45,
  "max_area": 80,
  "limit": 100
}
```

#### Example: Broad Discovery With Conservative Limit

```json
{
  "location": ["Gdańsk", "Gdynia", "Sopot"],
  "deal_type": "sale",
  "property_type": "houses",
  "radius": "25",
  "sort_by": "default",
  "limit": 75
}
```

### Output

#### Output Destination

The actor writes results to an Apify dataset as JSON records. The dataset is designed for direct consumption by analytics tools, ETL pipelines, and downstream APIs without post-processing.

Each item contains a stable record envelope plus a type-specific payload. For this actor, the primary record type is `listing`.

#### Record Envelope

All records include these stable identifiers:

- **type** *(string, required)*: record type, such as `listing`.
- **id** *(number, required)*: stable listing identifier.
- **url** *(string, required)*: public listing URL.

Recommended idempotency key: `type + ":" + id`.

Use this key to deduplicate records and perform upserts when syncing repeated runs into warehouses, CRMs, search indexes, or operational databases. The envelope makes records easier to merge, deduplicate, and synchronize across recurring collection workflows.

#### Examples

##### Example: listing (`type = "listing"`)

```json
{
  "type": "listing",
  "id": 60168503,
  "url": "https://www.otodom.pl/pl/ad/kawalerka-ursynow-sluzew-klobucka-pieskowa-skala-mordor-jeziorko-ID44szJ",
  "canonicalUrl": "https://www.otodom.pl/pl/ad/kawalerka-ursynow-sluzew-klobucka-pieskowa-skala-mordor-jeziorko-ID44szJ",
  "sourceUrl": "https://www.otodom.pl/pl/wyniki/wynajem/kawalerka/mazowieckie/warszawa/warszawa/warszawa?buildYearMax=2023&buildYearMin=2012&by=DEFAULT&direction=DESC&limit=36&ownerTypeSingleSelect=ALL&searchingCriteria=kawalerka&searchingCriteria=mazowieckie&searchingCriteria=warszawa&searchingCriteria=warszawa&searchingCriteria=warszawa&searchingCriteria=wynajem",
  "seedId": "c606ca6a7a94",
  "seedType": "query",
  "seedValue": "Warszawa",
  "pageIndex": 1,
  "extractionStrategy": "embedded_json",
  "title": "Kawalerka Ursynów Służew Kłobucka Pieskowa Skała Mordor Jeziorko",
  "price": "2800",
  "priceNumeric": 2800.0,
  "currency": "PLN",
  "description": "Wynajmę bezpośrednio kawalerkę zlokalizowaną na osiedlu Harmony Park, będącym częścią kultowego osiedla Central Park Ursynów, znajdującym się na pograniczu Ursynowa i Mokotowa, okolice ulicy Kłobuckie...",
  "location": "Warszawa, mazowieckie",
  "publishedAt": "2020-01-30T19:24:00+00:00",
  "areaSqm": 31.0,
  "rooms": "ONE",
  "floor": "SIXTH",
  "slug": "kawalerka-ursynow-sluzew-klobucka-pieskowa-skala-mordor-jeziorko-ID44szJ",
  "estate": "FLAT",
  "transaction": "RENT",
  "developmentId": 0,
  "street": "ul. Kłobucka",
  "city": "Warszawa",
  "province": "mazowieckie",
  "locationDetails": {
    "mapDetails": {
      "radius": 500
    },
    "address": {
      "street": {
        "name": "ul. Kłobucka",
        "number": ""
      },
      "city": {
        "name": "Warszawa"
      },
      "province": {
        "name": "mazowieckie"
      }
    },
    "reverseGeocoding": {
      "locations": [
        {
          "id": "mazowieckie",
          "fullName": "mazowieckie",
          "name": "mazowieckie",
          "locationLevel": "voivodeship"
        },
        {
          "id": "mazowieckie/warszawa/warszawa/warszawa",
          "fullName": "Warszawa, mazowieckie",
          "name": "Warszawa",
          "locationLevel": "city_or_village"
        },
        {
          "id": "mazowieckie/warszawa/warszawa/warszawa/ursynow",
          "fullName": "Ursynów, Warszawa, mazowieckie",
          "name": "Ursynów",
          "locationLevel": "district"
        },
        {
          "id": "mazowieckie/warszawa/warszawa/warszawa/ursynow/wyczolki",
          "fullName": "Wyczółki, Ursynów, Warszawa, mazowieckie",
          "name": "Wyczółki",
          "locationLevel": "residential"
        }
      ]
    }
  },
  "reverseGeocodingLocations": [
    {
      "id": "mazowieckie",
      "fullName": "mazowieckie",
      "name": "mazowieckie",
      "locationLevel": "voivodeship"
    },
    {
      "id": "mazowieckie/warszawa/warszawa/warszawa",
      "fullName": "Warszawa, mazowieckie",
      "name": "Warszawa",
      "locationLevel": "city_or_village"
    },
    {
      "id": "mazowieckie/warszawa/warszawa/warszawa/ursynow",
      "fullName": "Ursynów, Warszawa, mazowieckie",
      "name": "Ursynów",
      "locationLevel": "district"
    },
    {
      "id": "mazowieckie/warszawa/warszawa/warszawa/ursynow/wyczolki",
      "fullName": "Wyczółki, Ursynów, Warszawa, mazowieckie",
      "name": "Wyczółki",
      "locationLevel": "residential"
    }
  ],
  "images": [
    {
      "medium": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InZvbzVvbXNrOWI4MjItQVBMIiwidyI6W3siZm4iOiJlbnZmcXFlMWF5NGsxLUFQTCIsInMiOiIxNCIsInAiOiIxMCwtMTAiLCJhIjoiMCJ9XX0.bwShQzg_ao7bH4O-E_Ou5e1Mse2RZVWZIeSTfTS2Dmg/image;s=655x491;q=80",
      "large": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InZvbzVvbXNrOWI4MjItQVBMIiwidyI6W3siZm4iOiJlbnZmcXFlMWF5NGsxLUFQTCIsInMiOiIxNCIsInAiOiIxMCwtMTAiLCJhIjoiMCJ9XX0.bwShQzg_ao7bH4O-E_Ou5e1Mse2RZVWZIeSTfTS2Dmg/image;s=1280x1024;q=80"
    },
    {
      "medium": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InB5Y2FlYTVwMDBsay1BUEwiLCJ3IjpbeyJmbiI6ImVudmZxcWUxYXk0azEtQVBMIiwicyI6IjE0IiwicCI6IjEwLC0xMCIsImEiOiIwIn1dfQ.2Y7zvtJuwenm8NSP0ECIJO_LctSCYwlFqo1h6plj35Y/image;s=655x491;q=80",
      "large": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InB5Y2FlYTVwMDBsay1BUEwiLCJ3IjpbeyJmbiI6ImVudmZxcWUxYXk0azEtQVBMIiwicyI6IjE0IiwicCI6IjEwLC0xMCIsImEiOiIwIn1dfQ.2Y7zvtJuwenm8NSP0ECIJO_LctSCYwlFqo1h6plj35Y/image;s=1280x1024;q=80"
    },
    {
      "medium": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6ImEwNGg3Y2Uwb2xsLUFQTCIsInciOlt7ImZuIjoiZW52ZnFxZTFheTRrMS1BUEwiLCJzIjoiMTQiLCJwIjoiMTAsLTEwIiwiYSI6IjAifV19.cGPrRIBmi8P3gawcRSCxvQntVMT_q006M9S4DhwBekk/image;s=655x491;q=80",
      "large": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6ImEwNGg3Y2Uwb2xsLUFQTCIsInciOlt7ImZuIjoiZW52ZnFxZTFheTRrMS1BUEwiLCJzIjoiMTQiLCJwIjoiMTAsLTEwIiwiYSI6IjAifV19.cGPrRIBmi8P3gawcRSCxvQntVMT_q006M9S4DhwBekk/image;s=1280x1024;q=80"
    }
  ],
  "imageUrls": [
    "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InZvbzVvbXNrOWI4MjItQVBMIiwidyI6W3siZm4iOiJlbnZmcXFlMWF5NGsxLUFQTCIsInMiOiIxNCIsInAiOiIxMCwtMTAiLCJhIjoiMCJ9XX0.bwShQzg_ao7bH4O-E_Ou5e1Mse2RZVWZIeSTfTS2Dmg/image;s=1280x1024;q=80",
    "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InB5Y2FlYTVwMDBsay1BUEwiLCJ3IjpbeyJmbiI6ImVudmZxcWUxYXk0azEtQVBMIiwicyI6IjE0IiwicCI6IjEwLC0xMCIsImEiOiIwIn1dfQ.2Y7zvtJuwenm8NSP0ECIJO_LctSCYwlFqo1h6plj35Y/image;s=1280x1024;q=80",
    "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6ImEwNGg3Y2Uwb2xsLUFQTCIsInciOlt7ImZuIjoiZW52ZnFxZTFheTRrMS1BUEwiLCJzIjoiMTQiLCJwIjoiMTAsLTEwIiwiYSI6IjAifV19.cGPrRIBmi8P3gawcRSCxvQntVMT_q006M9S4DhwBekk/image;s=1280x1024;q=80"
  ],
  "imagesHashes": [
    "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InZvbzVvbXNrOWI4MjItQVBMIiwidyI6W3siZm4iOiJlbnZmcXFlMWF5NGsxLUFQTCIsInMiOiIxNCIsInAiOiIxMCwtMTAiLCJhIjoiMCJ9XX0.bwShQzg_ao7bH4O-E_Ou5e1Mse2RZVWZIeSTfTS2Dmg/image",
    "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InB5Y2FlYTVwMDBsay1BUEwiLCJ3IjpbeyJmbiI6ImVudmZxcWUxYXk0azEtQVBMIiwicyI6IjE0IiwicCI6IjEwLC0xMCIsImEiOiIwIn1dfQ.2Y7zvtJuwenm8NSP0ECIJO_LctSCYwlFqo1h6plj35Y/image",
    "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6ImEwNGg3Y2Uwb2xsLUFQTCIsInciOlt7ImZuIjoiZW52ZnFxZTFheTRrMS1BUEwiLCJzIjoiMTQiLCJwIjoiMTAsLTEwIiwiYSI6IjAifV19.cGPrRIBmi8P3gawcRSCxvQntVMT_q006M9S4DhwBekk/image"
  ],
  "totalPossibleImages": 17,
  "isExclusiveOffer": false,
  "isPrivateOwner": true,
  "isPromoted": false,
  "listingSource": "urn:site:local",
  "totalPrice": {
    "value": 2800,
    "currency": "PLN"
  },
  "rentPrice": {
    "value": 600,
    "currency": "PLN"
  },
  "rentPriceNumeric": 600.0,
  "pricePerSquareMeter": {
    "value": 90,
    "currency": "PLN"
  },
  "pricePerSqmNumeric": 90.0,
  "hidePrice": false,
  "dateCreated": "2026-04-24T17:51:19",
  "createdAtFirst": "2020-01-30T19:24:00+00:00",
  "pushedUpAt": "2026-04-24T17:51:19+02:00",
  "showPremiumTile": false,
  "advertOwner": {
    "name": "PAULA",
    "imageUrl": "",
    "contacts": []
  },
  "ownerName": "PAULA",
  "tags": [
    {
      "value": "BALCONY",
      "weight": 35
    },
    {
      "value": "SECURE_BUILDING",
      "weight": 10
    }
  ],
  "tagValues": [
    "BALCONY",
    "SECURE_BUILDING"
  ],
  "rowIndex": 2,
  "href": "[lang]/ad/kawalerka-ursynow-sluzew-klobucka-pieskowa-skala-mordor-jeziorko-ID44szJ",
  "listingDetails": {
    "id": 60168503,
    "title": "Kawalerka Ursynów Służew Kłobucka Pieskowa Skała Mordor Jeziorko",
    "slug": "kawalerka-ursynow-sluzew-klobucka-pieskowa-skala-mordor-jeziorko-ID44szJ",
    "estate": "FLAT",
    "development": null,
    "developmentId": 0,
    "developmentTitle": "",
    "developmentUrl": "",
    "transaction": "RENT",
    "location": {
      "mapDetails": {
        "radius": 500
      },
      "address": {
        "street": {
          "name": "ul. Kłobucka",
          "number": ""
        },
        "city": {
          "name": "Warszawa"
        },
        "province": {
          "name": "mazowieckie"
        }
      },
      "reverseGeocoding": {
        "locations": [
          {
            "id": "mazowieckie",
            "fullName": "mazowieckie",
            "name": "mazowieckie",
            "locationLevel": "voivodeship"
          },
          {
            "id": "mazowieckie/warszawa/warszawa/warszawa",
            "fullName": "Warszawa, mazowieckie",
            "name": "Warszawa",
            "locationLevel": "city_or_village"
          },
          {
            "id": "mazowieckie/warszawa/warszawa/warszawa/ursynow",
            "fullName": "Ursynów, Warszawa, mazowieckie",
            "name": "Ursynów",
            "locationLevel": "district"
          },
          {
            "id": "mazowieckie/warszawa/warszawa/warszawa/ursynow/wyczolki",
            "fullName": "Wyczółki, Ursynów, Warszawa, mazowieckie",
            "name": "Wyczółki",
            "locationLevel": "residential"
          }
        ]
      }
    },
    "images": [
      {
        "medium": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InZvbzVvbXNrOWI4MjItQVBMIiwidyI6W3siZm4iOiJlbnZmcXFlMWF5NGsxLUFQTCIsInMiOiIxNCIsInAiOiIxMCwtMTAiLCJhIjoiMCJ9XX0.bwShQzg_ao7bH4O-E_Ou5e1Mse2RZVWZIeSTfTS2Dmg/image;s=655x491;q=80",
        "large": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InZvbzVvbXNrOWI4MjItQVBMIiwidyI6W3siZm4iOiJlbnZmcXFlMWF5NGsxLUFQTCIsInMiOiIxNCIsInAiOiIxMCwtMTAiLCJhIjoiMCJ9XX0.bwShQzg_ao7bH4O-E_Ou5e1Mse2RZVWZIeSTfTS2Dmg/image;s=1280x1024;q=80"
      },
      {
        "medium": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InB5Y2FlYTVwMDBsay1BUEwiLCJ3IjpbeyJmbiI6ImVudmZxcWUxYXk0azEtQVBMIiwicyI6IjE0IiwicCI6IjEwLC0xMCIsImEiOiIwIn1dfQ.2Y7zvtJuwenm8NSP0ECIJO_LctSCYwlFqo1h6plj35Y/image;s=655x491;q=80",
        "large": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InB5Y2FlYTVwMDBsay1BUEwiLCJ3IjpbeyJmbiI6ImVudmZxcWUxYXk0azEtQVBMIiwicyI6IjE0IiwicCI6IjEwLC0xMCIsImEiOiIwIn1dfQ.2Y7zvtJuwenm8NSP0ECIJO_LctSCYwlFqo1h6plj35Y/image;s=1280x1024;q=80"
      },
      {
        "medium": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6ImEwNGg3Y2Uwb2xsLUFQTCIsInciOlt7ImZuIjoiZW52ZnFxZTFheTRrMS1BUEwiLCJzIjoiMTQiLCJwIjoiMTAsLTEwIiwiYSI6IjAifV19.cGPrRIBmi8P3gawcRSCxvQntVMT_q006M9S4DhwBekk/image;s=655x491;q=80",
        "large": "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6ImEwNGg3Y2Uwb2xsLUFQTCIsInciOlt7ImZuIjoiZW52ZnFxZTFheTRrMS1BUEwiLCJzIjoiMTQiLCJwIjoiMTAsLTEwIiwiYSI6IjAifV19.cGPrRIBmi8P3gawcRSCxvQntVMT_q006M9S4DhwBekk/image;s=1280x1024;q=80"
      }
    ],
    "totalPossibleImages": 17,
    "isExclusiveOffer": false,
    "isPrivateOwner": true,
    "isPromoted": false,
    "source": "urn:site:local",
    "agency": null,
    "openDays": "",
    "totalPrice": {
      "value": 2800,
      "currency": "PLN"
    },
    "rentPrice": {
      "value": 600,
      "currency": "PLN"
    },
    "priceFromPerSquareMeter": null,
    "pricePerSquareMeter": {
      "value": 90,
      "currency": "PLN"
    },
    "areaInSquareMeters": 31,
    "terrainAreaInSquareMeters": null,
    "roomsNumber": "ONE",
    "hidePrice": false,
    "floorNumber": "SIXTH",
    "investmentState": null,
    "investmentUnitsAreaInSquareMeters": null,
    "peoplePerRoom": null,
    "dateCreated": "2026-04-24 17:51:19",
    "createdAtFirst": "2020-01-30T19:24:00Z",
    "investmentUnitsNumber": null,
    "investmentUnitsRoomsNumber": null,
    "investmentEstimatedDelivery": null,
    "pushedUpAt": "2026-04-24T17:51:19+02:00",
    "specialOffer": null,
    "shortDescription": "Wynajmę bezpośrednio kawalerkę zlokalizowaną na osiedlu Harmony Park, będącym częścią kultowego osiedla Central Park Ursynów, znajdującym się na pograniczu Ursynowa i Mokotowa, okolice ulicy Kłobuckie...",
    "showPremiumTile": false,
    "organisationAssignedMember": null,
    "advertOwner": {
      "name": "PAULA",
      "imageUrl": "",
      "contacts": []
    },
    "tags": [
      {
        "value": "BALCONY",
        "weight": 35
      },
      {
        "value": "SECURE_BUILDING",
        "weight": 10
      }
    ],
    "imagesHashes": [
      "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InZvbzVvbXNrOWI4MjItQVBMIiwidyI6W3siZm4iOiJlbnZmcXFlMWF5NGsxLUFQTCIsInMiOiIxNCIsInAiOiIxMCwtMTAiLCJhIjoiMCJ9XX0.bwShQzg_ao7bH4O-E_Ou5e1Mse2RZVWZIeSTfTS2Dmg/image",
      "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6InB5Y2FlYTVwMDBsay1BUEwiLCJ3IjpbeyJmbiI6ImVudmZxcWUxYXk0azEtQVBMIiwicyI6IjE0IiwicCI6IjEwLC0xMCIsImEiOiIwIn1dfQ.2Y7zvtJuwenm8NSP0ECIJO_LctSCYwlFqo1h6plj35Y/image",
      "https://ireland.apollo.olxcdn.com/v1/files/eyJmbiI6ImEwNGg3Y2Uwb2xsLUFQTCIsInciOlt7ImZuIjoiZW52ZnFxZTFheTRrMS1BUEwiLCJzIjoiMTQiLCJwIjoiMTAsLTEwIiwiYSI6IjAifV19.cGPrRIBmi8P3gawcRSCxvQntVMT_q006M9S4DhwBekk/image"
    ],
    "rowIndex": 2,
    "href": "[lang]/ad/kawalerka-ursynow-sluzew-klobucka-pieskowa-skala-mordor-jeziorko-ID44szJ"
  },
  "domain": "otodom.pl",
  "fingerprint": "e960339e40131cf71738"
}
```

### Field Reference

#### Record Type: `listing`

- **type** *(string, required)*: record type; currently `listing`.
- **id** *(number, required)*: stable listing identifier.
- **url** *(string, required)*: public listing URL.
- **canonicalUrl** *(string, optional)*: canonical public URL for the listing.
- **sourceUrl** *(string, optional)*: public search or listing source URL associated with the record.
- **seedId** *(string, optional)*: identifier for the input scope that produced the record.
- **seedType** *(string, optional)*: input scope type, such as a location-based query.
- **seedValue** *(string, optional)*: original input value associated with the record.
- **pageIndex** *(number, optional)*: result page position associated with collection.
- **extractionStrategy** *(string, optional)*: record extraction label.
- **title** *(string, optional)*: listing title.
- **price / priceNumeric / currency** *(string, number, string; optional)*: display price, parsed numeric price, and currency.
- **description** *(string, optional)*: listing description or summary text.
- **location** *(string, optional)*: human-readable listing location.
- **publishedAt** *(string, optional)*: publication timestamp when available.
- **areaSqm** *(number, optional)*: property area in square meters.
- **rooms** *(string, optional)*: normalized room count label.
- **floor** *(string, optional)*: floor label when available.
- **slug** *(string, optional)*: listing slug.
- **estate** *(string, optional)*: property estate category.
- **transaction** *(string, optional)*: transaction category, such as `RENT` or `SALE`.
- **developmentId** *(number, optional)*: development identifier when the listing is associated with a development.
- **street / city / province** *(string, optional)*: address convenience fields.
- **locationDetails.mapDetails.radius** *(number, optional)*: map radius in meters when provided.
- **locationDetails.address.street.name** *(string, optional)*: street name.
- **locationDetails.address.street.number** *(string, optional)*: street number when available.
- **locationDetails.address.city.name** *(string, optional)*: city name.
- **locationDetails.address.province.name** *(string, optional)*: province name.
- **locationDetails.reverseGeocoding.locations\[].id** *(string, optional)*: location hierarchy identifier.
- **locationDetails.reverseGeocoding.locations\[].fullName** *(string, optional)*: full location hierarchy name.
- **locationDetails.reverseGeocoding.locations\[].name** *(string, optional)*: location name.
- **locationDetails.reverseGeocoding.locations\[].locationLevel** *(string, optional)*: location hierarchy level.
- **reverseGeocodingLocations\[]** *(array, optional)*: flattened location hierarchy with `id`, `fullName`, `name`, and `locationLevel`.
- **images\[].medium / images\[].large** *(string, optional)*: listing image URLs by size.
- **imageUrls\[]** *(array of strings, optional)*: convenience list of large image URLs.
- **imagesHashes\[]** *(array of strings, optional)*: stable image references when available.
- **totalPossibleImages** *(number, optional)*: number of images indicated for the listing.
- **isExclusiveOffer / isPrivateOwner / isPromoted** *(boolean, optional)*: listing flags.
- **listingSource** *(string, optional)*: public listing source label.
- **totalPrice.value / totalPrice.currency** *(number, string; optional)*: total price and currency.
- **rentPrice.value / rentPrice.currency / rentPriceNumeric** *(number, string, number; optional)*: rent price details when available.
- **pricePerSquareMeter.value / pricePerSquareMeter.currency / pricePerSqmNumeric** *(number, string, number; optional)*: price per square meter details.
- **hidePrice** *(boolean, optional)*: indicates whether the listing hides price information.
- **dateCreated / createdAtFirst / pushedUpAt** *(string, optional)*: listing creation and refresh timestamps when available.
- **showPremiumTile** *(boolean, optional)*: listing display flag.
- **advertOwner.name / advertOwner.imageUrl / advertOwner.contacts** *(string, string, array; optional)*: public owner information.
- **ownerName** *(string, optional)*: owner name convenience field.
- **tags\[].value / tags\[].weight** *(string, number; optional)*: listing feature tags and weights.
- **tagValues\[]** *(array of strings, optional)*: convenience list of tag values.
- **rowIndex** *(number, optional)*: listing position within the source result set.
- **href** *(string, optional)*: listing path.
- **domain** *(string, optional)*: source domain.
- **fingerprint** *(string, optional)*: stable record fingerprint for comparison workflows.
- **listingDetails.id** *(number, optional)*: nested listing identifier.
- **listingDetails.title** *(string, optional)*: nested listing title.
- **listingDetails.slug** *(string, optional)*: nested listing slug.
- **listingDetails.estate** *(string, optional)*: nested estate category.
- **listingDetails.development** *(object or null, optional)*: development object when available.
- **listingDetails.developmentId** *(number, optional)*: nested development identifier.
- **listingDetails.developmentTitle** *(string, optional)*: development title when available.
- **listingDetails.developmentUrl** *(string, optional)*: development URL when available.
- **listingDetails.transaction** *(string, optional)*: nested transaction category.
- **listingDetails.location.mapDetails.radius** *(number, optional)*: nested map radius in meters.
- **listingDetails.location.address.street.name** *(string, optional)*: nested street name.
- **listingDetails.location.address.street.number** *(string, optional)*: nested street number.
- **listingDetails.location.address.city.name** *(string, optional)*: nested city name.
- **listingDetails.location.address.province.name** *(string, optional)*: nested province name.
- **listingDetails.location.reverseGeocoding.locations\[]** *(array, optional)*: nested location hierarchy.
- **listingDetails.images\[].medium / listingDetails.images\[].large** *(string, optional)*: nested image URLs.
- **listingDetails.totalPossibleImages** *(number, optional)*: nested image count.
- **listingDetails.isExclusiveOffer / listingDetails.isPrivateOwner / listingDetails.isPromoted** *(boolean, optional)*: nested listing flags.
- **listingDetails.source** *(string, optional)*: nested source label.
- **listingDetails.agency** *(object or null, optional)*: agency information when publicly available.
- **listingDetails.openDays** *(string, optional)*: open day information when available.
- **listingDetails.totalPrice.value / listingDetails.totalPrice.currency** *(number, string; optional)*: nested total price.
- **listingDetails.rentPrice.value / listingDetails.rentPrice.currency** *(number, string; optional)*: nested rent price.
- **listingDetails.priceFromPerSquareMeter** *(object or null, optional)*: starting price per square meter when available.
- **listingDetails.pricePerSquareMeter.value / listingDetails.pricePerSquareMeter.currency** *(number, string; optional)*: nested price per square meter.
- **listingDetails.areaInSquareMeters** *(number, optional)*: nested area in square meters.
- **listingDetails.terrainAreaInSquareMeters** *(number or null, optional)*: land area in square meters when available.
- **listingDetails.roomsNumber** *(string, optional)*: nested room count label.
- **listingDetails.hidePrice** *(boolean, optional)*: nested price visibility flag.
- **listingDetails.floorNumber** *(string, optional)*: nested floor label.
- **listingDetails.investmentState** *(string or null, optional)*: investment status when available.
- **listingDetails.investmentUnitsAreaInSquareMeters** *(number or null, optional)*: investment unit area when available.
- **listingDetails.peoplePerRoom** *(number or null, optional)*: people-per-room value when available.
- **listingDetails.dateCreated / listingDetails.createdAtFirst / listingDetails.pushedUpAt** *(string, optional)*: nested listing timestamps.
- **listingDetails.investmentUnitsNumber** *(number or null, optional)*: investment units count when available.
- **listingDetails.investmentUnitsRoomsNumber** *(string or null, optional)*: investment unit room counts when available.
- **listingDetails.investmentEstimatedDelivery** *(string or null, optional)*: estimated delivery when available.
- **listingDetails.specialOffer** *(object or null, optional)*: special offer information when available.
- **listingDetails.shortDescription** *(string, optional)*: nested short description.
- **listingDetails.showPremiumTile** *(boolean, optional)*: nested display flag.
- **listingDetails.organisationAssignedMember** *(object or null, optional)*: assigned organization member when available.
- **listingDetails.advertOwner.name / listingDetails.advertOwner.imageUrl / listingDetails.advertOwner.contacts** *(string, string, array; optional)*: nested owner information.
- **listingDetails.tags\[].value / listingDetails.tags\[].weight** *(string, number; optional)*: nested feature tags.
- **listingDetails.imagesHashes\[]** *(array of strings, optional)*: nested image references.
- **listingDetails.rowIndex** *(number, optional)*: nested result position.
- **listingDetails.href** *(string, optional)*: nested listing path.

### Data Quality, Guarantees, And Handling

- **Structured records:** results are normalized into predictable JSON objects for downstream use.
- **Best-effort extraction:** fields may vary by region, session, availability, listing type, or target-side presentation changes.
- **Optional fields:** null-check optional fields in downstream code, especially nested price, owner, location, image, and investment fields.
- **Deduplication:** use `type + ":" + id` as the recommended key for idempotent storage.
- **Freshness:** results reflect the publicly available data at run time.
- **Repeated runs:** use the recommended idempotency key when syncing data into warehouses, CRMs, or search indexes.

### Tips For Best Results

- Start with a small `limit` to validate the output shape before scaling up.
- Use one geography, deal type, and property type per run when you need clean segmentation.
- Leave optional filters empty when the goal is broad discovery.
- Add price, area, room count, construction year, and radius filters gradually to understand how each field changes coverage.
- Use `sort_by: "date_latest"` for monitoring workflows where newly visible listings matter most.
- Schedule recurring runs instead of relying on manual one-off collection for reporting workflows.
- Store records with `type + ":" + id` to deduplicate listings across repeated runs.

### How to Run on Apify

1. Open the Actor in Apify Console.
2. Configure the available input fields for the target location, deal type, property type, and filters.
3. Set the maximum number of outputs to collect with `limit`.
4. Click **Start** and wait for the run to finish.
5. Download results in JSON, CSV, Excel, or other supported formats.

### Scheduling & Automation

#### Scheduling

**Automated Data Collection**

Schedule recurring runs to keep listing datasets fresh for monitoring, reporting, enrichment, and historical analysis. Use a consistent input configuration when comparing results over time.

- Navigate to **Schedules** in Apify Console
- Create a new schedule, such as daily, weekly, or custom cron
- Configure input parameters
- Enable notifications for run completion
- Add webhooks for automated processing

#### Integration Options

- **BI dashboards:** monitor pricing, availability, property mix, geographic coverage, and listing freshness over time.
- **Data warehouses:** store normalized listing records for historical analysis, forecasting, and operational reporting.
- **CRM enrichment:** sync public listing, owner, location, and price attributes into lead or account records.
- **Google Sheets or Airtable:** review smaller market segments, validate records, or collaborate on curated listing sets.
- **Webhooks:** trigger validation, notification, ingestion, or alerting workflows after each completed run.
- **Data enrichment pipelines:** join public Otodom attributes with internal datasets for scoring, segmentation, or market intelligence.

### Export Formats And Downstream Use

Apify datasets can be exported or consumed by downstream systems for analysis, reporting, and operational workflows.

- **JSON:** for APIs, applications, and data pipelines
- **CSV or Excel:** for spreadsheet workflows and manual review
- **API access:** for automated ingestion into internal systems
- **BI and warehouses:** for reporting, dashboards, and historical analysis

### Performance

Estimated run times:

- **Small runs (< 1,000 outputs):** ~3-5 minutes
- **Medium runs (1,000-5,000 outputs):** ~5-15 minutes
- **Large runs (5,000+ outputs):** ~15-30 minutes

Execution time varies based on filters, result volume, and how much information is returned per record. Highly filtered runs can finish faster, while broad discovery or detail-rich records may take longer.

### Limitations

- Availability depends on what [Otodom.pl](https://www.otodom.pl) publicly exposes at run time.
- Some optional fields may be missing on sparse listings, older listings, promoted placements, or certain property categories.
- Very broad searches may take longer or require higher `limit` values.
- Target-side changes can affect field availability, naming, or value formats.
- Regional, account, language, or availability differences may change visible results.
- Results should be validated against your intended business rules before automated decision-making.

### Troubleshooting

- **No results returned:** check filters, location spelling, property type, deal type, and whether Otodom.pl has matching public listings.
- **Fewer results than expected:** broaden filters, raise `limit`, expand `radius`, or verify that the target contains enough matching records.
- **Some fields are empty:** optional fields depend on what each public listing provides.
- **Run takes longer than expected:** reduce scope, lower `limit` for validation, or split broad collection into smaller location or category segments.
- **Output changed:** compare the current output with the field reference and include a small sample when requesting support.

### FAQ

#### What data does this actor collect?

It collects public Otodom.pl real-estate listing data, including listing identifiers, URLs, titles, prices, locations, property attributes, images, owner information, timestamps, and related metadata when available.

#### Can I filter by location, category, date, price, or other criteria?

You can filter by location, deal type, property type, price range, area range, construction year, search radius, room count, sorting preference, and result limit. The schema does not include a separate date filter, but `sort_by` can prioritize newest or oldest listings.

#### Why did I receive fewer results than my limit?

The matching public inventory may be smaller than the requested `limit`, or the selected filters may be narrow. Broaden the location, radius, property type, price, area, construction year, or room filters to increase coverage.

#### Can I schedule recurring runs?

Yes. Use Apify schedules to run the actor daily, weekly, or on a custom cron schedule for monitoring, reporting, and enrichment workflows.

#### How do I avoid duplicates across runs?

Use `type + ":" + id` as the idempotency key. This allows downstream systems to upsert existing listings instead of creating duplicate records.

#### Can I export the data to CSV, Excel, or JSON?

Yes. Apify datasets support exports such as JSON, CSV, Excel, and other formats available in Apify Console.

#### Does this actor collect private data?

The actor is intended to collect publicly available listing information from Otodom.pl. Users are responsible for using the data lawfully and respecting privacy, platform terms, and applicable data protection requirements.

#### What should I include when reporting an issue?

Include the input used with sensitive values redacted, the run ID, expected versus actual behavior, and a small output sample if it helps explain the issue.

### Compliance & Ethics

#### Responsible Data Collection

This actor collects publicly available real-estate listing information from **https://www.otodom.pl** for legitimate business purposes, including:

- **Real-estate** research and market analysis
- **Listing monitoring** and operational reporting
- **CRM, BI, and data enrichment** workflows

Users are responsible for ensuring their use of collected data complies with applicable laws, regulations, and the target site's terms. This section is informational and not legal advice.

#### Best Practices

- Use collected data in accordance with applicable laws, regulations, and the target site's terms
- Respect individual privacy and personal information
- Use data responsibly and avoid disruptive or excessive collection
- Do not use this actor for spamming, harassment, or other harmful purposes
- Follow relevant data protection requirements where applicable, such as GDPR and CCPA

### Support

For help, use the actor page or Issues. Include the input used with sensitive values redacted, the run ID, expected versus actual behavior, and an optional small output sample so the problem can be reproduced and reviewed efficiently.

# Actor input Schema

## `deal_type` (type: `string`):

Pick the deal type for the search results. Sale listings and rental listings are separated on Otodom, so this choice keeps the dataset aligned with your intended market.

## `location` (type: `array`):

Enter location names such as Warszawa, Kraków, Gdańsk, or a specific district. Each location is used as a separate search area so you can compare markets or collect listings from several places in one run.

## `radius` (type: `string`):

Choose how far from the selected location to search. Select 0 km to keep results limited to the exact location, or choose a larger radius to include nearby listings.

## `sort_by` (type: `string`):

Select how matching listings should be ordered before collection starts. The default option follows Otodom's standard result order.

## `property_type` (type: `string`):

Choose the type of property to include. This helps avoid mixing different real-estate categories that often have different pricing, size, and availability patterns.

## `min_price` (type: `integer`):

Enter the lowest listing price to include. Leave this empty if you do not want to exclude lower-priced properties.

## `max_price` (type: `integer`):

Enter the highest listing price to include. Leave this empty if there is no upper price limit for your search.

## `min_area` (type: `integer`):

Enter the smallest property area to include. Leave this empty if smaller listings should remain in the results.

## `max_area` (type: `integer`):

Enter the largest property area to include. Leave this empty if larger listings should remain in the results.

## `min_construction_year` (type: `integer`):

Enter the oldest construction year you want to include. For example, use 2010 to collect only properties built in 2010 or later.

## `max_construction_year` (type: `integer`):

Enter the newest construction year you want to include. This is useful when you want to focus on older buildings or exclude newly built properties.

## `rooms_number` (type: `array`):

Choose one or more room counts to include in the results. Leave this empty if any number of rooms is acceptable.

## `limit` (type: `integer`):

Enter the maximum number of listings to save for each location or input search. Leave this empty to collect as many matching listings as the actor can find.

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

Choose a proxy setup if you want requests to be sent through different IP addresses. Proxies can help with access control, rate limits, and location-sensitive content.

## Actor input object example

```json
{
  "radius": "0",
  "sort_by": "default",
  "property_type": "apartments",
  "limit": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "limit": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("fatihtahta/otodom-pl-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 = { "limit": 100 }

# Run the Actor and wait for it to finish
run = client.actor("fatihtahta/otodom-pl-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 '{
  "limit": 100
}' |
apify call fatihtahta/otodom-pl-scraper --silent --output-dataset

```

## MCP server setup

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