Work with this as data
Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/cenogram-pl:cenogram-pl-locations-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
OpenAPI Specification
openapi: 3.2.0
info:
title: Cenogram Locations API
version: 1.0.0
description: 'Property and parcel intelligence for Poland. 8M+ verified transactions from the official RCN registry (Rejestr Cen Nieruchomości) — prices from notarial deeds, not asking/listing prices, from 2003 to present, nationwide. On top of prices: the picture we hold of a parcel — cadastral core, the deed history recorded against it, and context layers (flood hazard, general-plan zoning, building permits, structures, heritage, landslide hazard, subsurface constraints from mining terrains and groundwater reservoirs, surroundings, transit access, road access, agricultural use), plus municipality-level demographics and infrastructure signals, and a comparable-based valuation estimate. Context layers are sourced from official public registers. Transaction coverage is nationwide; the cadastral parcel corpus is near-complete but not the whole register and not live — every parcel collection carries a `corpus_coverage` block measuring what we hold for the counties it addresses. Access is metered in tokens: a new account starts with a token allowance and a trial, after which a free account keeps calling on a recurring free allowance, and paid plans raise it. Current allowances, trial length and prices live at https://cenogram.pl/api and are deliberately not repeated here, because a figure frozen into this description goes stale on its own. A 402 response carries the current balance and, on a free account, the date the next tokens arrive. Every operation states its price both in prose and machine-readably, in the `x-cenogram-token-cost` extension field, generated from the one pricing table. Tokens and credits are the same unit under two names: the response headers (`X-Credits-Cost`, `X-Credits-Balance`, `X-Credits-Refunded`) and some older endpoint descriptions say credits. Working with an AI assistant? Give it https://cenogram.pl/api/llms.txt — this whole surface as one plain-text file, costs, refund rules and response conventions included.'
servers:
- url: https://cenogram.pl
security:
- apiKey: []
tags:
- name: Locations
description: District and location discovery
paths:
/api/v1/districts:
get:
summary: List districts
tags:
- Locations
description: 'Returns all unique district names. Optionally filter by search query (min 2 chars).
Free - discovery catalog, 0 tokens per call.'
parameters:
- schema:
type: string
example: Mokotów
in: query
name: q
required: false
description: Search query (min 2 characters)
x-cenogram-token-cost: 0
responses:
'200':
description: Default Response
operationId: getApiV1Districts
x-operation-id-source: derived
/api/v1/districts/coverage:
get:
summary: Coverage of RCN district labels
tags:
- Locations
description: 'For each RCN district name (the `rcn_district: true` rows of `/api/locations?search=`), returns how much data sits behind the label: `transactions` (all deeds booked under it), `price_samples` (the subset that enters price-per-m² statistics: units with a market price per m²), and the first and last transaction date (`date_from`, `date_to`, `YYYY-MM-DD`). `gmina` is set only when the name maps to exactly ONE gmina and at least 95% of the label''s transactions lie inside it: then `gmina.transactions` is the whole gmina''s count and `gmina.teryt` is the code to pass as `teryt=` for the full data; otherwise it is `null`. Pass 1-200 comma-separated names, each at most 100 characters. A name that is not an RCN district still answers 200 with `rcn_district: false`, zero counts and null dates. Rows keep the order of the request.
Free - discovery catalog, 0 tokens per call.'
parameters:
- schema:
type: string
example: Miastko,Wola
in: query
name: names
required: false
description: Required. Comma-separated RCN district names, 1-200, each at most 100 characters.
x-cenogram-token-cost: 0
responses:
'200':
description: Default Response
operationId: getApiV1DistrictsCoverage
x-operation-id-source: derived
/api/v1/locations:
get:
summary: Browse or search the TERYT hierarchy
tags:
- Locations
description: 'Two ways to reach a TERYT administrative unit. Browse the hierarchy with `parent` — voivodeships → counties → municipalities → precincts, one level at a time, passing the code of the level above to drill down. Or find a unit by NAME with `search`: any fragment of at least 2 letters or digits (at most 64 characters), matched anywhere inside the name, case- and diacritic-insensitive, `%` and `_` matched literally, across all four levels at once. Search results are ranked exact → prefix → infix and then broad → fine, capped at 50 rows with no paging — a fuller result means another character, not another page. `parent` wins when both are given. Every row carries `parent_name`, the name of the unit one level up (null for a voivodeship); search rows additionally carry `match` (`exact`, `prefix` or `infix`: how the fragment sits in the name, the tier the ranking above sorts by) and `rcn_district`. Only rows with `rcn_district: true` are usable as a `search_transactions(location=)` / `location=` name — for every other row use its `code` as `teryt=`, because the transaction search cannot find those names and `location=` would fall into an empty result. A search with no match answers 200 with an empty array and an `X-Cenogram-Hint` header pointing at /api/districts?q= for the registry name namespace, never a 404. Precincts with no name or a purely numeric name are left out of search.
Free - discovery catalog, 0 tokens per call.'
parameters:
- schema:
type: string
example: '14'
in: query
name: parent
required: false
description: Parent TERYT code (2=voivodeships, 4=counties, 6=municipalities). Empty for top-level.
- schema:
type: string
example: wejherowo
in: query
name: search
required: false
description: Name fragment to find a unit by, matched anywhere inside the name (min 2 letters or digits, max 64 characters, no wildcards). `parent` wins when both are given.
x-cenogram-token-cost: 0
responses:
'200':
description: Default Response
operationId: getApiV1Locations
x-operation-id-source: derived
/api/v1/streets:
get:
summary: List street names in a county
tags:
- Locations
description: 'Street names we hold for parcels inside an administrative scope, for completing what somebody is typing. `q` is any part of the name (at least 3 letters or digits, at most 200 characters, `%` and `_` matched literally) and is always required. The scope is required too, but can be given either way: `teryt`, a single prefix of at least 4 digits (county level or finer), or `location`, a county, city or gmina NAME resolved to its TERYT (a gmina name gives a 6-digit scope) — `teryt` wins when both are given, and a name shared by several units answers with the candidates rather than picking one. The scope itself is not optional: street names carry no administrative code of their own, so without one this would be a search of the whole country. The answer echoes the scope it used in `scope`, so several requests in flight at once can be told apart. Matching is ANYWHERE inside the name, not from its first character, and case- and accent-insensitive, so `q=Jana` does find `aleja Jana Pawła II` and `q=lesna` finds `Leśna`. That is deliberate and it is the same rule the `street=` filter on `/api/parcels` uses — a name this route suggests is a name that filter can find, and now in both directions. It also matters because some counties write the generic member into the name itself (`ulica X`, `aleja X`, `osiedle X`), and reading from the first character would answer nothing at all for them. Every entry says where the name came from: `rcn` for a name recorded in the registry, `approx` for one we derived for parcels the registry left without a street. The registry wins when both hold the same name, and names that differ only in capitalisation are returned once. At most 50 names come back and there is no paging: when `pagination.has_more` is true, type more of the name rather than asking for a second page. Coverage follows the addresses themselves — thin outside towns — so an empty answer means we hold no street names matching that fragment there, not that the county is missing. For rural parcels the way in is `/parcels/resolve?q=` with the precinct name. Your plan''s entitlement still applies, even though this draws nothing from your balance. The catalogue can be switched off for maintenance, in which case this answers 503 rather than an empty list.
Free - discovery catalog, 0 tokens per call.'
parameters:
- schema:
type: string
example: Karm
in: query
name: q
required: true
description: Part of the street name, matched anywhere inside it (min 3 letters or digits, max 200 characters, no wildcards)
- schema:
type: string
example: '1261'
in: query
name: teryt
required: false
description: TERYT prefix, at least 4 digits (county). Browse them with /api/locations. Either this or `location` is required; this one wins when both are given.
- schema:
type: string
example: Kraków
in: query
name: location
required: false
description: County, city or gmina NAME, resolved to its TERYT. Either this or `teryt` is required. A name shared by several units answers 400 listing the candidates rather than picking one.
x-cenogram-token-cost: 0
responses:
'200':
description: Default Response
operationId: getApiV1Streets
x-operation-id-source: derived
components:
securitySchemes:
apiKey:
type: apiKey
name: X-Api-Key
in: header