# Zonio Developers Poland’s land, zoning and infrastructure data as an API and an MCP server. Search 38M parcels in plain language from your code or your AI agent. # Introduction [Zonio](https://zonio.tech/en/) turns the fragmented world of Polish land data (cadastre, local and general zoning plans, environmental protection, the power grid, the property market) into one consistent, national dataset. The developer platform gives you that dataset in three forms: :::tip[Private preview] The platform is in private preview. Everything on this site describes the API we are opening to design partners; a key is required for every call. [Request access](/access). ::: ## Why this is hard without Zonio To answer *"can I build a 5 MW solar farm on this plot?"* in Poland you normally need to: 1. find the parcel in the [national cadastre (EGiB)](https://zonio.tech/en/layers/cadastre/cadastral-parcels/) and its exact geometry; 2. find out whether one of ~2,477 gminas has adopted a [local plan (MPZP)](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/) for it, locate that gmina's own geoportal, and read the zone symbol; 3. check the new [general plan (Plan Ogólny)](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/), the legacy [Studium](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/), and the statutory pre-emption rights of [KOWR](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/) and the State Forests; 4. intersect it with [Natura 2000 and protected landscapes](https://zonio.tech/en/layers/environment/protected-areas-gdos/), [flood-risk maps](https://zonio.tech/en/layers/hazards/flood-hazard-isok/), [heritage registers](https://zonio.tech/en/layers/cultural-heritage/heritage-historic-sites/), [mining areas](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/), [aviation obstacle surfaces](https://zonio.tech/en/layers/infrastructure/height-limits-ols/) and groundwater protection zones; 5. measure the distance to the nearest [110 kV substation](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/) and look up how much [connection capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/) the distribution operator has published there ([how that works](https://zonio.tech/en/articles/grid-connection-capacity-poland/)); 6. compare against recent land sales in the [national price register](https://zonio.tech/en/layers/market/transaction-prices/). Each of those is a different public service with a different format, coordinate system, paging quirk and update cycle. Zonio fetches all of them, normalises them into one PostGIS database in Poland's national coordinate grid, and keeps them fresh. You make one call. ## Who it is for * **AI and agent builders** who want their assistant to answer real-estate and energy-siting questions about Poland with real data instead of guesses. * **Renewable-energy developers** screening thousands of hectares for PV, wind, BESS, agri-PV and biogas. * **Real-estate and investment teams** running acquisition pipelines and due diligence. * **PropTech and GIS products** that need a parcel-level data backbone for Poland. ## Zonio for people The same data powers the [Zonio app](https://zonio.tech/en/features/): a national map with every layer, parcel reports and gmina profiles for investors, developers and advisers. Read [why we built it](https://zonio.tech/en/articles/introducing-zonio/), see the [features](https://zonio.tech/en/features/) and [pricing](https://zonio.tech/en/pricing/), or browse the [layer atlas](https://zonio.tech/en/layers/). ## Next steps * Make your first call in the [Quickstart](/quickstart). * Connect an agent with the [MCP guide](/mcp/connect). * Learn the vocabulary (EGiB ids, TERYT codes, MPZP, coverage) in [Core concepts](/concepts). ## Docs for agents This site is also published for language models: [`/llms.txt`](https://docs.zonio.tech/llms.txt) is an index of every page and [`/llms-full.txt`](https://docs.zonio.tech/llms-full.txt) is the whole site as one Markdown file. Point your coding agent at it when building on Zonio. # Quickstart ::::steps ### Get an API key [Request access](/access). Once approved, open **Settings → API keys** in the Zonio app and create a key. Test keys start with `zk_test_` and only see the sandbox; live keys start with `zk_live_`. ```bash export ZONIO_API_KEY="zk_test_4f9c2b..." ``` ### Find a parcel Look up the parcel under a point, here Kraków's main market square: :::code-group ```bash [cURL] curl "https://api.zonio.tech/v1/parcels/at?lng=19.9372&lat=50.0614" \ -H "Authorization: Bearer $ZONIO_API_KEY" ``` ```ts [TypeScript] import { Zonio } from '@zonio/sdk' const zonio = new Zonio({ apiKey: process.env.ZONIO_API_KEY }) const parcel = await zonio.parcels.at({ lng: 19.9372, lat: 50.0614 }) ``` ```python [Python] from zonio import Zonio zonio = Zonio() # reads ZONIO_API_KEY parcel = zonio.parcels.at(lng=19.9372, lat=50.0614) ``` ::: ```json { "type": "Feature", "id": "126101_1.0001.27/2", "geometry": { "type": "Polygon", "coordinates": [[[19.9365, 50.0611], "…"]] }, "properties": { "parcel_id": "126101_1.0001.27/2", "area_sqm": 31260.4, "gmina": "Kraków", "teryt": "1261011" } } ``` ### Ask what can be built there ```bash curl "https://api.zonio.tech/v1/parcels/126101_1.0001.27%2F2/regulations" \ -H "Authorization: Bearer $ZONIO_API_KEY" ``` You get the local plan zone, the general plan zone and any pre-emption rights. See [Parcel due diligence](/guides/due-diligence) for the full picture. ### Search in plain language ```bash curl https://api.zonio.tech/v1/search/natural-language \ -H "Authorization: Bearer $ZONIO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "vacant plots over 1 ha zoned for housing in Wieliczka, within 800 m of a bus stop"}' ``` The response contains the matching parcels **and** the structured filters Zonio derived from your sentence. See [Plain-language parcel search](/mcp/natural-language-search). ### Give the same powers to your agent ```bash [Claude Code] claude mcp add --transport http zonio https://mcp.zonio.tech/mcp \ --header "Authorization: Bearer $ZONIO_API_KEY" ``` Then ask: *"Which parcels in Wieliczka are zoned for single-family housing, vacant and bigger than 1,500 m²?"* More clients in [Connect your agent](/mcp/connect). :::: # Authentication Every request to the REST API and the MCP server is authenticated. There are no anonymous endpoints. ## API keys Send your key as a bearer token: ```bash curl https://api.zonio.tech/v1/layers \ -H "Authorization: Bearer zk_live_8a1f…" ``` | Prefix | Environment | Data | | --- | --- | --- | | `zk_test_` | `sandbox.api.zonio.tech` | Kraków and Wrocław only, free, rate-limited | | `zk_live_` | `api.zonio.tech` | All of Poland, billed per plan | Keys are created and revoked in the Zonio app under **Settings → API keys**. A key belongs to an organization, not a person, so it survives team changes. ## Scopes Restrict a key to what it needs. A key handed to an AI agent should usually be read-only. | Scope | Grants | | --- | --- | | `parcels:read` | Parcel lookup, regulations, constraints, terrain, proximity | | `search:read` | Structured, natural-language and scenario search | | `market:read` | Transaction comparables and price medians | | `layers:read` | Raw layer queries and vector tiles | | `reports:create` | Due-diligence reports (JSON and PDF) | ## OAuth for MCP clients Clients that support the MCP authorization flow (Claude.ai, Claude Desktop, ChatGPT connectors) don't need a key at all. Add `https://mcp.zonio.tech/mcp` as a connector, sign in with your Zonio account in the browser window that opens, and approve the scopes. Tokens are short-lived and can be revoked from **Settings → Connected apps**. Headless agents, CI jobs and servers use an API key in the `Authorization` header instead. See [Connect your agent](/mcp/connect). ## Keeping keys safe * Never ship a live key in a browser bundle or mobile app. Proxy through your backend, or use a test key for prototypes. * Use one key per integration so you can revoke one without breaking the others. * Every response carries a `Zonio-Request-Id` header. Include it when you contact support. # Core concepts ## Parcels and parcel ids A **parcel** (*działka ewidencyjna*) is the unit of land ownership in the national cadastre, EGiB. Zonio holds all ~38.7 M of them; see the [cadastral parcels layer](https://zonio.tech/en/layers/cadastre/cadastral-parcels/) on zonio.tech. Each has a nationally unique id: ``` 126101_1.0018.AR_1.100/1 └──┬───┘ └┬─┘ └─┬─┘ └─┬─┘ │ │ │ └ parcel number (may contain "/") │ │ └ map sheet (optional) │ └ cadastral precinct (obręb) └ cadastral unit (jednostka ewidencyjna, TERYT-based) ``` Because parcel numbers contain `/`, URL-encode it in paths: `100/1` → `100%2F1`. In JSON bodies, send the id as-is. ## Regions and TERYT codes Poland has 16 [voivodeships](https://zonio.tech/en/layers/cadastre/voivodeship-boundaries/) (*województwa*), 380 [powiats](https://zonio.tech/en/layers/cadastre/county-boundaries/) and ~2,477 [gminas](https://zonio.tech/en/layers/cadastre/municipality-boundaries/). Every one of them has its own page on the [Zonio national map](https://zonio.tech/en/map/). Every unit has a **TERYT** code: 2 digits for a voivodeship, 4 for a powiat, 7 for a gmina. Wherever the API takes a `region`, pass a TERYT code. Gmina names repeat across the country (there are several *Ostrów*), so resolve names with [`GET /regions`](/reference) or let the natural-language search do it for you. ## Planning instruments | Instrument | What it is | In the API | | --- | --- | --- | | **[MPZP](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/)** (*miejscowy plan zagospodarowania przestrzennego*) | The binding local zoning plan. Covers roughly a third of Poland. Defines zones (e.g. `MN` single-family, `MW` multi-family, `U` services, `P` production) and parameters such as max height and coverage. | `regulations.mpzp` | | **[Plan Ogólny](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/)** | The new general plan every gmina must adopt. It replaces the Studium and gates development permits outside an MPZP. [What it changes](https://zonio.tech/en/articles/general-plan-what-changes/). | `regulations.plan_ogolny` | | **[Studium](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/)** | The legacy, non-binding directional study. Still informative where no Plan Ogólny exists yet. | `regulations.studium` | | **WZ** (*warunki zabudowy*) | A case-by-case building permit outside an MPZP. | inferred via Plan Ogólny zone | Zone symbols are not standardised between gminas. Zonio maps every raw symbol to a common **category** (`residential_single_family`, `industrial`, `renewable_energy`, …) and keeps the original symbol, so you can filter nationally and still show the user the exact legal designation. ## Constraints A constraint is anything that limits what can be built: [Natura 2000 and other protected areas](https://zonio.tech/en/layers/environment/protected-areas-gdos/), [flood-risk zones](https://zonio.tech/en/layers/hazards/flood-hazard-isok/), [heritage protection](https://zonio.tech/en/layers/cultural-heritage/heritage-historic-sites/), [mining areas](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/), [aviation obstacle limitation surfaces](https://zonio.tech/en/layers/infrastructure/height-limits-ols/), groundwater protection zones, [statutory wind-turbine setbacks](https://zonio.tech/en/layers/renewables/700-m-from-wind-turbines/), [forest land](https://zonio.tech/en/layers/land-cover-land-use/forests-bdl/). Each is reported with a **severity**: `blocking`, `restrictive` or `informational`. ## Coverage Not every public dataset covers all of Poland. Local plans are published by each gmina; some layers are only available for some regions. Zonio never treats *"no data"* as *"no constraint"*. Every search and report returns a **coverage** block that tells you, criterion by criterion, whether it was checked: ```json "coverage": [ { "criterion": "zone_residential", "mode": "requires", "status": "available" }, { "criterion": "no_env_risk_nearby", "mode": "screens", "status": "available" }, { "criterion": "not_contaminated", "mode": "screens", "status": "missing" } ] ``` `requires` criteria with missing data block the search (`blocked: true`) rather than returning nothing. `screens` criteria with missing data still return results, flagged as unscreened. Agents are instructed to tell users about every `missing` criterion. See [Coverage & freshness](/data/coverage). ## Coordinates | Purpose | CRS | Where you see it | | --- | --- | --- | | Storage and every calculation | **EPSG:2180** (PUWG 1992) | distances, areas, buffers, always metres | | Wire format | EPSG:4326 (WGS84) | all GeoJSON, all `lng`/`lat` inputs and outputs | | Rendering | EPSG:3857 | inside vector tiles only | You send and receive WGS84. Zonio measures in metres on Poland's national grid, where scale error stays below 0.1 % country-wide. Degree-based buffers would under-reach east–west by 34–42 % at Polish latitudes. ## Scenarios A **scenario** is a named, tunable site-selection screen (`solar_farm_site`, `bess_storage`, `housing_estate_site`) made of hard filters (all must pass), parameters with sensible defaults (e.g. max distance to a substation) and ranking signals with default weights. See the [scenario catalogue](/data/scenarios). # Zonio MCP server The Zonio MCP server gives any [Model Context Protocol](https://modelcontextprotocol.io) client (Claude, Claude Code, Cursor, ChatGPT, your own agent) direct access to Poland's land data. Your users ask questions in plain language; the agent turns them into precise searches over 38.7 M parcels and explains the answer. ``` https://mcp.zonio.tech/mcp ``` | Property | Value | | --- | --- | | **Transport** | Streamable HTTP | | **Auth** | OAuth 2.1 (interactive clients) or `Authorization: Bearer zk_…` (headless) | | **Tools** | 13: search, parcel inspection, regulations, constraints, proximity, market, reports | | **Resources** | Layer catalogue, scenario catalogue, parcel and region documents | | **Prompts** | Site selection, due diligence, shortlist comparison | ## What your agent can do > **You:** I'm looking for land for a 10 MW solar farm in Lubelskie. At least 15 ha, flat, close to a substation, nothing protected nearby. What's out there? > **Claude** · *called `search_parcels`, then `check_constraints` on the top results* > > I found **23 parcels** that fit. The three strongest: > > | Parcel | Area | Substation | Slope | Solar yield | > | --- | --- | --- | --- | --- | > | `0614072_2.0007.412` | 18.2 ha | 2.1 km (GPZ Hrubieszów, 110 kV) | 1.4 % | 1,086 kWh/kWp | > | `0611042_2.0012.88/3` | 16.7 ha | 3.4 km | 2.2 % | 1,079 kWh/kWp | > | `0603052_2.0003.1201` | 21.5 ha | 4.8 km | 0.9 % | 1,081 kWh/kWp | > > All three are outside Natura 2000 and flood-risk zones. Two caveats: none of them has a local plan (MPZP), so you'd need one for a farm of this size on class III soil; the first parcel is class IV, which is simpler. And grid-capacity disclosures for this DSO are missing in the region, so I could not check available connection capacity. Every number in that answer comes from a tool call over real layers ([solar irradiance](https://zonio.tech/en/layers/renewables/solar-irradiance-pvgis/), [substations](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/), [terrain slope](https://zonio.tech/en/layers/terrain-relief/terrain-slope-lidar/), [protected areas](https://zonio.tech/en/layers/environment/protected-areas-gdos/), [soil classes](https://zonio.tech/en/layers/land-cover-land-use/land-use-soil-classes-egib/)), and the caveats come from the [`coverage`](/concepts#coverage) block the server returns. The agent is told what it could *not* check, so it does not over-promise. ## Why MCP and not just the REST API? The REST API is the right choice when your code knows exactly what it wants. MCP is the right choice when a **person** is asking, and the question is fuzzy: * **Tools are designed for language models.** Descriptions explain when to use each tool, inputs accept names as well as codes (`"Wieliczka"` or `"1219062"`), and outputs are compact summaries with ids to drill into, not 5 MB of GeoJSON. * **Plain-language search is built in.** [`search_parcels`](/mcp/tools#search_parcels) accepts the user's own words and returns the interpretation it used, so the agent can confirm or refine. See [how it works](/mcp/natural-language-search). * **Explanations, not just data.** Results carry `why` fields (which filters passed, which signal ranked a parcel first) so the agent can justify its answer. * **Safe by default.** Every tool is read-only, and each key only exposes the tools its scopes allow. ## Get started # Connect your agent The server lives at a single URL. Interactive clients sign in with OAuth; everything else sends an API key. ``` https://mcp.zonio.tech/mcp ``` ## Claude.ai and Claude Desktop 1. Open **Settings → Connectors → Add custom connector**. 2. Name it **Zonio** and paste `https://mcp.zonio.tech/mcp`. 3. Click **Connect**, sign in with your Zonio account and approve the scopes. Start a new chat and ask *"What's the zoning of parcel 126101\_1.0018.AR\_1.100/1?"* Claude will ask permission the first time it calls a Zonio tool. ## Claude Code ```bash claude mcp add --transport http zonio https://mcp.zonio.tech/mcp \ --header "Authorization: Bearer $ZONIO_API_KEY" ``` Add `--scope project` to share it with your team through `.mcp.json`: ```json [.mcp.json] { "mcpServers": { "zonio": { "type": "http", "url": "https://mcp.zonio.tech/mcp", "headers": { "Authorization": "Bearer ${ZONIO_API_KEY}" } } } } ``` ## Cursor ```json [~/.cursor/mcp.json] { "mcpServers": { "zonio": { "url": "https://mcp.zonio.tech/mcp", "headers": { "Authorization": "Bearer zk_live_…" } } } } ``` ## VS Code (GitHub Copilot) ```json [.vscode/mcp.json] { "servers": { "zonio": { "type": "http", "url": "https://mcp.zonio.tech/mcp", "headers": { "Authorization": "Bearer ${input:zonio-key}" } } }, "inputs": [ { "id": "zonio-key", "type": "promptString", "description": "Zonio API key", "password": true } ] } ``` ## Clients without remote-server support Use the `mcp-remote` bridge to expose the remote server over stdio: ```json { "mcpServers": { "zonio": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.zonio.tech/mcp", "--header", "Authorization: Bearer ${ZONIO_API_KEY}"], "env": { "ZONIO_API_KEY": "zk_live_…" } } } } ``` ## Your own agent * **Claude API:** pass the server in `mcp_servers` and Anthropic connects to it for you. See [Build an agent with the Claude API](/mcp/claude-api). * **Any MCP SDK:** connect with the Streamable HTTP client transport and the `Authorization` header. ```ts [agent.ts] import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' const client = new Client({ name: 'my-agent', version: '1.0.0' }) await client.connect( new StreamableHTTPClientTransport(new URL('https://mcp.zonio.tech/mcp'), { requestInit: { headers: { Authorization: `Bearer ${process.env.ZONIO_API_KEY}` } }, }), ) const { tools } = await client.listTools() const result = await client.callTool({ name: 'search_parcels', arguments: { query: 'vacant plots over 1 ha zoned for logistics near the A4 in Gliwice' }, }) ``` ## Limit what the agent can do Create a dedicated key for each agent with only the scopes it needs (see [Scopes](/authentication#scopes)). Tools outside the key's scopes are hidden from `tools/list`, so the model never sees them. | To expose | Give the key | | --- | --- | | Search and read-only inspection | `parcels:read`, `search:read` | | + market comparables | `market:read` | | + PDF reports | `reports:create` | ## Verify the connection Ask your agent: *"Use Zonio to tell me which layers are available."* It should call [`list_layers`](/mcp/tools#list_layers) and list ~45 layers. If it can't see the tools, check that the key is live (`zk_live_`) and that your client supports Streamable HTTP. # Tools The Zonio MCP server exposes 13 tools. Each description below is the one the model sees, followed by its inputs and an abbreviated example result. All tools are annotated `readOnlyHint: true`. | Tool | Use it to | Scope | | --- | --- | --- | | [`search_parcels`](#search_parcels) | Find parcels from a plain-language description | `search:read` | | [`find_sites`](#find_sites) | Run a ready-made site-selection scenario | `search:read` | | [`list_scenarios`](#list_scenarios) | See which scenarios exist and their parameters | `search:read` | | [`locate_parcel`](#locate_parcel) | Turn an address, coordinates or a parcel number into a parcel | `parcels:read` | | [`resolve_region`](#resolve_region) | Turn a place name into a TERYT code | `parcels:read` | | [`get_parcel`](#get_parcel) | Get a parcel's facts in one call | `parcels:read` | | [`check_regulations`](#check_regulations) | Read the zoning and planning status | `parcels:read` | | [`check_constraints`](#check_constraints) | List environmental and legal constraints | `parcels:read` | | [`nearby`](#nearby) | Measure distances to infrastructure | `parcels:read` | | [`market_comparables`](#market_comparables) | Pull recent sales near a parcel | `market:read` | | [`compare_parcels`](#compare_parcels) | Put a shortlist side by side | `parcels:read` | | [`parcel_report`](#parcel_report) | Produce a shareable due-diligence report | `reports:create` | | [`list_layers`](#list_layers) | See what data exists and how fresh it is | `layers:read` | ## Search ### search\_parcels > Find land parcels in Poland that match a plain-language description, e.g. "vacant plots over 2 ha zoned for industry near Rzeszów within 5 km of a 110 kV substation". Use this whenever the user describes the land they want rather than naming a specific parcel. Returns a ranked shortlist, the structured interpretation of the request, and a coverage report listing criteria that could not be checked. Always tell the user about those. | Input | Type | Description | | --- | --- | --- | | `query` | string, required | The user's request, in English or Polish | | `region` | string | Place name or TERYT code; overrides any place in `query` | | `limit` | integer | 1–50, default 10 | ```json { "interpretation": { "region": { "teryt": "1863", "name": "Rzeszów", "unit_type": "powiat" }, "filters": [ "area ≥ 20,000 m²", "zoning category in [industrial, commercial_services]", "no building footprint", "≤ 5,000 m to a substation ≥ 110 kV" ], "assumptions": ["\"near Rzeszów\" includes the surrounding powiat (1816)"] }, "count": 12, "results": [ { "parcel_id": "186301_1.0213.1544/7", "area_sqm": 41210, "gmina": "Rzeszów", "score": 94, "why": ["2.4 km to GPZ Rzeszów-Załęże (110 kV)", "MPZP zone 3P/U", "mean slope 1.2 %"], "map_url": "https://app.zonio.tech/p/186301_1.0213.1544%2F7" } ], "coverage": [ { "criterion": "zoning", "status": "partial", "note": "4 of 6 gminas publish MPZP data" } ] } ``` See [Plain-language parcel search](/mcp/natural-language-search) for how queries are interpreted. ### find\_sites > Run one of Zonio's site-selection scenarios (solar farm, wind farm, battery storage, agri-PV, biogas, data centre, logistics, housing estate, retail park, brownfield, public land) over a region. Prefer this over search\_parcels when the user's goal matches a scenario, because scenarios encode the legal and technical criteria for that asset type. Call list\_scenarios first if unsure which parameters exist. | Input | Type | Description | | --- | --- | --- | | `scenario` | string, required | A scenario id, e.g. `bess_storage` | | `region` | string, required | Place name or TERYT code | | `params` | object | Override defaults, e.g. `{ "substation_km": 5 }` | | `weights` | object | Re-weight ranking, e.g. `{ "grid_capacity_mw": 80 }` | | `limit` | integer | 1–50, default 10 | ### list\_scenarios > List the available site-selection scenarios with their hard criteria, adjustable parameters (default, min, max, unit) and ranking signals. No inputs. Also available as the [`zonio://scenarios`](/mcp/resources-and-prompts#resources) resource. ## Parcels ### locate\_parcel > Identify a parcel from whatever the user gave you: a street address, a pair of coordinates, a full EGiB parcel id, or a parcel number plus a precinct or gmina name ("działka 100/1, obręb Podgórze, Kraków"). Returns the best match and up to 5 alternatives. | Input | Type | Description | | --- | --- | --- | | `address` | string | Free-text address | | `lat`, `lng` | number | WGS84 coordinates | | `parcel` | string | Full id or parcel number with precinct / gmina | ### resolve\_region > Resolve a place name to administrative units with TERYT codes. Several gminas share names, so check `parent` and ask the user when more than one candidate is plausible. | Input | Type | Description | | --- | --- | --- | | `name` | string, required | Diacritics optional: `"lodz"` finds `Łódź` | ### get\_parcel > Get the essential facts about one parcel in a single call: area, location, land-use classes, zoning summary, blocking constraints, terrain and nearest key infrastructure. Use the specialised tools only when the user wants more depth on one aspect. | Input | Type | Description | | --- | --- | --- | | `parcel_id` | string, required | EGiB id | Data: [cadastral parcels](https://zonio.tech/en/layers/cadastre/cadastral-parcels/), [soil classes](https://zonio.tech/en/layers/land-cover-land-use/land-use-soil-classes-egib/), [terrain slope](https://zonio.tech/en/layers/terrain-relief/terrain-slope-lidar/), plus the layers behind the tools below. ```json { "parcel_id": "121905_2.0007.412/2", "gmina": "Wieliczka", "area_sqm": 1840, "land_use_classes": ["RIVa"], "zoning": "MPZP 12MN: single-family housing, max height 10 m, max coverage 30 %", "blocking_constraints": [], "terrain": { "slope_mean_pct": 3.4, "elevation_m": 262 }, "nearest": { "transit_stop_m": 520, "school_m": 1180, "power_substation_m": 4100 }, "map_url": "https://app.zonio.tech/p/121905_2.0007.412%2F2" } ``` ### check\_regulations > Explain what planning law allows on a parcel: the local plan (MPZP) zone with its symbol, normalised category and parameters, the general plan (Plan Ogólny) zone and its status, the legacy Studium designation, and statutory pre-emption rights (KOWR, State Forests, gmina revitalisation). If no MPZP applies, say so and explain that a WZ decision would be needed. | Input | Type | Description | | --- | --- | --- | | `parcel_id` | string, required | EGiB id | Data: [local zoning plans (MPZP)](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/), [general plans](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/), [Studium](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/), [KOWR land](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/). ### check\_constraints > List every environmental and legal constraint on or near a parcel (Natura 2000, protected landscapes, national parks, flood-risk zones, heritage protection, mining areas, aviation obstacle surfaces, groundwater protection, wind-turbine setbacks), each with a severity (blocking / restrictive / informational) and the overlap or distance. | Input | Type | Description | | --- | --- | --- | | `parcel_id` | string, required | EGiB id | | `buffer_m` | integer | Look this far beyond the boundary, default 100, max 5000 | Data: [protected areas](https://zonio.tech/en/layers/environment/protected-areas-gdos/), [flood hazard](https://zonio.tech/en/layers/hazards/flood-hazard-isok/), [heritage](https://zonio.tech/en/layers/cultural-heritage/heritage-historic-sites/), [archaeological sites](https://zonio.tech/en/layers/cultural-heritage/archaeological-sites-nid/), [mining areas](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/), [landslides](https://zonio.tech/en/layers/hazards/landslides-sopo-pig-pib/), [height limits (OLS)](https://zonio.tech/en/layers/infrastructure/height-limits-ols/), [airspace](https://zonio.tech/en/layers/infrastructure/airspace-zones-pansa/), [wind setbacks](https://zonio.tech/en/layers/renewables/700-m-from-wind-turbines/), [forests](https://zonio.tech/en/layers/land-cover-land-use/forests-bdl/). ### nearby > Measure straight-line distance from a parcel to the nearest infrastructure of each requested kind: power substations (by voltage), MV lines, available grid capacity, public-transport stops, schools, water bodies, existing and planned roads and railways, EV chargers, data centres. | Input | Type | Description | | --- | --- | --- | | `parcel_id` | string, required | EGiB id | | `kinds` | string\[] | Defaults to all | Data: [HV/EHV substations](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/), [power lines](https://zonio.tech/en/layers/power-grid/power-lines-bdot10k/), [grid capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/), [transit stops](https://zonio.tech/en/layers/infrastructure/transit-stops/), [services](https://zonio.tech/en/layers/buildings-development/services-bdot10k/), [rivers](https://zonio.tech/en/layers/hydrography/rivers-canals-bdot10k/), [roads](https://zonio.tech/en/layers/infrastructure/roads-bdot10k/), [planned roads](https://zonio.tech/en/layers/planned-infrastructure/planned-roads/), [planned railways](https://zonio.tech/en/layers/planned-infrastructure/planned-railways/), [EV charging](https://zonio.tech/en/layers/power-grid/ev-charging-openchargemap/), [data centres](https://zonio.tech/en/layers/power-grid/data-centers-osm/). ### market\_comparables > Return recent notarised transactions from the national price register (RCN) near a parcel or in a region, with the median price per m². Use it to sanity-check asking prices or to estimate land value. | Input | Type | Description | | --- | --- | --- | | `parcel_id` or `region` | string | Where to look | | `property_type` | string | `land`, `building` or `apartment` | | `radius_m` | integer | Default 2000 | | `since` | date | Default: two years ago | Data: [transaction prices (RCN)](https://zonio.tech/en/layers/market/transaction-prices/). ### compare\_parcels > Compare 2–20 parcels side by side on area, zoning, constraints, terrain, infrastructure distances and, optionally, fit with a scenario. Returns a table-ready structure. | Input | Type | Description | | --- | --- | --- | | `parcel_ids` | string\[], required | 2–20 EGiB ids | | `scenario` | string | Evaluate each parcel against this scenario | ### parcel\_report > Generate a complete due-diligence report for one parcel and return a shareable link to a PDF. Only call this when the user asks for a report or a document to share. | Input | Type | Description | | --- | --- | --- | | `parcel_id` | string, required | EGiB id | | `scenario` | string | Add a scenario-fit section | | `language` | string | `en` or `pl`, default `en` | ## Data ### list\_layers > List every data layer Zonio holds with its category, source, coverage (national / partial) and last refresh date. Use it to answer "do you have data on X?" and to explain coverage gaps. No inputs. Also available as the [`zonio://layers`](/mcp/resources-and-prompts#resources) resource. Every layer it returns has a page in the [zonio.tech layer atlas](https://zonio.tech/en/layers/). ## Errors Tool errors are returned as results with `isError: true` and a message written for the model, e.g.: ```json { "isError": true, "content": [{ "type": "text", "text": "\"Ostrów\" matches 4 gminas (Ostrów Mazowiecka, Ostrów Wielkopolski, Ostrów in Podkarpackie, Ostrów in Opolskie). Ask the user which one, or call resolve_region." }] } ``` # Resources & prompts Besides tools, the Zonio MCP server exposes **resources** that clients can attach as context and **prompts** that users can pick from a menu. ## Resources | URI | Contents | | --- | --- | | `zonio://layers` | The layer catalogue: id, category, source, coverage, refresh date | | `zonio://scenarios` | The scenario catalogue with parameters and ranking signals | | `zonio://zoning-categories` | The normalised zoning taxonomy and the common MPZP symbols that map to each category | | `zonio://parcels/{parcel_id}` | A Markdown fact sheet for one parcel, ideal to attach to a conversation | | `zonio://regions/{teryt}` | A region profile: area, parcel count, MPZP coverage %, Plan Ogólny status, layer coverage | Parcel and region resources are **resource templates**: clients that support them (Claude Desktop, Claude Code) let users type `@zonio:parcels/…` to attach one. ```md [zonio://parcels/121905_2.0007.412%2F2] # Parcel 121905_2.0007.412/2, Wieliczka - **Area:** 1,840 m² · land class RIVa - **Zoning:** MPZP „Wieliczka – Krzyszkowice” (2019), zone **12MN**: single-family housing max height 10 m · max coverage 30 % · min biologically active 50 % - **Plan Ogólny:** adopted 2025, zone SJ (single-family housing) - **Constraints:** none blocking · mining area „Wieliczka” 340 m away (informational) - **Nearest:** bus stop 520 m · primary school 1.18 km · 110 kV substation 4.1 km - **Market:** 14 land sales within 2 km since 2024, median 412 PLN/m² ``` ## Prompts Prompts are guided workflows. In Claude Desktop they appear under the **+** menu; in Claude Code as `/mcp__zonio__`. ### site\_selection Arguments: `asset_type`, `region`, `min_area_ha` (optional), `notes` (optional). Walks the agent through picking the right scenario, running it, checking the top five candidates for regulations and constraints, and presenting a ranked shortlist with risks and the criteria that could not be verified. ### parcel\_due\_diligence Arguments: `parcel` (id, address or parcel number), `intended_use`. Locates the parcel, then covers zoning, constraints, terrain, access to utilities and transport, and market value, ending with a go / caution / no-go verdict and the questions to ask the seller or the gmina. ### compare\_shortlist Arguments: `parcel_ids`, `intended_use`. Builds a side-by-side comparison and recommends one parcel, with the trade-offs spelled out. # Plain-language parcel search The flagship of the Zonio platform is search in the user's own words. It is available as the [`search_parcels`](/mcp/tools#search_parcels) MCP tool and as [`POST /search/natural-language`](/reference) in the REST API. ``` "Flat plots over 2 ha near Rzeszów, zoned for industry, within 5 km of a 110 kV substation and outside Natura 2000" ``` ## How a query is answered :::steps ### Understand A language model trained on Polish planning vocabulary extracts the **region**, the **asset type**, and every **criterion**. It knows that *"zoned for industry"* means MPZP categories `P`, `PU` and `P/U`, that *"flat"* is a slope threshold, that *"near the A4"* is a distance to a specific road, and that *"bez Natury"* means outside Natura 2000. ### Ground Every named thing is resolved against the database, never guessed: places to TERYT codes (asking when *"Ostrów"* is ambiguous), roads and substations to features, zone words to the normalised zoning taxonomy. If the request matches a [scenario](/data/scenarios), the scenario's legal criteria are added: for a large PV farm on farmland, for example, the soil-class rules. ### Compile The interpretation becomes the same structured request you could send to [`POST /search/parcels`](/reference): hard filters, ranking signals and weights. No SQL is ever generated by the model; filters come from a fixed, tested catalogue. ### Search The query runs on PostGIS in EPSG:2180 with spatial indexes, walking candidates largest-first and stopping early, so a national search typically returns in under two seconds. ### Explain The response carries the interpretation, the assumptions that were made, a `why` list for each result, and the [coverage](/concepts#coverage) of every criterion. The agent can show the user exactly what was searched and offer to adjust it. ::: ## The interpretation is part of the answer ```json "interpretation": { "region": { "teryt": "1863", "name": "Rzeszów", "unit_type": "powiat" }, "scenario": null, "request": { "region": "1863", "filters": { "area_sqm": { "min": 20000 }, "zoning": { "categories": ["industrial", "commercial_services"] }, "slope_pct": { "max": 5 }, "near": [{ "layer": "power_substations", "within_m": 5000, "where": { "voltage_kv_gte": 110 } }], "exclude": [{ "layer": "env_layers", "kinds": ["natura2000"], "buffer_m": 0 }] } }, "assumptions": [ "\"near Rzeszów\" was read as powiat Rzeszów (1816) plus the city (1863).", "\"flat\" was read as mean slope ≤ 5 %." ] } ``` Your application can replay `interpretation.request` against `POST /search/parcels` to page, tweak a slider or save the search, deterministically and without another model call. ## What it understands | Users say | Zonio searches | | --- | --- | | *"buildable"*, *"pod zabudowę"* | Residential / service [MPZP zones](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/), or [Plan Ogólny zones](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/) that allow building | | *"flat"*, *"płaska"* | Mean [slope](https://zonio.tech/en/layers/terrain-relief/terrain-slope-lidar/) ≤ 5 % (tunable) | | *"empty"*, *"vacant"*, *"niezabudowana"* | No [BDOT10k building](https://zonio.tech/en/layers/buildings-development/buildings-bdot10k/) footprint | | *"near a substation"*, *"blisko GPZ"* | ≤ 10 km to a [substation](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/) ≥ 110 kV | | *"with grid capacity"* | DSO-published [available capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/) ≥ requested MW | | *"near public transport"* | ≤ 800 m to a [stop](https://zonio.tech/en/layers/infrastructure/transit-stops/) | | *"no protected nature"* | Outside [Natura 2000, national/landscape parks, reserves](https://zonio.tech/en/layers/environment/protected-areas-gdos/) | | *"not in a flood zone"* | Outside 1 % and 0.2 % [flood-risk areas](https://zonio.tech/en/layers/hazards/flood-hazard-isok/) | | *"cheap"* | Ranked by median [RCN price](https://zonio.tech/en/layers/market/transaction-prices/) per m² in the gmina | | *"state-owned"*, *"KOWR"* | Overlaps [KOWR agricultural property stock](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/) | | *"for a data centre"*, *"for BESS"*, … | The matching [scenario](/data/scenarios) | ## Writing good queries * **Name the place.** A voivodeship, powiat or gmina keeps results relevant; without one, the whole country is searched. * **Give numbers when you have them.** *"Over 2 ha"* beats *"large"*. * **Say what the land is for.** *"For a 5 MW PV farm"* adds the legal criteria that matter for that asset. * **Read the coverage.** A criterion marked `missing` was not checked in that region, so treat matches as unscreened on it. # Prompt cookbook Copy these into Claude or any agent connected to Zonio. Each one shows the tools the agent is expected to call. ## Energy ### Solar farm shortlist ```txt Find 10 parcels in Lubelskie suitable for a 5 MW ground-mounted PV farm: at least 10 ha, slope under 10 %, within 8 km of a 110 kV substation, outside Natura 2000 and flood zones. Prefer class IV–VI soil. Rank by grid distance and solar yield, and tell me which criteria you couldn't check. ``` Tools: `find_sites` (`solar_farm_site`) → `check_regulations` on the top results. ### Battery storage next to available capacity ```txt Where in Wielkopolskie can I connect a 20 MW BESS? I need 0.5 ha within 3 km of a GPZ that publishes at least 20 MW of available capacity. Show the substation name for each plot. ``` Tools: `find_sites` (`bess_storage`, `min_capacity_mw: 20`, `capacity_km: 3`). ### Wind setbacks ```txt Is parcel 2610052_2.0004.233 far enough from homes for a wind turbine under the current setback law? What else would block a turbine there: aviation, Natura 2000, airspace? ``` Tools: `check_constraints` (wind setback, aviation obstacle surfaces, airspace, environment). ## Real estate ### Housing estate ```txt I'm a developer looking for land for 40–60 single-family homes around Kraków. Plots of 1.5–4 ha, zoned MN, vacant, within 1 km of a school and 800 m of a bus stop. Exclude anything KOWR could pre-empt. ``` Tools: `find_sites` (`housing_estate_site`) → `market_comparables` for the top three. ### Logistics ```txt Find flat plots over 5 ha within 3 km of an A1 or A2 interchange in Łódzkie, zoned for production or storage. Compare the best five. ``` Tools: `search_parcels` → `compare_parcels`. ### Address to answer ```txt What can I build at ul. Wielicka 250, Kraków? Is there a local plan, and what does it allow? ``` Tools: `locate_parcel` → `check_regulations`. ## Due diligence ### One parcel, full check ```txt Run a full due diligence on działka 412/2, obręb Krzyszkowice, Wieliczka for a block of 12 apartments. Give me a go / caution / no-go and a PDF I can send to my bank. ``` Tools: `locate_parcel` → `get_parcel` → `check_regulations` → `check_constraints` → `market_comparables` → `parcel_report`. ### Price sanity check ```txt A seller is asking 650 PLN/m² for parcel 146501_8.0204.17/4. How does that compare with land sales within 2 km over the last two years? ``` Tools: `market_comparables`. ## Tips for agent builders * Put *"Always report criteria with coverage status `missing` to the user"* in your system prompt. Zonio's tool descriptions already say so; repetition helps on long tasks. * Ask the model to include `map_url` links; users want to see the plot. * For batch work (hundreds of parcels), call the REST API directly; MCP shines when a person is in the loop. # Build an agent with the Claude API The Claude API can connect to remote MCP servers for you: pass the Zonio server in `mcp_servers`, reference it from `tools`, and Claude calls Zonio tools during the request, with no MCP client code on your side. ## Minimal example :::code-group ```python [Python] import os import anthropic client = anthropic.Anthropic() response = client.beta.messages.create( model="claude-opus-5-5", max_tokens=16000, betas=["mcp-client-2025-11-20"], mcp_servers=[{ "type": "url", "url": "https://mcp.zonio.tech/mcp", "name": "zonio", "authorization_token": os.environ["ZONIO_API_KEY"], }], tools=[{"type": "mcp_toolset", "mcp_server_name": "zonio"}], messages=[{ "role": "user", "content": "Find vacant plots over 1 ha zoned for housing in Wieliczka within 800 m of a bus stop.", }], ) for block in response.content: if block.type == "text": print(block.text) ``` ```ts [TypeScript] import Anthropic from '@anthropic-ai/sdk' const client = new Anthropic() const response = await client.beta.messages.create({ model: 'claude-opus-5-5', max_tokens: 16000, betas: ['mcp-client-2025-11-20'], mcp_servers: [ { type: 'url', url: 'https://mcp.zonio.tech/mcp', name: 'zonio', authorization_token: process.env.ZONIO_API_KEY, }, ], tools: [{ type: 'mcp_toolset', mcp_server_name: 'zonio' }], messages: [ { role: 'user', content: 'Find vacant plots over 1 ha zoned for housing in Wieliczka within 800 m of a bus stop.', }, ], }) for (const block of response.content) { if (block.type === 'text') console.log(block.text) } ``` ```bash [cURL] curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: mcp-client-2025-11-20" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-5-5", "max_tokens": 16000, "mcp_servers": [{ "type": "url", "url": "https://mcp.zonio.tech/mcp", "name": "zonio", "authorization_token": "'"$ZONIO_API_KEY"'" }], "tools": [{ "type": "mcp_toolset", "mcp_server_name": "zonio" }], "messages": [{ "role": "user", "content": "What is the zoning of parcel 126101_1.0018.AR_1.100/1?" }] }' ``` ::: ## Allow only some tools Use the toolset's `default_config` and `configs` to expose an allowlist. For example, a public chatbot that may search and read but never create reports: ```python tools=[{ "type": "mcp_toolset", "mcp_server_name": "zonio", "default_config": {"enabled": False}, "configs": { "search_parcels": {"enabled": True}, "locate_parcel": {"enabled": True}, "get_parcel": {"enabled": True}, "check_regulations": {"enabled": True}, "check_constraints": {"enabled": True}, }, }] ``` A key with only `parcels:read` and `search:read` scopes enforces the same limit server-side. ## A system prompt that works ```txt You are a land-acquisition assistant for Poland, backed by Zonio's national parcel data. - Use Zonio tools for every factual claim about a parcel, zoning, constraint or distance. - When a tool returns coverage entries with status "missing", tell the user which criteria could not be checked in that region. Never describe an unchecked parcel as clean. - Quote parcel ids exactly and include the map_url for every parcel you recommend. - Give areas in m² under 1 ha and in ha above; distances in m under 1 km and km above. - Reply in the user's language (Polish or English). ``` ## Production checklist * **Use one Zonio key per deployment** with only the scopes you need, and rotate it like any secret. * **Stream long answers.** Site-selection runs call several tools; use `client.beta.messages.stream(...)` so users see progress. * **Log `Zonio-Request-Id`.** MCP tool results include it in `_meta` so you can trace any answer back to the data that produced it. * **Cache the system prompt and tool list** with prompt caching; both are stable across requests. # REST API overview The Zonio Geo API is a JSON-over-HTTPS API described by an [OpenAPI 3.1 document](/reference). Use it when your code knows what it wants: batch scoring, pipelines, dashboards, map backends. ``` https://api.zonio.tech/v1 ``` ## Endpoint groups | Group | Endpoints | What for | | --- | --- | --- | | **Search** | `POST /search/natural-language`, `POST /search/parcels` | Find parcels from a sentence or from structured filters | | **Parcels** | `GET /parcels/{id}`, `/at`, `/autocomplete`, `/batch`, `/{id}/regulations`, `/constraints`, `/terrain`, `/proximity`, `/report` | Everything about one parcel or many | | **Geocoding** | `GET /geocode`, `GET /regions` | Addresses → parcels, names → TERYT codes | | **Market** | `GET /market/transactions` | Notarised sales and median prices | | **Layers** | `GET /layers`, `GET /layers/{id}/features` | The raw data, by bounding box | ## Conventions * **Authentication:** `Authorization: Bearer ` on every request. See [Authentication](/authentication). * **Geometry:** GeoJSON in WGS84 (EPSG:4326), RFC 7946, in `[lng, lat]` order. Responses that are a single feature or a feature collection use `application/geo+json`. * **Units:** metres, square metres, percent, PLN. Field names carry the unit: `area_sqm`, `distance_m`, `slope_mean_pct`, `price_per_sqm_pln`. * **Ids:** EGiB parcel ids as published by GUGiK; URL-encode `/` in path parameters. See [Parcels and parcel ids](/concepts#parcels-and-parcel-ids). * **Regions:** TERYT codes as strings (`"02"`, `"1261"`, `"1261011"`), never integers: leading zeros matter. * **Pagination:** cursor-based. Pass `next_cursor` back as `cursor`; `null` means you're done. * **Embedding:** `GET /parcels/{id}?include=regulations,constraints` saves round-trips. * **Nulls are meaningful:** `regulations.mpzp: null` means *no plan applies*; a missing layer is reported in `coverage`, never as `null`. ## Versioning The major version is in the path (`/v1`). Within a major version we only add: new endpoints, new optional parameters, new response fields and new enum values. Write clients that ignore unknown fields. Breaking changes ship as `/v2` with at least 12 months of overlap. ## OpenAPI Download the spec to generate clients or import into Postman, Insomnia or Bruno: ```bash curl -O https://docs.zonio.tech/openapi.yaml ``` # Errors ## Error format Errors use standard HTTP status codes and a consistent body: ```json { "error": { "code": "invalid_teryt", "message": "region must be a 2, 4 or 7 digit TERYT code.", "request_id": "req_01J9ZM4D1Q6W" } } ``` | Status | Code | Meaning | | --- | --- | --- | | 400 | `invalid_point`, `invalid_teryt`, `invalid_bbox`, `invalid_limit`, `unknown_scenario` | The request is malformed. The message says which field. | | 401 | `invalid_api_key` | Missing, revoked or malformed key. | | 403 | `insufficient_scope` | The key lacks the scope for this endpoint. | | 404 | `parcel_not_found`, `layer_not_found` | No such object. | | 422 | `ambiguous_region`, `uninterpretable_query` | Natural-language search could not resolve the request. `candidates` lists options. | | 429 | `rate_limited` | Too many requests. Retry after the `Retry-After` header. | | 503 | `search_timeout`, `upstream_unavailable` | A search was too broad to finish, or an upstream service (e.g. the national geocoder) is down. Safe to retry. | A `search_timeout` is returned with a hint instead of a gateway error: ```json { "error": { "code": "search_timeout", "message": "Criteria too strict to finish nationally. Narrow the region or relax min area." } } ``` ## Retrying * Retry `429` after the `Retry-After` header. * Retry `503` with exponential backoff (1 s, 2 s, 4 s, max 3 attempts). * Never retry `4xx` other than `429` without changing the request. The official [SDKs](/api/sdks) do all of this for you. # Zonio Geo API Version: `1.0.0-preview` The Zonio Geo API exposes the national land dataset that powers Zonio: every cadastral parcel in Poland, the local and general plans that zone it, the environmental and regulatory constraints that limit it, and the infrastructure around it. All requests require an API key sent as `Authorization: Bearer `. Geometry is returned as GeoJSON in WGS84 (EPSG:4326); every distance and area is computed in metres in EPSG:2180 (PUWG 1992). ## Servers - `https://api.zonio.tech/v1`: Production - `https://sandbox.api.zonio.tech/v1`: Sandbox (test keys, Kraków + Wrocław only) ## Endpoints ### Search Find parcels with structured filters or a plain-language query. - [`POST /search/natural-language`](/reference/search#searchnaturallanguage): Search parcels in plain language - [`POST /search/parcels`](/reference/search#searchparcels): Search parcels with structured filters ### Parcels Look up a parcel and everything known about it. - [`GET /parcels/{parcel_id}`](/reference/parcels#getparcel): Get a parcel - [`GET /parcels/at`](/reference/parcels#getparcelatpoint): Get the parcel at a point - [`GET /parcels/autocomplete`](/reference/parcels#autocompleteparcels): Autocomplete parcel ids - [`POST /parcels/batch`](/reference/parcels#getparcelsbatch): Get many parcels - [`GET /parcels/{parcel_id}/regulations`](/reference/parcels#getparcelregulations): Get zoning & planning status - [`GET /parcels/{parcel_id}/constraints`](/reference/parcels#getparcelconstraints): Get environmental & legal constraints - [`GET /parcels/{parcel_id}/terrain`](/reference/parcels#getparcelterrain): Get terrain statistics - [`GET /parcels/{parcel_id}/proximity`](/reference/parcels#getparcelproximity): Get distances to nearby infrastructure - [`GET /parcels/{parcel_id}/report`](/reference/parcels#getparcelreport): Get a due-diligence report ### Geocoding Turn addresses and place names into parcels and TERYT codes. - [`GET /geocode`](/reference/geocoding#geocode): Geocode an address - [`GET /regions`](/reference/geocoding#searchregions): Search administrative units ### Market Real-estate transaction comparables and price medians. - [`GET /market/transactions`](/reference/market#listtransactions): List transaction comparables ### Layers The raw data layers behind every answer. - [`GET /layers`](/reference/layers#listlayers): List data layers - [`GET /layers/{layer_id}/features`](/reference/layers#getlayerfeatures): Query a layer # SDKs :::note The SDKs are in preview alongside the API. They are generated from the [OpenAPI spec](/reference) and add retries, pagination and typed GeoJSON. ::: ## TypeScript ```bash npm install @zonio/sdk ``` ```ts import { Zonio } from '@zonio/sdk' const zonio = new Zonio({ apiKey: process.env.ZONIO_API_KEY }) const { results, coverage } = await zonio.scenarios.search('bess_storage', { region: '30', params: { min_capacity_mw: 20, substation_km: 5 }, limit: 25, }) for (const r of results) console.log(r.parcel_id, r.area_sqm, r.score) const missing = coverage.filter((c) => c.status === 'missing') if (missing.length) console.warn('Not checked here:', missing.map((c) => c.criterion)) ``` Paginate any list with `for await`: ```ts for await (const tx of zonio.market.transactions({ region: '1261011', since: '2025-01-01' })) { console.log(tx.date, tx.price_per_sqm_pln) } ``` ## Python ```bash pip install zonio ``` ```python from zonio import Zonio zonio = Zonio() # reads ZONIO_API_KEY result = zonio.search.natural_language( query="flat plots over 2 ha zoned for industry within 5 km of a 110 kV substation", region="1863", ) print(result.interpretation.assumptions) for parcel in result.results: regs = zonio.parcels.regulations(parcel.parcel_id) print(parcel.parcel_id, regs.mpzp.symbol if regs.mpzp else "no MPZP") ``` ### GeoPandas ```python gdf = zonio.layers.features("power_substations", bbox=(21.8, 49.9, 22.2, 50.1)).to_geodataframe() gdf.to_crs(2180).plot() ``` ## Other languages Generate a client from the spec with [openapi-generator](https://openapi-generator.tech) or your favourite tool: ```bash npx @openapitools/openapi-generator-cli generate \ -i https://docs.zonio.tech/openapi.yaml -g go -o ./zonio-go ``` # Find a parcel Users rarely know an EGiB id. The API meets them where they are. ## From an address ```bash curl "https://api.zonio.tech/v1/geocode?q=Kraków,%20Wielicka%20250" \ -H "Authorization: Bearer $ZONIO_API_KEY" ``` ```json [ { "label": "Wielicka 250, Kraków", "lng": 19.9922, "lat": 50.0179, "parcel_id": "126103_1.0053.214/9" } ] ``` Addresses are matched against PRG, the national address register. Each hit is resolved to the parcel at that point. ## From a click on a map ```bash curl "https://api.zonio.tech/v1/parcels/at?lng=19.9922&lat=50.0179" \ -H "Authorization: Bearer $ZONIO_API_KEY" ``` If the point falls on a road or a gap between parcels, the nearest parcel within 200 m is returned. ## From a parcel number Sellers and notaries quote *"działka 214/9, obręb 53, Podgórze"*. Autocomplete matches any fragment of the id: ```bash curl "https://api.zonio.tech/v1/parcels/autocomplete?q=0053.214" \ -H "Authorization: Bearer $ZONIO_API_KEY" ``` ## From a description When the user describes the land rather than a specific plot, use [plain-language search](/mcp/natural-language-search): ```bash curl https://api.zonio.tech/v1/search/natural-language \ -H "Authorization: Bearer $ZONIO_API_KEY" -H "Content-Type: application/json" \ -d '{"query": "the big empty plot next to the Wieliczka salt mine car park"}' ``` ## From a list Importing a spreadsheet of ids from a land register extract? Fetch up to 500 at once: ```bash curl https://api.zonio.tech/v1/parcels/batch \ -H "Authorization: Bearer $ZONIO_API_KEY" -H "Content-Type: application/json" \ -d '{"parcel_ids": ["126103_1.0053.214/9", "126103_1.0053.215/1"], "include": ["regulations"]}' ``` Ids that don't exist come back in `missing`, so you can flag typos to the user. # Site selection with scenarios A scenario packages everything an expert checks for one asset type (zoning, constraints, grid, terrain, transport) into one call. This guide finds battery-storage sites in Wielkopolskie. :::steps ### Pick the criteria Start from the matching entry in the [scenario catalogue](/data/scenarios). For battery storage (`bess_storage`) that is: no blocking zone, available grid capacity near a substation, no environmental risk nearby, and a gentle slope, ranked mostly by available capacity. ### Run it on a region Find the region's TERYT code with `GET /regions?q=wielkopolskie` (→ `30`), then express the criteria as filters, tightened to a 20 MW project: ```bash curl https://api.zonio.tech/v1/search/parcels \ -H "Authorization: Bearer $ZONIO_API_KEY" -H "Content-Type: application/json" \ -d '{ "region": "30", "filters": { "area_sqm": { "min": 2000 }, "slope_pct": { "max": 15 }, "near": [ { "layer": "grid_capacity", "within_m": 3000, "where": { "available_mw_gte": 20 } }, { "layer": "power_substations", "within_m": 5000, "where": { "voltage_kv_gte": 110 } } ], "exclude": [{ "layer": "env_layers", "kinds": ["natura2000", "flood_risk_1pct"], "buffer_m": 100 }] }, "rank_by": [ { "signal": "grid_capacity_mw", "weight": 70 }, { "signal": "grid_distance_km", "weight": 20 }, { "signal": "area", "weight": 10 } ], "limit": 25 }' ``` Agents get the same result in one step: the [`find_sites`](/mcp/tools#find_sites) MCP tool applies a scenario by name, and [plain-language search](/mcp/natural-language-search) picks one from a sentence like *"sites for a 20 MW BESS in Wielkopolskie"*. ### Read the ranking ```json { "count": 25, "blocked": false, "coverage": [ { "criterion": "grid_capacity_available", "mode": "requires", "status": "available" }, { "criterion": "no_env_risk_nearby", "mode": "screens", "status": "available" }, { "criterion": "slope_within_tolerance", "mode": "screens", "status": "partial" } ], "results": [ { "parcel_id": "3064011_1.0021.88/4", "area_sqm": 6120, "gmina": "Poznań", "score": 97, "components": [ { "signal": "grid_capacity_mw", "value": 46, "contribution": 70 }, { "signal": "grid_distance_km", "value": 0.8, "contribution": 18.4 }, { "signal": "area", "value": 6120, "contribution": 8.6 } ] } ] } ``` Scores are **relative** (0–100 within this result set) and `components` shows exactly how each was earned, so you can explain the ranking to a user or an investment committee. ### Check the coverage `slope_within_tolerance` is `partial` here: terrain statistics are not yet available for every parcel in the region, so some results were not screened on slope. Show that to your user. If a `requires` criterion is `missing`, the search returns `blocked: true` and no results, rather than an empty list that looks like *"no sites exist"*. ### Drill into the shortlist Take the top results into [due diligence](/guides/due-diligence) or ask your agent to `compare_parcels`. ::: ## Build your own screen `POST /search/parcels` is not limited to the catalogue: any combination of area, zoning, slope, vacancy, `near` and `exclude` clauses over any [layer](/data/layers), plus your own ranking weights. See the [API reference](/reference). # Parcel due diligence Before an offer, every buyer asks the same questions. Each maps to one endpoint, or to a single `include`. | Question | Endpoint | | --- | --- | | What may I build? | `GET /parcels/{id}/regulations` | | What stops me? | `GET /parcels/{id}/constraints` | | Is it buildable physically? | `GET /parcels/{id}/terrain` | | Can I connect it? | `GET /parcels/{id}/proximity` | | What is it worth? | `GET /market/transactions?parcel_id=…` | | All of the above, as a document | `GET /parcels/{id}/report` | ## One call ```bash curl "https://api.zonio.tech/v1/parcels/121905_2.0007.412%2F2?include=regulations,constraints,terrain,proximity" \ -H "Authorization: Bearer $ZONIO_API_KEY" ``` ## Regulations From [local zoning plans](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/), [general plans](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/) ([what the reform changes](https://zonio.tech/en/articles/general-plan-what-changes/)) and the [Studium](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/). ```json { "mpzp": { "plan_name": "MPZP „Wieliczka – Krzyszkowice”", "adopted_on": "2019-04-24", "symbol": "12MN", "category": "residential_single_family", "label": "Single-family residential", "max_height_m": 10, "max_building_coverage_pct": 30, "min_biologically_active_pct": 50, "document_url": "https://bip.wieliczka.eu/…/uchwala.pdf" }, "plan_ogolny": { "status": "adopted", "zone_symbol": "SJ", "zone_label": "Single-family housing zone" }, "studium": null, "pre_emption": [] } ``` `pre_emption` lists rights that can stop or delay a sale: KOWR's right on [agricultural land](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/), the State Forests' right next to forests, and gmina rights in revitalisation areas. ## Constraints From [protected areas](https://zonio.tech/en/layers/environment/protected-areas-gdos/), [flood hazard](https://zonio.tech/en/layers/hazards/flood-hazard-isok/), [mining areas](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/), [landslides](https://zonio.tech/en/layers/hazards/landslides-sopo-pig-pib/), [heritage](https://zonio.tech/en/layers/cultural-heritage/heritage-historic-sites/), [height limits](https://zonio.tech/en/layers/infrastructure/height-limits-ols/) and more. ```json { "blocking": false, "items": [ { "layer": "mining", "kind": "mining_area", "name": "Wieliczka", "distance_m": 340, "severity": "informational" }, { "layer": "groundwater", "kind": "gzwp_protection", "name": "GZWP 451", "overlap_pct": 100, "severity": "restrictive" } ] } ``` ## Report The same report is available to people, without code, in the [Zonio app](https://zonio.tech/en/features/) ([pricing](https://zonio.tech/en/pricing/)). Ask for a PDF and you get a branded, shareable document with maps, all of the above, comparable sales, and (if you pass `scenario`) a pass/fail checklist against that asset type: ```bash curl "https://api.zonio.tech/v1/parcels/121905_2.0007.412%2F2/report?scenario=housing_estate_site" \ -H "Authorization: Bearer $ZONIO_API_KEY" \ -H "Accept: application/pdf" -o parcel-report.pdf ``` :::warning Reports are decision support, not legal opinions. Zoning data is only as current as the gmina's publication; every block carries the source and the date it was fetched so you can verify against the original act. ::: # Maps & vector tiles Every polygon and line layer is published as a [PMTiles](https://docs.protomaps.com/pmtiles/) archive: a single file of vector tiles served over HTTP range requests. No tile server to run. ## Tile URLs `GET /layers` returns a `tiles_url` for every tiled layer. URLs are signed for your key and valid for 24 hours: ```json { "id": "mpzp_zones", "name": "Local zoning plan (MPZP) zones", "category": "planning", "coverage": "partial", "tiles_url": "https://tiles.zonio.tech/v1/mpzp_zones.pmtiles?sig=…" } ``` ## MapLibre ```ts import maplibregl from 'maplibre-gl' import { Protocol } from 'pmtiles' maplibregl.addProtocol('pmtiles', new Protocol().tile) const { tiles_url } = await fetch('/api/zonio/layers/mpzp_zones').then((r) => r.json()) // via your backend const map = new maplibregl.Map({ container: 'map', style: 'https://tiles.openfreemap.org/styles/positron', center: [19.94, 50.06], zoom: 13, }) map.on('load', () => { map.addSource('mpzp', { type: 'vector', url: `pmtiles://${tiles_url}` }) map.addLayer({ id: 'mpzp-fill', type: 'fill', source: 'mpzp', 'source-layer': 'mpzp_zones', paint: { 'fill-color': ['match', ['get', 'category'], 'residential_single_family', '#f6c85f', 'residential_multi_family', '#e8833a', 'industrial', '#9b59b6', 'green', '#6fbf73', '#cccccc'], 'fill-opacity': 0.5, }, }) }) ``` ## Tiled layers [Parcels](https://zonio.tech/en/layers/cadastre/cadastral-parcels/), [MPZP zones](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/), [Plan Ogólny zones](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/), [Studium zones](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/), [environmental layers](https://zonio.tech/en/layers/environment/protected-areas-gdos/), [buildings](https://zonio.tech/en/layers/buildings-development/buildings-bdot10k/), [roads](https://zonio.tech/en/layers/infrastructure/roads-bdot10k/), [transit stops](https://zonio.tech/en/layers/infrastructure/transit-stops/), [contours](https://zonio.tech/en/layers/terrain-relief/contour-lines-lidar/), [grid capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/), [air-quality stations](https://zonio.tech/en/layers/environment/air-quality-gios/), [aviation obstacle surfaces](https://zonio.tech/en/layers/infrastructure/height-limits-ols/), [airspace](https://zonio.tech/en/layers/infrastructure/airspace-zones-pansa/), [KOWR land](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/), [mining areas](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/) and [forest stands](https://zonio.tech/en/layers/land-cover-land-use/forests-bdl/). Orthophoto and [hillshade](https://zonio.tech/en/layers/terrain-relief/hillshade-nmt/) are proxied from GUGiK WMS. Each link opens the layer's page on zonio.tech with a preview of how it looks on the map. See [Layers](/data/layers). ## Link to Zonio Want to see the styling before you build? Every layer is live in the [Zonio app](https://zonio.tech/en/features/); the [layer atlas](https://zonio.tech/en/layers/) has a screenshot of each one. For the legal side of maps used in design work, see [map for project purposes](https://zonio.tech/en/articles/map-for-project-purposes/). Every parcel has a shareable map page: `https://app.zonio.tech/p/`. MCP tool results include it as `map_url`. # Data catalogue Zonio runs a dedicated geospatial pipeline that fetches Polish public data from dozens of government and open sources, normalises it into one PostGIS database, and keeps it fresh. The API and the MCP server read from that database directly. ## Explore the data on zonio.tech The [zonio.tech layer atlas](https://zonio.tech/en/layers/) is the reference for what Zonio holds: every layer has its own page with a description, the publisher, a link to the original source and a live map preview. Use it to check what a layer contains before you query it, and the [national map](https://zonio.tech/en/map/) to see coverage gmina by gmina. ## For developers These pages cover what the atlas does not: how each layer is exposed through the API, and how to reason about gaps and freshness in your code. # Layers Each layer is produced by one pipeline topic and can be queried through `GET /layers/{id}/features`, used in search filters, and, where marked, rendered as vector tiles. `GET /layers` returns this list live, with feature counts and refresh dates. The last column links to each layer's page on [zonio.tech](https://zonio.tech/en/layers/), with a description, the publisher and a live map preview. **Served via:** `api`: queryable through the REST API and MCP · `tiles`: also a PMTiles archive · `wms`: proxied raster. ## Cadastre & administration On zonio.tech: [Cadastre](https://zonio.tech/en/layers/cadastre/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `parcels` | Cadastral parcels with geometry, area and TERYT | GUGiK EGiB | api, tiles | [Parcels](https://zonio.tech/en/layers/cadastre/cadastral-parcels/) · [Soil classes](https://zonio.tech/en/layers/land-cover-land-use/land-use-soil-classes-egib/) | | `cadastral_units` | Cadastral units (~3.2 k) and precincts (~54 k) | GUGiK PRG | api | [Units](https://zonio.tech/en/layers/cadastre/cadastral-units-jednostki/) · [Districts](https://zonio.tech/en/layers/cadastre/cadastral-districts-obreby/) | | `admin_bounds` | Voivodeships, powiats, gminas | GUGiK PRG | api | [Gminas](https://zonio.tech/en/layers/cadastre/municipality-boundaries/) · [Powiats](https://zonio.tech/en/layers/cadastre/county-boundaries/) · [Voivodeships](https://zonio.tech/en/layers/cadastre/voivodeship-boundaries/) | ## Planning On zonio.tech: [Spatial planning](https://zonio.tech/en/layers/spatial-planning/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `mpzp_plans` | Local plan boundaries, adoption dates, documents | National INSPIRE MPZP service | api | [MPZP](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/) | | `mpzp_zones` | Local plan zones with raw and normalised symbols | Gmina WFS / ArcGIS services | api, tiles | [MPZP](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/) | | `plan_ogolny_zones` | General plan zones and status | Gmina services | api, tiles | [General plan](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/) | | `studium_zones` | Legacy directional study designations | Gmina services | api, tiles | [Studium](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/) | | `gunb_permits` | Building permits, joined to parcels | GUNB | api | [Building permits](https://zonio.tech/en/layers/buildings-development/building-permits-gunb/) | | `psi` | Polish Investment Zone operator jurisdictions | PAIH / zone operators | api | [Investment Zone](https://zonio.tech/en/layers/spatial-planning/polish-investment-zone/) | ## Environment On zonio.tech: [Environment](https://zonio.tech/en/layers/environment/) · [Land cover & land use](https://zonio.tech/en/layers/land-cover-land-use/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `env_layers` | Natura 2000, national and landscape parks, reserves, flood risk, wetlands | GDOŚ, Wody Polskie | api, tiles | [Protected areas](https://zonio.tech/en/layers/environment/protected-areas-gdos/) · [Flood hazard](https://zonio.tech/en/layers/hazards/flood-hazard-isok/) | | `groundwater` | Major groundwater reservoirs (GZWP) and protection areas | PIG-PIB | api | [Category](https://zonio.tech/en/layers/environment/) | | `forest_stands` | Managed-forest stands | Forest Data Bank (BDL) | api, tiles | [Forests](https://zonio.tech/en/layers/land-cover-land-use/forests-bdl/) | | `land_cover` | Arable land, orchards, grassland, forest | BDOT10k | api | [Land cover](https://zonio.tech/en/layers/land-cover-land-use/land-cover-bdot10k/) | | `air_quality` | Air-quality index per station | GIOŚ | api, tiles | [Air quality](https://zonio.tech/en/layers/environment/air-quality-gios/) | ## Energy & grid On zonio.tech: [Power grid](https://zonio.tech/en/layers/power-grid/) · [Renewables](https://zonio.tech/en/layers/renewables/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `power_substations` | HV and MV substations with voltage | BDOT10k, OSM | api | [PSE](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/) · [OSM](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-osm/) · [BDOT10k](https://zonio.tech/en/layers/power-grid/transformer-stations-bdot10k/) | | `power_lines` | 110 / 220 / 400 kV lines and MV lines | BDOT10k, OSM | api | [PSE](https://zonio.tech/en/layers/power-grid/transmission-lines-pse/) · [OSM](https://zonio.tech/en/layers/power-grid/hv-ehv-power-lines-osm/) · [BDOT10k](https://zonio.tech/en/layers/power-grid/power-lines-bdot10k/) | | `grid_capacity` | Available connection capacity per substation | DSO disclosures | api, tiles | [Grid capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/) | | `solar_irradiance` | Annual PV yield grid (kWh/kWp) | EU JRC PVGIS | api | [Solar irradiance](https://zonio.tech/en/layers/renewables/solar-irradiance-pvgis/) | | `oze_installations` | Existing solar, wind, biogas and hydro installations | OSM | api | [Wind](https://zonio.tech/en/layers/renewables/wind-turbines-osm/) · [Solar](https://zonio.tech/en/layers/renewables/solar-pv-plants-osm/) · [Hydro](https://zonio.tech/en/layers/renewables/hydropower-plants-osm/) · [Biogas](https://zonio.tech/en/layers/renewables/biomass-biogas-osm/) | | `wind_exclusion` | Statutory setback zones around buildings | Derived from buildings | api | [700 m buffer](https://zonio.tech/en/layers/renewables/700-m-from-wind-turbines/) | | `data_centers` | Existing data centres | OSM | api | [Data centres](https://zonio.tech/en/layers/power-grid/data-centers-osm/) | | `ev_chargers` | EV charging stations | OpenChargeMap | api | [EV charging](https://zonio.tech/en/layers/power-grid/ev-charging-openchargemap/) | ## Transport & infrastructure On zonio.tech: [Infrastructure](https://zonio.tech/en/layers/infrastructure/) · [Planned infrastructure](https://zonio.tech/en/layers/planned-infrastructure/) · [Hydrography](https://zonio.tech/en/layers/hydrography/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `roads` | Classified road segments | BDOT10k | api, tiles | [Roads](https://zonio.tech/en/layers/infrastructure/roads-bdot10k/) | | `transport_stops` | Bus, tram and rail stops | GTFS feeds, OSM | api, tiles | [Transit stops](https://zonio.tech/en/layers/infrastructure/transit-stops/) | | `planned_roads`, `planned_railways` | Roads and railways planned or under construction | OSM, GDDKiA | api | [Roads](https://zonio.tech/en/layers/planned-infrastructure/planned-roads/) · [Railways](https://zonio.tech/en/layers/planned-infrastructure/planned-railways/) | | `cpk` | CPK airport-and-rail programme zones and corridors | CPK | api | [Area](https://zonio.tech/en/layers/planned-infrastructure/port-polska-surrounding-area/) · [Corridors](https://zonio.tech/en/layers/planned-infrastructure/port-polska-corridors/) | | `waterways` | Rivers, canals and water bodies | BDOT10k | api | [Rivers](https://zonio.tech/en/layers/hydrography/rivers-canals-bdot10k/) · [Lakes](https://zonio.tech/en/layers/hydrography/lakes-reservoirs-bdot10k/) | | `hydrants` | Fire hydrants and water tanks | OSM | api | [Fire hydrants](https://zonio.tech/en/layers/infrastructure/fire-hydrants/) | | `industrial_areas` | Industrial and storage land, named industrial parks | BDOT10k | api | [Category](https://zonio.tech/en/layers/buildings-development/) | ## Buildings & services On zonio.tech: [Buildings & development](https://zonio.tech/en/layers/buildings-development/) · [Cultural heritage](https://zonio.tech/en/layers/cultural-heritage/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `buildings` | Building footprints with function and floors | BDOT10k | api, tiles | [Buildings](https://zonio.tech/en/layers/buildings-development/buildings-bdot10k/) | | `services_poi` | Schools, clinics, shops and other services | Derived from BDOT10k | api | [Services](https://zonio.tech/en/layers/buildings-development/services-bdot10k/) | | `heritage_sites` | Registered monuments | NID, OSM | api | [Heritage](https://zonio.tech/en/layers/cultural-heritage/heritage-historic-sites/) · [Archaeology](https://zonio.tech/en/layers/cultural-heritage/archaeological-sites-nid/) | ## Terrain On zonio.tech: [Terrain relief](https://zonio.tech/en/layers/terrain-relief/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `terrain_stats` | Elevation and slope statistics per parcel | GUGiK LIDAR DTM | api | [Slope](https://zonio.tech/en/layers/terrain-relief/terrain-slope-lidar/) | | `contours` | 5 m contour lines | GUGiK LIDAR DTM | tiles | [Contours](https://zonio.tech/en/layers/terrain-relief/contour-lines-lidar/) | | `terrain_hillshade` | Shaded relief | GUGiK WMS | wms | [Hillshade](https://zonio.tech/en/layers/terrain-relief/hillshade-nmt/) | | `ortho` | Orthophotomap | GUGiK WMS | wms | n/a | ## Risk & airspace On zonio.tech: [Hazards](https://zonio.tech/en/layers/hazards/) · [Infrastructure](https://zonio.tech/en/layers/infrastructure/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `mining_areas` | Mining areas and terrains | PIG-PIB MIDAS | api, tiles | [Mining](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/) · [Landslides](https://zonio.tech/en/layers/hazards/landslides-sopo-pig-pib/) | | `aviation_obstacles` | Obstacle limitation surfaces for 66 aerodromes and the national obstacle register | ULC | api, tiles | [Height limits](https://zonio.tech/en/layers/infrastructure/height-limits-ols/) · [Obstacles](https://zonio.tech/en/layers/infrastructure/aviation-obstacles/) | | `airspace` | Controlled, restricted and temporary airspace | PAŻP | api, tiles | [Airspace](https://zonio.tech/en/layers/infrastructure/airspace-zones-pansa/) | ## Market & society On zonio.tech: [Market](https://zonio.tech/en/layers/market/) | Layer | Contents | Source | Served via | On zonio.tech | | --- | --- | --- | --- | --- | | `transactions` | Notarised real-estate transactions | GUGiK RCN | api | [Transaction prices](https://zonio.tech/en/layers/market/transaction-prices/) | | `lokale` | Apartment sales with floor, rooms and market | GUGiK RCN | api | [Transaction prices](https://zonio.tech/en/layers/market/transaction-prices/) | | `prices` | Residential price medians per city | Derived from RCN | api | [Category](https://zonio.tech/en/layers/market/) | | `offer_prices` | Developer primary-market offer prices per gmina | Developer disclosures | api | [Category](https://zonio.tech/en/layers/market/) | | `epc` | Energy-performance certificates | National EPC register | api | [Category](https://zonio.tech/en/layers/market/) | | `demographics` | Population and density | GUS | api | n/a | | `kowr_land` | State agricultural land stock | KOWR | api, tiles | [KOWR land](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/) | | `protests` | Local opposition signals for renewable projects, per gmina | News + language model | api | [Protests](https://zonio.tech/en/layers/spatial-planning/protests-renewables-competition/) | # Scenarios A scenario is a site-selection screen for one asset type. **Hard criteria** must all pass; **parameters** tune them; **ranking signals** order the survivors with default weights you can override per request. Agents use them through the [`find_sites`](/mcp/tools#find_sites) MCP tool, and [plain-language search](/mcp/natural-language-search) applies the right one when a request names an asset type. In code, use them as a template for [`POST /search/parcels`](/guides/site-selection). ## Renewable energy Built on [grid connection capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/), [HV/EHV substations](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/), [MV power lines](https://zonio.tech/en/layers/power-grid/power-lines-bdot10k/), [solar irradiance](https://zonio.tech/en/layers/renewables/solar-irradiance-pvgis/), [wind setbacks](https://zonio.tech/en/layers/renewables/700-m-from-wind-turbines/), [land cover](https://zonio.tech/en/layers/land-cover-land-use/land-cover-bdot10k/), [protected areas](https://zonio.tech/en/layers/environment/protected-areas-gdos/) and [rivers](https://zonio.tech/en/layers/hydrography/rivers-canals-bdot10k/). New to grid capacity? Read [how to check connection capacity in Poland](https://zonio.tech/en/articles/grid-connection-capacity-poland/). | Scenario | Hard criteria | Min area | Ranked by | | --- | --- | --- | --- | | `solar_farm_site` | No blocking zone · no environmental risk nearby · near a substation · minimum solar yield · slope within tolerance · not forest or orchard | 1 ha | grid distance 40 · solar yield 30 · area 20 · slope 10 | | `wind_farm_site` | No blocking zone · statutory setback clear · no environmental risk within 1 km · near a substation | 0.5 ha | grid distance 50 · area 50 | | `bess_storage` | No blocking zone · grid capacity available · near a substation · no environmental risk · slope within tolerance | 0.2 ha | grid capacity 55 · grid distance 30 · area 15 | | `agrivoltaics_site` | Agricultural land · near an MV line · no blocking zone · not forest or orchard · slope within tolerance · no environmental risk | 2 ha | grid distance 45 · solar yield 30 · area 25 | | `biogas_plant` | Agricultural land · no blocking zone · near a substation · no environmental risk | 0.5 ha | area 45 · grid distance 35 · slope 20 | | `data_center_site` | Grid capacity ≥ 20 MW · near a substation · near water · no environmental risk · slope within tolerance | 0.5 ha | grid capacity 45 · water distance 30 · area 25 | ## Real estate Built on [local zoning plans](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/), [buildings](https://zonio.tech/en/layers/buildings-development/buildings-bdot10k/), [services](https://zonio.tech/en/layers/buildings-development/services-bdot10k/), [transit stops](https://zonio.tech/en/layers/infrastructure/transit-stops/), [planned roads](https://zonio.tech/en/layers/planned-infrastructure/planned-roads/), [heritage](https://zonio.tech/en/layers/cultural-heritage/heritage-historic-sites/), [terrain slope](https://zonio.tech/en/layers/terrain-relief/terrain-slope-lidar/) and [KOWR land](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/). | Scenario | Hard criteria | Min area | Ranked by | | --- | --- | --- | --- | | `urban_residential_development` | Residential zoning · vacant · no environmental risk · slope within tolerance · near transit | 500 m² | area 40 · transit distance 35 · slope 25 | | `housing_estate_site` | Residential zoning · vacant · near a school · near transit · no environmental risk · slope within tolerance · no KOWR pre-emption | 0.5 ha | area 35 · transit distance 35 · slope 30 | | `retail_park_site` | Commercial zoning · vacant · near transit · no environmental risk · slope within tolerance | 0.5 ha | area 45 · transit distance 45 · slope 10 | | `logistics_warehouse` | Near a planned or trunk road · no environmental risk · slope within tolerance | 1 ha | area 50 · slope 30 · transit distance 20 | | `brownfield_redevelopment` | Industrial zoning · existing low-rise building · slope within tolerance · not heritage · not contaminated | 0.1 ha | area 50 · floors 30 · slope 20 | | `public_land_opportunity` | Overlaps state agricultural land (KOWR) | 0.3 ha | area 60 · transit distance 40 | ## Parameters | Parameter | Unit | Default | Range | | --- | --- | --- | --- | | `min_area_sqm` | m² | per scenario | n/a | | `env_buffer_m` | m | 100 | 0–2,000 | | `max_slope_pct` | % | 30 | 5–60 | | `substation_km` | km | 10 | 1–30 | | `mv_line_km` | km | 2 | 0.5–10 | | `capacity_km` | km | 5 | 1–30 | | `min_capacity_mw` | MW | 10 | 1–200 | | `min_yield_kwh_kwp` | kWh/kWp | 1,000 | 880–1,120 | | `max_stop_m` | m | 800 | 100–2,000 | | `school_m` | m | 1,500 | 500–3,000 | | `planned_road_km` | km | 5 | 1–30 | | `water_km` | km | 3 | 1–20 | | `max_floors` | floors | 2 | 1–5 | Scenarios override some defaults: `wind_farm_site` uses a 1 km environmental buffer and 15 km substation reach, for example. The [`list_scenarios`](/mcp/tools#list_scenarios) tool always returns the effective values. ## Need another asset type? Scenarios are configuration on our side, not code. Tell us what you are siting (hotels, cemeteries, PV on rooftops, hydrogen) and the criteria an expert would apply. [Get in touch](/access). # Coverage & freshness ## National by default Every layer Zonio publishes is loaded for the **whole country**, not a demo city. A layer is only marked national once its row count has been checked against the publisher's own total. Some layers are inherently partial because the public data is: | Layer | Why it is partial | | --- | --- | | [`mpzp_zones`](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/) | Roughly a third of Poland is covered by a local plan, and gminas publish them through their own services of varying quality. Plan *boundaries* are national; zone polygons are added gmina by gmina. | | [`plan_ogolny_zones`](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/) | Gminas are adopting general plans now. Coverage grows every month ([what the reform changes](https://zonio.tech/en/articles/general-plan-what-changes/)). | | [`grid_capacity`](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/) | Each DSO publishes capacity in its own format and granularity ([how to read it](https://zonio.tech/en/articles/grid-connection-capacity-poland/)). | | [`terrain_stats`](https://zonio.tech/en/layers/terrain-relief/terrain-slope-lidar/) | Computed per parcel from LIDAR; rolling out powiat by powiat. | `GET /regions?q=…` returns, for any region, which layers have data there. The `zonio://regions/{teryt}` MCP resource does the same for agents, and every gmina's coverage and general-plan status is visible on the [Zonio national map](https://zonio.tech/en/map/). ## No data is not *no* The most dangerous answer a land-data product can give is *"no constraints found"* when the constraint layer simply does not cover the area. Zonio separates the two. Every search response includes a `coverage` entry per criterion: | `mode` | `status: missing` means | | --- | --- | | `requires` | The criterion must be proven true, and the layer has no data here. The search returns `blocked: true` and no results; it does not pretend nothing qualifies. | | `screens` | The criterion excludes bad parcels, and the layer has no data here. Results are returned **unscreened** on that criterion; you must tell the user. | Reports and MCP tool outputs follow the same rule, and our tool descriptions instruct agents to surface every missing criterion. ## Refresh cadence | Cadence | Layers | | --- | --- | | Daily | Airspace (AUP/UUP), air quality | | Weekly | Grid capacity, building permits, transactions, Plan Ogólny | | Monthly | MPZP zones and plans, environmental layers, OSM-derived layers | | Quarterly | Parcels, buildings, roads (BDOT10k), terrain | Each refresh only touches a table in a final atomic step, so the API never serves a half-loaded layer. `GET /layers` returns `refreshed_at` for each layer, and every row carries its own fetch time. See [Provenance & pipeline](/data/pipeline). # Provenance & pipeline Behind the API is a purpose-built geospatial ETL. Knowing how it works tells you how far to trust each answer. ## From source to API ``` public source ──fetch──▶ staging ──transform──▶ working copy ──validate──▶ clean table ──▶ API · MCP · tiles (WFS, ArcGIS, raw rows reproject to atomic swap ATOM, WMS, files) + run id EPSG:2180, fix per run geometries ``` * **Fetch.** Each of ~45 topics has its own fetcher for its source's protocol: OGC WFS with adaptive tile paging, ArcGIS REST, ATOM/GML downloads, per-powiat BDOT10k packages, WMS sampling, Overpass. Fetchers compare what they received against the server's own feature count and fail loudly on a shortfall. * **Transform.** Geometry is reprojected to EPSG:2180 and validated; invalid shapes are repaired, and anything that still fails goes to a dead-letter queue for inspection instead of aborting the run. * **Promote.** Data reaches the clean table in one atomic step. A failed run leaves the previous version in place. * **Index.** GiST spatial indexes on every geometry; large tables are partitioned by gmina so a regional query only touches that region. ## Every row is traceable Each row in every table carries: | Column | Meaning | | --- | --- | | `source_run_id` | The pipeline run that produced it | | `fetched_at` | When it was fetched | | `source_url` | The exact endpoint it came from | | `source_bbox` | The area that run covered | Detail endpoints expose this as a `provenance` object, so you can always show *"from GDOŚ, fetched 2026-09-28"* next to a constraint, or audit an answer months later. ## Normalisation * **Zoning symbols.** Thousands of local spellings (`MN`, `MN1`, `1MN/U`, `M.N.`) are mapped to a common taxonomy of categories; the raw symbol is always kept and anything unrecognised is flagged for review instead of guessed. * **Coordinates.** One storage CRS for everything, so a distance is always metres. See [Coordinates](/concepts#coordinates). * **Identifiers.** Parcels keyed by EGiB id, regions by TERYT, so data from any source joins cleanly. ## Quality checks After each national load we check row counts against the publisher's totals and look at the category breakdown. A category with zero rows nationally is treated as a bug until proven otherwise. Verification dates and national counts are recorded per layer. # Use cases Teams that don't want to build can use the [Zonio app](https://zonio.tech/en/) directly: see [features](https://zonio.tech/en/features/) and [pricing](https://zonio.tech/en/pricing/). ## AI land scout **Who:** PropTech startups, brokers, investment boutiques. A chat assistant on your site where buyers describe what they want (*"a quiet plot for a house near Kraków, under 400 k PLN, with a bus nearby"*) and get real parcels back, with zoning explained in plain language and a link to the parcel in the [Zonio app](https://zonio.tech/en/features/). Built with the [MCP server and the Claude API](/mcp/claude-api) in an afternoon. ## Renewable-energy origination **Who:** PV, wind, BESS and agri-PV developers. Screen a voivodeship for every parcel that clears the legal and technical bar for your asset, ranked by distance to [substations](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/) and [available capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/) ([how capacity data works](https://zonio.tech/en/articles/grid-connection-capacity-poland/)). Hand the shortlist to the land team with constraints and owners' pre-emption risks already flagged. See [Site selection](/guides/site-selection). ## Due diligence at scale **Who:** Banks, funds, land-acquisition teams. Feed hundreds of parcels from a land-register extract into `POST /parcels/batch?include=regulations,constraints` and get a red/amber/green sheet before anyone opens a geoportal. Generate a PDF [report](/guides/due-diligence) for the ones that go forward. ## Valuation and market analytics **Who:** Valuers, analysts, lenders. Combine [RCN transaction comparables](https://zonio.tech/en/layers/market/transaction-prices/) with zoning and constraints so the comps you pick actually compare like with like: the same zone category, the same kind of access. ## Map products **Who:** GIS teams, municipal tools, consultancies. Drop national zoning, constraints and grid layers (browse them in the [layer atlas](https://zonio.tech/en/layers/)) into your own MapLibre application with [vector tiles](/guides/maps), and use the API for click-through detail. # Request access The Zonio API and MCP server are in **private preview**. We are onboarding a small group of design partners who build with Polish land data: AI products, energy developers, real-estate and finance teams. Email **[kontakt@zonio.tech](mailto\:kontakt@zonio.tech)** with: 1. who you are and what you are building; 2. whether you will use the REST API, the MCP server, or both; 3. the regions and layers you care about most. Looking for the Zonio app rather than the API? Start at [zonio.tech](https://zonio.tech/en/). More about us on the [company page](https://zonio.tech/en/company/). # Glossary Most terms link to the matching layer in the [zonio.tech layer atlas](https://zonio.tech/en/layers/). | Term | Meaning | | --- | --- | | **[BDOT10k](https://zonio.tech/en/layers/buildings-development/buildings-bdot10k/)** | National topographic object database at 1:10,000: buildings, roads, land cover, utilities. Published per powiat by GUGiK. | | **[BDL](https://zonio.tech/en/layers/land-cover-land-use/forests-bdl/)** | *Bank Danych o Lasach*, the Forest Data Bank. | | **[Działka ewidencyjna](https://zonio.tech/en/layers/cadastre/cadastral-parcels/)** | Cadastral parcel. The unit Zonio searches. | | **[DSO / OSD](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/)** | Distribution system operator (*operator systemu dystrybucyjnego*): PGE, Tauron, Enea, Energa, Stoen. Publishes available connection capacity. | | **[EGiB](https://zonio.tech/en/layers/land-cover-land-use/land-use-soil-classes-egib/)** | *Ewidencja Gruntów i Budynków*, the land and buildings register; source of parcel geometry and ids. | | **[GDOŚ](https://zonio.tech/en/layers/environment/protected-areas-gdos/)** | General Directorate for Environmental Protection; publishes protected areas. | | **[GPZ](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/)** | *Główny Punkt Zasilający*, an HV/MV substation, the usual grid connection point for renewables. | | **GUGiK** | Head Office of Geodesy and Cartography; publishes cadastre, BDOT10k, LIDAR, orthophoto and RCN. | | **GZWP** | *Główny Zbiornik Wód Podziemnych*, a major groundwater reservoir, often with protection restrictions. | | **[Gmina](https://zonio.tech/en/layers/cadastre/municipality-boundaries/)** | Municipality. ~2,477 in Poland; responsible for local and general plans. Each has a profile on the [national map](https://zonio.tech/en/map/). | | **[KOWR](https://zonio.tech/en/layers/land-cover-land-use/state-agricultural-land-kowr/)** | National Support Centre for Agriculture; holds state agricultural land and a pre-emption right on many farmland sales. | | **[MIDAS](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/)** | National mineral-resources and mining-area database (PIG-PIB). | | **[MPZP](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/)** | *Miejscowy plan zagospodarowania przestrzennego*, the local zoning plan. Legally binding. | | **[Natura 2000](https://zonio.tech/en/layers/environment/protected-areas-gdos/)** | EU network of protected sites (bird and habitat directives). | | **[NMT / DTM](https://zonio.tech/en/layers/terrain-relief/hillshade-nmt/)** | Digital terrain model from LIDAR. | | **[Obręb](https://zonio.tech/en/layers/cadastre/cadastral-districts-obreby/)** | Cadastral precinct; part of the parcel id. | | **[OZE](https://zonio.tech/en/layers/renewables/)** | *Odnawialne źródła energii*, renewable energy sources. | | **[Plan Ogólny](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/)** | General plan, mandatory for every gmina, replacing the Studium. [What it changes](https://zonio.tech/en/articles/general-plan-what-changes/). | | **[Powiat](https://zonio.tech/en/layers/cadastre/county-boundaries/)** | County. 380 in Poland. | | **[PRG](https://zonio.tech/en/layers/cadastre/municipality-boundaries/)** | National register of boundaries and addresses. | | **PUWG 1992** | Poland's national coordinate system, EPSG:2180. Zonio stores and measures in it. | | **[RCN](https://zonio.tech/en/layers/market/transaction-prices/)** | *Rejestr Cen Nieruchomości*, the national register of notarised real-estate prices. | | **[Studium](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/)** | *Studium uwarunkowań i kierunków zagospodarowania przestrzennego*, the legacy directional study. | | **TERYT** | National territorial division codes: 2 digits voivodeship, 4 powiat, 7 gmina. | | **[ULC](https://zonio.tech/en/layers/infrastructure/height-limits-ols/)** | Civil Aviation Authority; publishes obstacle limitation surfaces. | | **[Województwo](https://zonio.tech/en/layers/cadastre/voivodeship-boundaries/)** | Voivodeship (province). 16 in Poland. | | **WZ** | *Warunki zabudowy*, a building conditions decision, required outside an MPZP. | Zonio Geo API · MCP server · Private preview Ask Poland's land anything. 38 million cadastral parcels, every zoning plan we can reach, environmental constraints, the power grid and the property market, behind one API key. Query it from your code, or give it to your AI agent as an MCP server. Connect your agent Quickstart API reference
## Search parcels the way you'd ask a colleague Add the Zonio MCP server to Claude, Cursor or your own agent and ask: > *Find vacant plots over 2 ha within 5 km of a 110 kV substation in Podkarpackie, outside Natura 2000, and rank them by available grid capacity.* The agent calls [`search_parcels`](/mcp/tools#search_parcels), checks each shortlisted plot with [`check_regulations`](/mcp/tools#check_regulations) and [`check_constraints`](/mcp/tools#check_constraints), and answers with parcel ids, a map link and the reasons every plot made the list, including which criteria **could not be checked** in that region. ```bash [Claude Code] claude mcp add --transport http zonio https://mcp.zonio.tech/mcp \ --header "Authorization: Bearer $ZONIO_API_KEY" ``` ## Start here ## See the data on zonio.tech Every layer behind the API has its own page on [zonio.tech](https://zonio.tech/en/) with a description, the publisher and a live map. Start with the ones agents ask about most: * **Zoning:** [local zoning plans (MPZP)](https://zonio.tech/en/layers/spatial-planning/local-zoning-plans-mpzp/), [municipal general plans](https://zonio.tech/en/layers/spatial-planning/municipal-general-plan/), [Studium](https://zonio.tech/en/layers/spatial-planning/studium-suikzp-archival/) * **Cadastre:** [cadastral parcels](https://zonio.tech/en/layers/cadastre/cadastral-parcels/), [cadastral districts](https://zonio.tech/en/layers/cadastre/cadastral-districts-obreby/), [municipality boundaries](https://zonio.tech/en/layers/cadastre/municipality-boundaries/) * **Power grid:** [grid connection capacity](https://zonio.tech/en/layers/power-grid/grid-connection-capacity-dsos-pse/), [HV/EHV substations (PSE)](https://zonio.tech/en/layers/power-grid/hv-ehv-substations-pse/), [transmission lines](https://zonio.tech/en/layers/power-grid/transmission-lines-pse/) * **Environment & hazards:** [protected areas](https://zonio.tech/en/layers/environment/protected-areas-gdos/), [flood hazard](https://zonio.tech/en/layers/hazards/flood-hazard-isok/), [mining areas](https://zonio.tech/en/layers/hazards/mining-areas-terrains-midas/) * **Market:** [transaction prices](https://zonio.tech/en/layers/market/transaction-prices/) Browse all 14 categories in the [layer atlas](https://zonio.tech/en/layers/), or open any of the 2,477 gminas on the [national map](https://zonio.tech/en/map/).
# Geocoding Turn addresses and place names into parcels and TERYT codes. ## Geocode an address `GET /geocode` Free-text address search against the national address register (PRG), resolved to the parcel at each hit. ### Query parameters - `q` `string` _(required)_ ### Responses #### `200`: Up to 15 candidates, best first. Body (`application/json`): - `label` `string` - `lng` `number` - `lat` `number` - `parcel_id` `string | null` ### Example request ```bash curl 'https://api.zonio.tech/v1/geocode?q=Kraków, Rynek Główny 1' ``` ```ts fetch('https://api.zonio.tech/v1/geocode?q=Kraków, Rynek Główny 1') ``` ## Search administrative units `GET /regions` Diacritic-insensitive autocomplete over every gmina, powiat and voivodeship. `krakow` finds `Kraków`. ### Query parameters - `q` `string` _(required)_ ### Responses #### `200`: Matching units. Body (`application/json`): - `teryt` `string` - `name` `string` - `unit_type` `string` - `parent` `string | null` - `layers_available` `string[]`: Layers with data in this unit. Check before relying on a partial layer. ### Example request ```bash curl 'https://api.zonio.tech/v1/regions?q=krakow' ``` ```ts fetch('https://api.zonio.tech/v1/regions?q=krakow') ``` # Layers The raw data layers behind every answer. ## List data layers `GET /layers` ### Responses #### `200`: Every layer with its source, coverage and freshness. Body (`application/json`): - `id` `string` - `name` `string` - `category` `string` - `geometry_type` `string` - `source` `string` - `coverage` `string` - `feature_count` `integer` - `refreshed_at` `string ` - `tiles_url` `string | null` ### Example request ```bash curl https://api.zonio.tech/v1/layers ``` ```ts fetch('https://api.zonio.tech/v1/layers') ``` ## Query a layer `GET /layers/{layer_id}/features` Raw features of one layer inside a bounding box, as GeoJSON. Paginate with `cursor`. ### Path parameters - `layer_id` `string` _(required)_ ### Query parameters - `bbox` `string` _(required)_: WGS84 `minLng,minLat,maxLng,maxLat`. - `limit` `integer` - `cursor` `string` ### Responses #### `200`: A page of features. Body (`application/geo+json`): - `type` `string` - `features` `object[]` - `next_cursor` `string | null` ### Example request ```bash curl 'https://api.zonio.tech/v1/layers/power_substations/features?bbox=19.80,49.95,20.25,50.15&limit=500&cursor=string' ``` ```ts fetch('https://api.zonio.tech/v1/layers/power_substations/features?bbox=19.80,49.95,20.25,50.15&limit=500&cursor=string') ``` # Market Real-estate transaction comparables and price medians. ## List transaction comparables `GET /market/transactions` Notarised sales from the national price register (RCN) near a parcel or in a region. ### Query parameters - `parcel_id` `string` - `region` `string` - `radius_m` `integer` - `property_type` `string` - `since` `string ` ### Responses #### `200`: Transactions, newest first. Body (`application/json`): - `count` `integer` - `median_price_per_sqm_pln` `number` - `transactions` `object[]` - `date` `string ` - `property_type` `string` - `price_pln` `number` - `area_sqm` `number` - `price_per_sqm_pln` `number` - `distance_m` `number` ### Example request ```bash curl 'https://api.zonio.tech/v1/market/transactions?parcel_id=string®ion=string&radius_m=2000&property_type=land&since=2024-01-01' ``` ```ts fetch('https://api.zonio.tech/v1/market/transactions?parcel_id=string®ion=string&radius_m=2000&property_type=land&since=2024-01-01') ``` # Parcels Look up a parcel and everything known about it. ## Get a parcel `GET /parcels/{parcel_id}` One cadastral parcel as a GeoJSON Feature. Use `include` to embed the regulations, constraints, terrain, proximity and market blocks in a single round-trip. ### Path parameters - `parcel_id` `string` _(required)_: EGiB parcel id. URL-encode the `/` in parcel numbers (`100/1` → `100%2F1`). ### Query parameters - `include` `string`: Comma-separated blocks to embed. ### Responses #### `200`: The parcel. Body (`application/geo+json`): - `type` `string` - `id` `string` - `geometry` `object`: GeoJSON Polygon or MultiPolygon in WGS84. - `properties` `object & object` #### `401`: Missing or invalid API key. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` #### `404`: No such parcel. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` ### Example request ```bash curl 'https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1?include=regulations,constraints,terrain,proximity' ``` ```ts fetch('https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1?include=regulations,constraints,terrain,proximity') ``` ## Get the parcel at a point `GET /parcels/at` The parcel containing the point, falling back to the nearest parcel within 200 m. ### Query parameters - `lng` `number` _(required)_ - `lat` `number` _(required)_ ### Responses #### `200`: The parcel. Body (`application/geo+json`): - `type` `string` - `id` `string` - `geometry` `object`: GeoJSON Polygon or MultiPolygon in WGS84. - `properties` `object & object` #### `404`: No such parcel. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` ### Example request ```bash curl 'https://api.zonio.tech/v1/parcels/at?lng=19.9372&lat=50.0614' ``` ```ts fetch('https://api.zonio.tech/v1/parcels/at?lng=19.9372&lat=50.0614') ``` ## Autocomplete parcel ids `GET /parcels/autocomplete` Prefix and substring match on the EGiB parcel id, for search boxes. ### Query parameters - `q` `string` _(required)_ ### Responses #### `200`: Up to 10 suggestions. Body (`application/json`): - `parcel_id` `string` - `area_sqm` `number` - `gmina` `string` - `teryt` `string` ### Example request ```bash curl 'https://api.zonio.tech/v1/parcels/autocomplete?q=126101_1.0018.AR_1.100' ``` ```ts fetch('https://api.zonio.tech/v1/parcels/autocomplete?q=126101_1.0018.AR_1.100') ``` ## Get many parcels `POST /parcels/batch` Up to 500 parcels in one call. Unknown ids are listed in `missing`. ### Request body (required) (`application/json`) - `parcel_ids` `string[]` _(required)_ - `include` `string[]` ### Responses #### `200`: The parcels found. Body (`application/json`): - `type` `string` - `features` `object[]` - `type` `string` - `id` `string` - `geometry` `object`: GeoJSON Polygon or MultiPolygon in WGS84. - `properties` `object & object` - `missing` `string[]` ### Example request ```bash curl https://api.zonio.tech/v1/parcels/batch \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "parcel_ids": [ "126101_1.0018.AR_1.100/1", "126101_1.0018.AR_1.101/2" ], "include": [ "regulations" ] }' ``` ```ts fetch('https://api.zonio.tech/v1/parcels/batch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ parcel_ids: ['126101_1.0018.AR_1.100/1', '126101_1.0018.AR_1.101/2'], include: ['regulations'] }) }) ``` ## Get zoning & planning status `GET /parcels/{parcel_id}/regulations` The planning instruments that apply to the parcel: the local plan (MPZP) zone and its parameters, the general plan (Plan Ogólny) zone, the legacy Studium designation, and statutory pre-emption rights. ### Path parameters - `parcel_id` `string` _(required)_: EGiB parcel id. URL-encode the `/` in parcel numbers (`100/1` → `100%2F1`). ### Responses #### `200`: The regulatory state. Body (`application/json`): - `mpzp` `object | null`: Local zoning plan (MPZP) zone at the parcel, or null if no plan applies. - `plan_name` `string` - `adopted_on` `string ` - `symbol` `string` - `category` `string` - `label` `string` - `max_height_m` `number | null` - `max_building_coverage_pct` `number | null` - `min_biologically_active_pct` `number | null` - `document_url` `string ` - `plan_ogolny` `object | null`: General plan zone, mandatory for every gmina from 2026. - `status` `string` - `zone_symbol` `string` - `zone_label` `string` - `studium` `object | null` - `designation` `string` - `pre_emption` `object[]`: Statutory pre-emption rights that apply to a sale. - `holder` `string` - `basis` `string` ### Example request ```bash curl https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/regulations ``` ```ts fetch('https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/regulations') ``` ## Get environmental & legal constraints `GET /parcels/{parcel_id}/constraints` Every constraint layer that intersects the parcel or a buffer around it: Natura 2000, protected landscapes, flood-risk zones, heritage protection, mining areas, aviation obstacle surfaces, groundwater protection, wind-turbine setbacks. ### Path parameters - `parcel_id` `string` _(required)_: EGiB parcel id. URL-encode the `/` in parcel numbers (`100/1` → `100%2F1`). ### Query parameters - `buffer_m` `integer` ### Responses #### `200`: The constraints found. Body (`application/json`): - `blocking` `boolean`: True when at least one constraint normally prevents development. - `items` `object[]` - `layer` `string` - `kind` `string` - `name` `string` - `overlap_pct` `number` - `distance_m` `number` - `severity` `string` ### Example request ```bash curl 'https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/constraints?buffer_m=0' ``` ```ts fetch('https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/constraints?buffer_m=0') ``` ## Get terrain statistics `GET /parcels/{parcel_id}/terrain` Elevation and slope derived from GUGiK LIDAR. ### Path parameters - `parcel_id` `string` _(required)_: EGiB parcel id. URL-encode the `/` in parcel numbers (`100/1` → `100%2F1`). ### Responses #### `200`: Terrain statistics. Body (`application/json`): - `elevation_min_m` `number` - `elevation_max_m` `number` - `slope_mean_pct` `number` - `slope_p90_pct` `number` - `aspect` `string` ### Example request ```bash curl https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/terrain ``` ```ts fetch('https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/terrain') ``` ## Get distances to nearby infrastructure `GET /parcels/{parcel_id}/proximity` The nearest feature of each requested kind and its distance in metres: substations, MV lines, transit stops, schools, water, planned roads, EV chargers and more. ### Path parameters - `parcel_id` `string` _(required)_: EGiB parcel id. URL-encode the `/` in parcel numbers (`100/1` → `100%2F1`). ### Query parameters - `kinds` `string`: Comma-separated. Defaults to all. ### Responses #### `200`: Nearest features. ### Example request ```bash curl 'https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/proximity?kinds=power_substation,transit_stop,school' ``` ```ts fetch('https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/proximity?kinds=power_substation,transit_stop,school') ``` ## Get a due-diligence report `GET /parcels/{parcel_id}/report` Every block above plus market comparables, assembled into one document. Ask for `application/pdf` to get a branded, shareable PDF. ### Path parameters - `parcel_id` `string` _(required)_: EGiB parcel id. URL-encode the `/` in parcel numbers (`100/1` → `100%2F1`). ### Query parameters - `scenario` `string`: Evaluate the parcel against a scenario's criteria. ### Responses #### `200`: The report. Body (`application/json`): - `parcel` `object` - `type` `string` - `id` `string` - `geometry` `object`: GeoJSON Polygon or MultiPolygon in WGS84. - `properties` `object & object` - `summary` `string` - `scenario_fit` `object | null` - `scenario` `string` - `passes` `boolean` - `failed_criteria` `string[]` - `market` `object` - `median_price_per_sqm_pln` `number` - `comparables` `integer` ### Example request ```bash curl 'https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/report?scenario=solar_farm_site' ``` ```ts fetch('https://api.zonio.tech/v1/parcels/126101_1.0018.AR_1.100%252F1/report?scenario=solar_farm_site') ``` # Search Find parcels with structured filters or a plain-language query. ## Search parcels in plain language `POST /search/natural-language` Describe the land you are looking for in English or Polish. Zonio resolves the region, picks the matching scenario and filters, runs the search and returns both the parcels **and the structured interpretation**, so you can show the user exactly what was searched and re-run it with `POST /search/parcels`. ### Request body (required) (`application/json`) - `query` `string` _(required)_ - `region` `string`: Optional TERYT code that overrides any place named in the query. - `language` `string` - `limit` `integer` ### Responses #### `200`: The interpretation and the matching parcels. #### `400`: The request was malformed. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` #### `401`: Missing or invalid API key. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` #### `422`: The query could not be resolved to a region or any filter. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` #### `429`: Too many requests. Retry after `Retry-After` seconds. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` ### Example request ```bash curl https://api.zonio.tech/v1/search/natural-language \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "query": "Flat plots over 2 ha near Rzeszów, zoned for industry, within 5 km of a 110 kV substation and outside Natura 2000", "limit": 20 }' ``` ```ts fetch('https://api.zonio.tech/v1/search/natural-language', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: 'Flat plots over 2 ha near Rzeszów, zoned for industry, within 5 km of a 110 kV substation and outside Natura 2000', limit: 20 }) }) ``` ## Search parcels with structured filters `POST /search/parcels` Hard filters must all pass; survivors are ranked by the optional `rank_by` signals. Every response carries a `coverage` array: when a filter's data layer does not cover the region, the filter is reported as `missing` instead of silently matching nothing. ### Request body (required) (`application/json`) - `region` `string`: TERYT code (2, 4 or 7 digits). Omit to search all of Poland. - `bbox` `string`: WGS84 `minLng,minLat,maxLng,maxLat`, as an alternative to `region`. - `filters` `object` - `area_sqm` `object` - `min` `number` - `max` `number` - `zoning` `object` - `categories` `string[]` - `symbols` `string[]` - `require_plan` `boolean` - `slope_pct` `object` - `max` `number` - `vacant` `boolean`: No building footprint on the parcel. - `near` `object[]` - `layer` `string` - `within_m` `number` - `where` `object` - `exclude` `object[]` - `layer` `string` - `kinds` `string[]` - `buffer_m` `number` - `rank_by` `object[]` - `signal` `string` - `weight` `integer` - `limit` `integer` ### Responses #### `200`: Matching parcels, ranked. Body (`application/json`): - `count` `integer` - `blocked` `boolean` - `coverage` `object[]` - `criterion` `string` - `mode` `string`: `requires`: a missing layer blocks the search. `screens`: results are returned but not screened on this criterion. - `status` `string` - `results` `object[]` - `parcel_id` `string` - `area_sqm` `number` - `gmina` `string` - `geometry` `object` - `score` `number | null`: Relative 0–100 rank within this result set. - `components` `object[]` - `signal` `string` - `value` `number` - `contribution` `number` #### `400`: The request was malformed. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` #### `401`: Missing or invalid API key. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` #### `429`: Too many requests. Retry after `Retry-After` seconds. Body (`application/json`): - `error` `object` - `code` `string` _(required)_ - `message` `string` _(required)_ - `request_id` `string` - `candidates` `string[]` ### Example request ```bash curl https://api.zonio.tech/v1/search/parcels \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "region": "1863", "filters": { "area_sqm": { "min": 20000 }, "zoning": { "categories": [ "industrial", "commercial_services" ] }, "slope_pct": { "max": 5 }, "near": [ { "layer": "power_substations", "within_m": 5000, "where": { "voltage_kv_gte": 110 } } ], "exclude": [ { "layer": "env_layers", "kinds": [ "natura2000", "flood_risk_1pct" ], "buffer_m": 100 } ] }, "rank_by": [ { "signal": "grid_distance_km", "weight": 60 }, { "signal": "area", "weight": 40 } ], "limit": 50 }' ``` ```ts fetch('https://api.zonio.tech/v1/search/parcels', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ region: '1863', filters: { area_sqm: { min: 20000 }, zoning: { categories: ['industrial', 'commercial_services'] }, slope_pct: { max: 5 }, near: [ { layer: 'power_substations', within_m: 5000, where: { voltage_kv_gte: 110 } } ], exclude: [ { layer: 'env_layers', kinds: ['natura2000', 'flood_risk_1pct'], buffer_m: 100 } ] }, rank_by: [ { signal: 'grid_distance_km', weight: 60 }, { signal: 'area', weight: 40 } ], limit: 50 }) }) ```