# Poland Land Parcels API – Official ULDK Zoning (`enterprising_hamster/polish-land-parcels-uldk-zoning`) Actor

Polish land parcels (działki) from the official GUGiK ULDK service: location, area, boundary, plus local zoning plans (MPZP) and general plan zones (POG) with building limits from Rejestr Urbanistyczny. Lookup by ID, precinct or GPS. No owners. Export CSV, JSON or Excel.

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

## Pricing

from $3.20 / 1,000 parcels

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 Land Parcels API – Official ULDK Zoning

**Poland land parcels API**: look up **Polish cadastral parcels (działki ewidencyjne)** and what the **zoning plans** allow on them. Give a TERYT parcel ID, a precinct name with a parcel number, or GPS coordinates, and get the parcel's location, administrative units, area and boundary from the official **GUGiK ULDK** service, plus the **local zoning plan (MPZP, miejscowy plan zagospodarowania przestrzennego)** and the **general plan zone (POG, plan ogólny gminy)** with building limits (max height, coverage, intensity) from the national planning register **Rejestr Urbanistyczny**. One clean JSON item per parcel for land due diligence, PropTech and AI agents; export CSV, JSON or Excel. **No owners, no land-register numbers.** Polski opis: **działki ewidencyjne** z ULDK GUGiK oraz **MPZP** i strefy **planu ogólnego** z Rejestru Urbanistycznego — po ID, nazwie obrębu albo współrzędnych; bez właścicieli i KW.

**No personal data.** The Actor returns no owners and nothing from the land and mortgage register (księgi wieczyste). The public services it reads do not publish them.

### What data can you get

- **Parcel:** full ID (e.g. `221509_2.0010.707/2`), parcel number, voivodeship, county, commune and precinct names with TERYT codes.
- **Geometry:** area in m² and ha (computed from the official boundary, corrected for map projection scale), centroid latitude/longitude, bounding box, and on request the boundary as GeoJSON (WGS84).
- **Local plans (MPZP)** covering the parcel: title, status (in force, being adopted, in preparation, no longer in force), dates, share of the parcel covered, the adopting resolution with its official journal reference, and links to the plan text and drawing.
- **General plan (POG):** plan name, status, adopting resolution and link.
- **General plan zones** covering the parcel: zone designation and type (e.g. `SJ` single-family housing, `SW` multi-family, `SP` economic/industrial, `SO` open land), English and Polish names, share of the parcel covered, allowed land-use profiles, and the building limits: **max building height**, max building coverage, max above-ground intensity, min biologically active share.
- **Infill area (obszar uzupełnienia zabudowy)** and **downtown area (obszar zabudowy śródmiejskiej)** coverage, both important for building permits without a local plan.
- A plain-English `zoningNote` when nothing was found or the register did not answer.

### How to use

1. Press **Start**. With no changes the Actor looks up three example parcels (two in Szemud near Gdańsk with zoning, one in Mińsk Mazowiecki) in under a minute.
2. Paste your **parcel IDs**, or **precinct name and number** like `Krzewina 134`, or **points** like `52.2297,21.0122`.
3. Switch **Include parcel boundary** on to get GeoJSON for maps.
4. Schedule a watch list with **Only new or changed** to be told when a plan for your parcels is added or changes.

### Input

| Field | Default | What it does |
|---|---|---|
| `parcelIds` | 3 examples | Full TERYT parcel IDs, e.g. `141201_1.0001.6509`. |
| `parcelNames` | none | Precinct (obręb) or commune name and parcel number, e.g. `Krzewina 134`. One name can match parcels in several places. |
| `points` | none | `latitude,longitude` in WGS84; the parcel containing the point is returned. |
| `includeZoning` | `true` | Look the parcel up in Rejestr Urbanistyczny. Off = parcel data only. |
| `minCoveragePct` | `1` | Ignore plans and zones that cover less of the parcel (boundary slivers). |
| `includeGeometry` | `false` | Add the boundary as GeoJSON. |
| `maxMatchesPerName` | `5` | Cap for ambiguous names (1–100). |
| `maxItems` | `100` | Hard cap of parcels, and so of cost. |
| `onlyNew`, `stateKey` | `false`, `default` | Skip parcels already delivered with unchanged data. |
| `maxRequestsPerSecond` | `2` | Politeness limit towards the public services (0.2–5). |

Parcel in Szemud with MPZP/POG and GeoJSON boundary (due diligence / map):

```json
{ "parcelIds": ["221509_2.0010.707/2"], "includeZoning": true, "includeGeometry": true, "maxItems": 50 }
```

Parcel under a GPS point in Warsaw + plan zone (PropTech / lead scoring):

```json
{ "points": ["52.2297,21.0122"], "includeZoning": true, "maxItems": 20 }
```

Mixed IDs, points and precinct names:

```json
{
  "parcelIds": ["221509_2.0010.707/2"],
  "points": ["54.4686,18.3491"],
  "parcelNames": ["Krzewina 134"],
  "includeGeometry": true
}
```

### Output

One item per parcel (shortened):

