# Poland Developer Apartment Prices API – Official dane.gov.pl (`enterprising_hamster/polish-developer-apartment-prices`) Actor

Every Polish developer price list published on dane.gov.pl (ceny mieszkań deweloperów, jawność cen), parsed fresh into one clean record per apartment or house, with price-change and new/sold detection. Export CSV, JSON or Excel.

- **URL**: https://apify.com/enterprising_hamster/polish-developer-apartment-prices.md
- **Developed by:** [Kamil Kaczmarek](https://apify.com/enterprising_hamster) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 offers

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

## Poland Developer Apartment Prices API – Official dane.gov.pl

**Poland developer apartment prices API**: get **asking prices of new apartments and houses in Poland** straight from the official developer price lists on [dane.gov.pl](https://dane.gov.pl) under the 2025 price transparency law (**jawność cen mieszkań**). The Actor downloads the newest lists, reads every format they come in (the ministry CSV/XLSX template and its variants, ODS, XLS, vendor JSON and XML), and returns one clean record per apartment or house: price, price per m², area, location, investment, developer company and availability. A second mode compares each list with the developer's previous one and returns only new, removed, repriced or sold units. Export CSV, JSON or Excel. Polski opis: **ceny mieszkań deweloperów** / **cenniki deweloperów** z dane.gov.pl (jawność cen) — jeden rekord na lokal z ceną, m², lokalizacją i inwestycją; wykrywanie nowych, sprzedanych i zmian ceny.

Keywords: ceny mieszkań deweloperów, ceny ofertowe mieszkań, jawność cen mieszkań, cennik dewelopera, cena za m2, rynek pierwotny, nowe mieszkania ceny, dane.gov.pl ceny mieszkań, zmiany cen mieszkań.

### What data can you get

Each record is one unit from one developer price list:

- **Unit:** `unitId` (developer's unit number), `propertyType` (`apartment`, `house`, or null when the list does not say), `areaM2`, `rooms` and `floor` when listed.
- **Prices (PLN):** `pricePerM2`, `priceBase` (area × price per m²), `priceTotal` (with the other components the developer lists), `price` (total, else base), and the date each price applies from.
- **Add-ons sold with the unit:** `parkingAndOtherParts`, `storageRooms`, `rights`, `otherPayments`, each with type or description, id and price.
- **Availability:** `status` as written and `isAvailable` (false for sold or reserved; null when the list does not say, which is the most common case).
- **Location:** `voivodeship`, `county`, `municipality`, `city`, `street`, `buildingNumber`, `postalCode`, `locationText`.
- **Investment and developer:** `investmentName`, `developerName` (companies only), `developerLegalForm`, `developerKrs`, `developerNip`, `developerRegon`, `prospectusUrl`, `offerUrl`.
- **Provenance:** `dataDate` (the day the list describes), `publishedAt`, `datasetId`, `resourceId`, `sourceUrl` (the file's page on dane.gov.pl), `fileUrl` (companies only), `source`, `license`, `retrievedAt`, plus `derivedFields` and `qualityFlags` that tell you what was computed or looks implausible.

In `priceChanges` mode each record also has `changeType` (`new`, `removed`, `price-change`, `availability-change`), `previousDataDate`, `previousPrice`, `previousPricePerM2`, `previousIsAvailable`, `priceChange`, `priceChangePct`, `pricePerM2Change` and `pricePerM2ChangePct`.

Parking spaces, storage rooms and commercial units are not returned as separate records. They appear only as add-ons of a unit.

### How to use

1. Run with the default input: units from every developer list published in the last 24 hours, up to 1,000 records.
2. Narrow it with `cities`, `voivodeships`, `developers` or `propertyTypes`, and raise `maxItems`. On 2026-10-19 developers published about 6,300 files, and in a test run a list held about 30 units on average.
3. For a daily feed, schedule the Actor with `onlyNew: true` (each list is delivered once) or with `mode: "priceChanges"` (only what changed).

```json
{
  "lastDays": 1,
  "cities": ["Warszawa"],
  "propertyTypes": ["apartment"],
  "onlyNew": true,
  "stateKey": "warsaw-daily",
  "maxItems": 20000
}
```

The `examples` directory has more inputs.

### Input

Warsaw apartments from lists published in the last 24 hours (each list once):

```json
{
  "lastDays": 1,
  "cities": ["Warszawa"],
  "propertyTypes": ["apartment"],
  "onlyNew": true,
  "stateKey": "warsaw-daily",
  "maxItems": 20000
}
```

Daily price-change feed (new, removed, repriced or sold units):

```json
{ "mode": "priceChanges", "lastDays": 1, "maxPriceLists": 40, "maxItems": 3000 }
```

| Field | Default | Meaning |
| --- | --- | --- |
| `mode` | `offers` | `offers`: every unit of the newest list of each developer dataset published in the period. `priceChanges`: only units that are new, removed, repriced or changed availability since that dataset's previous list. |
| `lastDays` | 1 | Lists published in the last N×24 hours (1–14). Ignored when `dateFrom` or `dateTo` is set. |
| `dateFrom`, `dateTo` | – | Publication dates, YYYY-MM-DD, Polish time, inclusive, at most 14 days. |
| `voivodeships` | all | Keep units in these voivodeships, e.g. `mazowieckie`. |
| `cities` | all | Keep units whose city or municipality is one of these. Case and Polish letters do not matter. |
| `developers` | all | Keep units whose developer name, list title or investment name contains one of these fragments. |
| `propertyTypes` | `apartment`, `house` | Units of unknown type are kept. |
| `onlyAvailable` | false | Drop units marked sold or reserved. Units of unknown availability are kept. |
| `onlyNew` | false | Skip lists delivered in an earlier run with the same `stateKey`. |
| `stateKey` | `default` | Separate memory for `onlyNew`, e.g. one per schedule. |
| `maxItems` | 1000 | Stop after this many records (each is one charged event). |
| `maxPriceLists` | 0 (all) | Read at most this many lists, newest first. |
| `maxConcurrency` | 8 | Lists read in parallel. |
| `maxRequestsPerSecond` | 8 | Politeness limit for file downloads. The dane.gov.pl API is always queried at 2 requests per second. |

### Output

```json
{
  "offerId": "3e5718c787ae8e68",
  "propertyType": "apartment",
  "unitId": "M11",
  "areaM2": 44.64,
  "rooms": 2,
  "floor": "1",
  "pricePerM2": 15950.0,
  "priceTotal": 712008.0,
  "price": 712008.0,
  "parkingAndOtherParts": [],
  "storageRooms": [],
  "status": "Dostępny",
  "isAvailable": true,
  "developerName": "ROGATKA22 SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ",
  "developerIsCompany": true,
  "developerKrs": "0001109323",
  "investmentName": "Rogatka 22",
  "voivodeship": "małopolskie",
  "municipality": "Kraków",
  "city": "Kraków",
  "street": "Rogatka",
  "buildingNumber": "22",
  "postalCode": "31-425",
  "derivedFields": [],
  "qualityFlags": [],
  "dataDate": "2026-10-19",
  "publishedAt": "2026-10-19T16:22:25Z",
  "datasetId": "42157",
  "resourceId": "2828404",
  "sourceUrl": "https://dane.gov.pl/pl/dataset/42157/resource/2828404",
  "license": "CC0 1.0",
  "retrievedAt": "2026-10-20T15:26:12Z"
}
```

This is a shortened record from a test run. Full records include every field listed above, with `null` where the list has no value.

Besides the dataset, every run saves to its key-value store:

- `RESOURCE_REPORT`: one entry per file with its status (`parsed`, `no-units`, `not-a-price-list`, `unsupported-format`, `parse-error`, `download-error`, `too-large`, `unsafe-url`, `previous-list-error`, `no-previous-list`) and the number of units found.
- `SUMMARY`: resources listed, lists read and status counts.
- `RUN_STATS`: requests, retries and records per event.

### Pricing (pay per event)

| Event | Price | Charged for |
| --- | --- | --- |
| `offer` | $0.002 | Each unit record saved in `offers` mode |
| `price-change` | $0.004 | Each change record saved in `priceChanges` mode |

You pay only for records saved to the dataset. Files that fail, filtered-out units and lists skipped by `onlyNew` cost nothing. The run stops cleanly when your maximum cost per run is reached. Apify platform usage is billed separately as usual.

### Use cases

- **Buyers and advisors:** compare price per m² across investments in a city and watch for discounts.
- **Developers:** track competitors' prices and sales pace (units that disappear or become sold).
- **Banks, valuers and analysts:** build a daily primary-market price index by city or voivodeship.
- **Journalists and researchers:** analyse how asking prices move after the transparency law.
- **AI agents and RAG:** give an assistant current, sourced asking prices for a given location.

### FAQ

**Is there an API for Polish developer apartment prices / ceny mieszkań deweloperów?** Developers must publish daily price lists on dane.gov.pl under the 2025 transparency law (jawność cen mieszkań). This Actor turns those live files into **one clean, filterable table** callable from the Apify API, with CSV/JSON/Excel export.

**How do I export Polish developer apartment prices to CSV or Excel?** Set `lastDays` or dates, optional `cities` / `voivodeships` / `developers`, run the Actor, then download the dataset as CSV or Excel from the run page or via the Apify API.

**Do you include personal data, phones or e-mails?** No contact columns and no head-office or sales-office address. Developer name and file link only when the developer is a company; NIP/REGON only for KRS companies. E-mails and phones in free text become `[redacted]`.

**What is the difference vs Otodom / OLX / listing scrapers?** Listing scrapers are classified ads (often secondary market, contacts, incomplete asking prices). This Actor reads the **official developer price lists** published on dane.gov.pl for the primary market, with price-change and new/sold detection.

**Is it legal to use this data?** Yes. Developers publish these lists under art. 19b of the Act of 20 May 2021 on protecting buyers of residential units, as amended in 2025 (Dz.U. 2025 poz. 758), and the minister publishes them on dane.gov.pl as open data. The developer datasets on dane.gov.pl are licensed CC0 1.0, and the portal states that its data may be reused without an application. Every record keeps its source and file link.

**What about personal data?** Many developers are sole traders, and their lists contain a person's name, phone numbers, e-mails and office addresses. The Actor outputs no contact columns and no head-office or sales-office address. It outputs a developer name and the file link only when the developer is a company, and NIP and REGON only for companies registered in KRS. E-mail addresses and phone numbers inside free-text fields are replaced with `[redacted]`.

**How fresh is it?** Developers publish updated lists on dane.gov.pl, many of them daily. The Actor reads the live dane.gov.pl API and the files at run time. It does not use a cached index.

**How complete is the parsing?** In a test run on 150 developer datasets published on 2026-10-19 (187 files), 179 files gave units. Six were PDFs, web pages or ZIP archives, which are reported as `unsupported-format`. Of the 4,495 units, 90% had a unit number, 95% had area and price, 90% had a city, 80% had a voivodeship, and 20% had availability. `RESOURCE_REPORT` shows the outcome for every file.

**Why does a city filter take as long as an unfiltered run?** dane.gov.pl does not index lists by location, so every list published in the period is downloaded and read before filtering. You are charged only for records that pass the filters.

**Rate limits?** The Actor queries the dane.gov.pl API at 2 requests per second and downloads files at up to `maxRequestsPerSecond`. Failed requests are retried with backoff. Listing one busy day takes about two minutes.

### Integrations

- **API:** start runs and read the dataset with the Apify API or the Python and JavaScript clients.
- **Schedules:** run daily with `onlyNew` or `priceChanges` for a feed of new data only.
- **Webhooks:** call your endpoint, Make, Zapier or n8n when a run finishes.
- **MCP and AI agents:** call the Actor as a tool through the Apify MCP server.

### Development

```bash
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest -q        # offline tests on redacted real price lists
examples/run-local-ppe.sh examples/smoke-one-day.json   # local run with simulated pay-per-event prices (apify-cli, no login)
```

`examples/pricing_info.local.json` holds the simulated prices. `docs/DESIGN.md` describes the pipeline, parsing and privacy rules.

### Limitations

- Location fields are only as good as the list. About 10% of units have no city and 20% no voivodeship, and the location filters drop them.
- `isAvailable` is null for most units, because most lists do not state availability.
- In `priceChanges` mode the previous list is searched among the dataset's newest 100 files. A dataset with no earlier list gives no changes and is reported as `no-previous-list`. If any file of either list cannot be read, that dataset gives no changes, so that no false "new" or "removed" records are created. Units without a developer unit number are left out of change detection.
- Unit ids (`offerId`) are stable as long as the developer keeps the same dataset, street, building and unit number.
- Some files linked from developer websites cannot be downloaded: the host refuses the request (HTTP 403) or has a broken TLS certificate. They are reported as `download-error`, typically 1–2% of files.
- `priceTotal` follows each developer's own definition, so it includes different components from list to list. Compare `pricePerM2` or `priceBase` across developers.

### Disclaimer

This Actor is unofficial. It is not affiliated with or endorsed by the Ministry of Digital Affairs, dane.gov.pl or any developer. Prices are asking prices as published by developers. They are not transaction prices or offers to sell. Check the source file (`sourceUrl`) before relying on a figure.

### 🇵🇱 Po polsku

**Ceny mieszkań deweloperów z dane.gov.pl** – Actor pobiera aktualne cenniki, które deweloperzy publikują na podstawie ustawy o jawności cen mieszkań. Zwraca jeden rekord na mieszkanie lub dom: cenę, cenę za m², powierzchnię, lokalizację, inwestycję, nazwę spółki dewelopera i dostępność. Tryb `priceChanges` porównuje cennik z poprzednim i zwraca tylko nowe, wycofane, przecenione lub sprzedane lokale.

- **Wejście:** okres publikacji (`lastDays` albo `dateFrom`/`dateTo`), filtry: województwa, miejscowości, deweloperzy, rodzaj nieruchomości, tylko dostępne, tylko nowe cenniki (`onlyNew`), limit rekordów `maxItems`.
- **Wyjście:** rekordy w zbiorze danych (pola opisane wyżej) oraz raport dla każdego pliku (`RESOURCE_REPORT`).
- **Cena:** $0.002 za rekord lokalu (`offer`) i $0.004 za rekord zmiany (`price-change`). Płacisz tylko za zapisane rekordy.
- **Dane osobowe:** Actor nie zwraca telefonów, e-maili ani adresów biur. Nazwy i numery NIP/REGON zwraca tylko dla spółek.
- Dane: dane.gov.pl, licencja CC0 1.0. Actor nie jest oficjalnym narzędziem ministerstwa ani portalu.

# Changelog

This Actor's version history is a separate document: https://apify.com/enterprising_hamster/polish-developer-apartment-prices/changelog.md

# Actor input Schema

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

Offers returns each unit of the newest price list of every developer dataset published in the period. Price changes compares that list with the developer's previous one and returns only differences.

## `lastDays` (type: `integer`):

Price lists published on dane.gov.pl in the last N×24 hours. Ignored when a date is set below. Developers republish lists daily, so 1 day already covers most active investments.

## `dateFrom` (type: `string`):

First publication date, YYYY-MM-DD, Polish time. At most 14 days with dateTo.

## `dateTo` (type: `string`):

Last publication date, inclusive, YYYY-MM-DD, Polish time.

## `voivodeships` (type: `array`):

Keep units in these voivodeships (województwa). Units whose list gives no voivodeship are dropped by this filter.

## `cities` (type: `array`):

Keep units whose city (miejscowość) or municipality (gmina) is one of these, e.g. Warszawa, Kraków. Case and Polish letters do not matter.

## `developers` (type: `array`):

Keep units whose developer name, price-list title or investment name contains one of these fragments, e.g. Dom Development, Murapol.

## `propertyTypes` (type: `array`):

Parking spaces, storage and commercial units are never returned as units; they appear as add-ons of a unit. Units whose type the list does not state are kept.

## `onlyAvailable` (type: `boolean`):

Drop units marked sold or reserved. Most lists do not state availability; those units are kept.

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

Skip price lists this Actor already delivered in an earlier run with the same state key. Use with a daily schedule.

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

Separate memory for onlyNew, e.g. one key per schedule. Letters, digits, \_ and -.

## `maxItems` (type: `integer`):

Stop after this many records. Each record is one charged event.

## `maxPriceLists` (type: `integer`):

Read at most this many developer price lists, newest first. 0 means all.

## `maxConcurrency` (type: `integer`):

Price lists read at the same time.

## `maxRequestsPerSecond` (type: `number`):

Politeness limit for file downloads. The dane.gov.pl API itself is always queried at 2 requests per second.

## Actor input object example

```json
{
  "mode": "offers",
  "lastDays": 1,
  "voivodeships": [],
  "cities": [],
  "developers": [],
  "propertyTypes": [
    "apartment",
    "house"
  ],
  "onlyAvailable": false,
  "onlyNew": false,
  "maxItems": 1000,
  "maxPriceLists": 0,
  "maxConcurrency": 8,
  "maxRequestsPerSecond": 8
}
```

# Actor output Schema

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

One record per unit (mode offers) or per change (mode priceChanges).

## `report` (type: `string`):

What happened to every price list: parsed, unsupported format, download error and so on.

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

Counts of listed resources, price lists read and file statuses.

# 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 = {
    "lastDays": 1,
    "maxItems": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("enterprising_hamster/polish-developer-apartment-prices").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 = {
    "lastDays": 1,
    "maxItems": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("enterprising_hamster/polish-developer-apartment-prices").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 '{
  "lastDays": 1,
  "maxItems": 1000
}' |
apify call enterprising_hamster/polish-developer-apartment-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,enterprising_hamster/polish-developer-apartment-prices"
        }
    }
}
```

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/fP88h0hjdF3gRvOwc/builds/x8nWo9YIrWGPNXvfm/openapi.json