```json
{
  "parcelId": "221509_2.0010.707/2",
  "commune": "Szemud",
  "region": "Kielno",
  "parcelNumber": "707/2",
  "communeTeryt": "221509_2",
  "areaM2": 7154.1,
  "centroidLat": 54.4686194,
  "centroidLon": 18.3490931,
  "hasLocalPlan": true,
  "hasGeneralPlan": true,
  "mainZoneSymbol": "SP",
  "mainZoneNameEn": "economic (industrial) zone",
  "mainZoneCoveragePct": 67.74,
  "maxBuildingHeightM": 16,
  "localPlans": [{
    "title": "w sprawie zmian miejscowego planu zagospodarowania przestrzennego dla fragmentów obrębu geodezyjnego Bojano i Kielno, gmina Szemud",
    "status": "in force",
    "inForceFrom": "2025-11-05",
    "coveragePct": 71.94,
    "documents": [{"role": "adopting resolution", "number": "Dziennik Województwa Pomorskiego, poz. 3476 z 2025-10-21",
                   "url": "https://szemud.e-mapa.net/implementation/szemud/pln/pelna_tresc/132.pdf"}],
    "drawingUrls": ["https://szemud.e-mapa.net/wykazplanow/tiff/221509/132"]
  }],
  "generalPlan": {"title": "Plan ogólny gminy Szemud", "status": "in force", "inForceFrom": "2026-09-22"},
  "generalPlanZones": [{"designation": "66SP", "symbol": "SP", "coveragePct": 67.74, "maxBuildingHeightM": 16,
                        "maxBuildingCoveragePct": 70, "maxAbovegroundIntensity": 2.5, "minBiologicallyActivePct": 20}],
  "infillAreaCoveragePct": 12.94,
  "zoningNote": null,
  "source": "ULDK parcel location service, GUGiK (uldk.gugik.gov.pl); Rejestr Urbanistyczny (rejestr-urbanistyczny.gov.pl)"
}
```

The run's key-value store also holds `SUMMARY` (parcels not found, ambiguous names, register failures) and `RUN_STATS`.

### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| `parcel` | $0.004 | A delivered parcel with no local or general plan in the register, with zoning switched off, or when the register did not answer. |
| `parcel-with-zoning` | $0.02 | A delivered parcel with at least one local plan (MPZP) or general plan zone (POG). |

1,000 parcels cost $4 without zoning and at most $20 with zoning. Parcels that are not found cost nothing, and a parcel is never charged twice in one run. Comparable zoning lookups in the Apify Store cost $0.03–0.05 per address for France or Spain.

### Use cases

- **Land due diligence:** check what may be built on a plot before an offer: zone type, max height and building coverage.
- **Real-estate lead scoring:** enrich listings or parcel lists with zoning and area.
- **PropTech and GIS:** parcel boundaries and centroids for maps without running your own GIS server.
- **Monitoring:** watch parcels and get notified when the municipality adds a plan.
- **AI agents:** a structured tool for "what can I build on parcel X?" questions.

### FAQ

**Is there an API for Polish land parcels and zoning?** GUGiK publishes ULDK (parcel by ID, name or coordinates) and Rejestr Urbanistyczny exposes MPZP/POG over WMS/WFS. This Actor turns those public services into one filterable table callable from the Apify API, with CSV/JSON/Excel export.

**How do I export Polish parcels with zoning to CSV or Excel?** Set Max items (and IDs/points/names), run the Actor, then download the dataset as CSV or Excel from the run page or via the Apify API.

**Do you include owners or land-register (KW) numbers?** No. Public ULDK and Rejestr Urbanistyczny do not publish them; the Actor never returns personal data.

**What is the difference vs Otodom/OLX scrapers or Zonio/Cenogram?** Listing scrapers are classified ads. Zonio and Cenogram are commercial parcel APIs outside Apify. This Actor is the Apify Store entry for **official GUGiK ULDK + Rejestr Urbanistyczny** (MPZP/POG building limits), pay-per-event, no owners.

**Is this legal?** Yes. GUGiK states that its spatial data services, including ULDK, "are publicly available and free of charge" and may be used "in both state and commercial systems". The Rejestr Urbanistyczny network services declare "no conditions on access and use", and zoning plans are acts of local law, which Polish copyright law excludes from protection. Please keep the source attribution (`source` field) when you publish the data.

**Why is no plan found for my parcel?** Polish communes (about 2,500) are still adopting their general plans (POG) and have until **30 November 2026** to upload their older local plans to Rejestr Urbanistyczny. In October 2026 the register held about 4,200 local plans and 560 general plans, so many plans are still missing: a parcel without a plan in the register may still have one in the commune's BIP. The `zoningNote` says so.

**Why not the older KIMPZP map service?** GUGiK's Krajowa Integracja MPZP is being switched off after the move to Rejestr Urbanistyczny, and it returns different, unstructured output for each commune. This Actor only uses the structured national register.

**Is the area the official registered area?** No. It is computed from the ULDK boundary, which usually matches the cadastral area within a few square metres but is not the legal area from the land register.

**Do I get owners or land register numbers?** No, and they are not available from these public services.

**The register is slow or down.** The Actor detects it after the first failed request, delivers the remaining parcels without zoning (charged as `parcel`), and lists them in `SUMMARY.zoningFailures`. Run them again later.

### Integrations

Use the Apify API, Python/JS clients, Make, Zapier, n8n or the MCP server to call the Actor and read the dataset as JSON, CSV or Excel.

### Development

```bash
python -m venv .venv && .venv/bin/pip install -r requirements.txt -r requirements-dev.txt
.venv/bin/python -m pytest -q
examples/run-local-ppe.sh examples/points-with-boundary.json
```

### Disclaimer

Data comes from public services run by GUGiK and the ministry responsible for spatial planning, processed by this Actor. Not an official extract: always confirm with the commune (wypis i wyrys) before legal or financial decisions. Source: Główny Urząd Geodezji i Kartografii (ULDK) and Rejestr Urbanistyczny.

### 🇵🇱 Po polsku

Aktor zwraca dane działek ewidencyjnych z usługi **ULDK GUGiK** (położenie, jednostki administracyjne z kodami TERYT, powierzchnia liczona z granicy, centroid, granica w GeoJSON) oraz informacje planistyczne z **Rejestru Urbanistycznego**: miejscowe plany (MPZP) z uchwałą i linkami do tekstu i rysunku, strefy planu ogólnego (POG) z parametrami zabudowy, obszar uzupełnienia zabudowy i zabudowy śródmiejskiej. Wyszukiwanie po identyfikatorze działki, nazwie obrębu i numerze albo współrzędnych. Bez danych osobowych i bez ksiąg wieczystych. Do 30.11.2026 gminy uzupełniają rejestr, więc brak planu w wynikach nie oznacza, że planu nie ma. Źródło: GUGiK (ULDK) i Rejestr Urbanistyczny.

# Changelog

This Actor's version history is a separate document: https://apify.com/enterprising_hamster/polish-land-parcels-uldk-zoning/changelog.md

# Actor input Schema

## `parcelIds` (type: `array`):

Full parcel identifiers from the Polish land register (EGiB), for example 141201_1.0001.6509 or 306401_1.0051.AR_26.32. If you leave parcel IDs, names and points all empty, the Actor looks up three example parcels in the municipality of Szemud and in Mińsk Mazowiecki.

## `parcelNames` (type: `array`):

A precinct (obręb) name followed by a parcel number, for example "Krzewina 134". Names repeat across Poland, so one entry can match several parcels; see 'Matches per name'.

## `points` (type: `array`):

Coordinates in decimal degrees, latitude first, for example "52.2297,21.0122" (as copied from Google Maps). Returns the parcel that contains each point.

## `includeZoning` (type: `boolean`):

Check each parcel against Rejestr Urbanistyczny: local zoning plans (MPZP) and general plan zones (POG) with building limits. Parcels with zoning found are charged as 'Parcel with zoning'.

## `minCoveragePct` (type: `number`):

Ignore plans and zones that cover less than this share of the parcel. Plan boundaries and parcel boundaries come from different maps, so slivers below 1% are usually drawing noise.

## `includeGeometry` (type: `boolean`):

Add the parcel boundary as GeoJSON in WGS84 (longitude, latitude) to each item.

## `maxMatchesPerName` (type: `integer`):

The most parcels returned for one 'precinct name and number' entry. Extra matches are listed in the run summary, not delivered or charged.

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

Hard cap on delivered (and charged) parcels in this run.

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

Skip parcels delivered by an earlier run with the same state key unless something about them changed (for example a new local plan or a plan entering into force). Skipped parcels are not charged. Use with a schedule to monitor zoning.

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

Name of the memory used by 'Only new or changed'. Use one key per watch list.

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

Polite request rate towards each public service (ULDK and the planning register).

## Actor input object example

```json
{
  "parcelIds": [
    "221509_2.0010.707/2",
    "221509_2.0017.326/10",
    "141201_1.0001.6509"
  ],
  "includeZoning": true,
  "minCoveragePct": 1,
  "includeGeometry": false,
  "maxMatchesPerName": 5,
  "maxItems": 100,
  "onlyNew": false,
  "maxRequestsPerSecond": 2
}
```

# Actor output Schema

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

One item per parcel: location, area and what the planning register says about it.

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

Lookups not found in ULDK, ambiguous names, source failures and charge limit.

# 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 = {
    "parcelIds": [
        "221509_2.0010.707/2",
        "221509_2.0017.326/10",
        "141201_1.0001.6509"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("enterprising_hamster/polish-land-parcels-uldk-zoning").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 = { "parcelIds": [
        "221509_2.0010.707/2",
        "221509_2.0017.326/10",
        "141201_1.0001.6509",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("enterprising_hamster/polish-land-parcels-uldk-zoning").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 '{
  "parcelIds": [
    "221509_2.0010.707/2",
    "221509_2.0017.326/10",
    "141201_1.0001.6509"
  ]
}' |
apify call enterprising_hamster/polish-land-parcels-uldk-zoning --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,enterprising_hamster/polish-land-parcels-uldk-zoning"
        }
    }
}
```

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/YbR4zktTxcjC2B6MN/builds/YfCqbkCjHEVLsLr1h/openapi.json
