--- name: address-search description: Resolve a free-text address to the official one, with its cadastral unit, EHR code and coordinates. module: ads.mjs execution: get --- # Estonian address search (ADS / in-aadress) The **aadressiandmete süsteem** is the register that decides what an Estonian address *is*. Every other property register is keyed by something this returns, so this is where a question about a place starts — not where it ends. Use it whenever the citizen names a place: "my house", "the lot next door", "Vabriku 12". It answers two questions the other registers cannot: - **Which one did they mean?** `Vabriku 12` is five places — Tallinn, Viljandi and three in Rakvere. - **Does it exist at all?** `Toompuiestee 1` is not an address; ADS answers with 10, 11, 11a, 12 and no number 1. ## Vetted module (use this first) `ads.mjs` ships in https://kodanikukratt.ee/kratt-agent.tar.gz (plain ESM, Node 22+) and is re-verified against the live source: ```js import { show } from "./kratt.mjs"; import { findAddress } from "./ads.mjs"; const r = await findAddress("Vabriku 12"); show({ source: r.source, unique: r.unique, exact: r.exact }); // cite r.source show(r.candidates); // unique=false → SEVERAL real places match. Ask, or state which one you took. // exact=false → the address does not exist; these are its street's neighbours. const hit = r.candidates[0]; show({ ehr: hit.ehrCode, katastritunnus: hit.cadastralUnit, aadress: hit.address }); ``` - `findAddress(text)` returns candidates best-first, each merged from the building row and the cadastral row ADS returns separately for the same place, so one call carries **both** `ehrCode` and `cadastralUnit`. - Also exported: `addressAt(x, y)` (nearest address to an L-EST97 point), `apartments(text)`, `gazetteer(params)` for raw access. The endpoint below is the fallback if a module call throws. ## Access One public JSON endpoint. No key, no login, no rate limit published: ```text GET https://inaadress.maaamet.ee/inaadress/gazetteer?address={text}&results=20&features=EHITISHOONE,KATASTRIYKSUS ``` - `address` accepts free text (`Vabriku 12`), a full address (`Vabriku tn 12, Tallinn`) or a **cadastral identifier** (`78401:101:5214`) — the last is how you go from a plot back to its address. - `features` filters the object type. Valid values: `EHITISHOONE`, `KATASTRIYKSUS`, `EHAK`, `VAIKEKOHT`, `TANAV`. Request the first two together; `TANAV` and `EHAK` are area matches that crowd out real hits under `results`. - Reverse geocoding is `?x={easting}&y={northing}` **in L-EST97** — feed back the `viitepunkt_x` / `viitepunkt_y` a hit returned. WGS84 degrees return nothing rather than an error. - `appartment=1` adds an `appartments` array listing every unit in the building, each with its own ADS id. ## Retrieve Each hit carries the identifiers, the administrative hierarchy as both names and EHAK codes, and coordinates in both L-EST97 and WGS84: | field | is | | --- | --- | | `pikkaadress` | the official full address; `aadresstekst` is just the street and number | | `ads_oid` | the stable ADS object id (`EE…` building, `CU…` cadastral unit, `ER…` apartment) | | `tunnus` | **means a different thing per row**: the EHR code when `liikVal` is `EHITISHOONE`, the katastritunnus when it is `KATASTRIYKSUS` | | `ehakmk` / `ehakov` / `ehak` | county / municipality / settlement codes, alongside `maakond`, `omavalitsus`, `asustusyksus` | | `asum`, `sihtnumber` | neighbourhood (`Kalamaja asum`) and postcode | | `viitepunkt_x/_y`, `viitepunkt_b/_l` | reference point in L-EST97, then WGS84 latitude and longitude | | `unik` | `1` = a resolvable address, `0` = a partial object (a street, a secondary object with no code) | | `kvaliteet` | how well it matched — see below | `kvaliteet` is the field that separates an answer from a guess: - `tapne_lahiaadress` — the full address matched. - `tapne_nr` — street and house number matched but no settlement was given, so it matched in **every** town that has one. - `origtunnus` — matched by cadastral identifier, not by text. - `osaline` — **partial**: a prefix of what was asked. If every hit is `osaline`, the address asked for does not exist. The same place is returned once per object type, so `Vabriku tn 12, Tallinn` is two rows — one `EHITISHOONE`, one `KATASTRIYKSUS`. Merge them by `pikkaadress` before reporting, or you will report one address as two. ## Return Give the full `pikkaadress` you resolved to, the `katastritunnus` and EHR code when present, and the URL fetched. When more than one candidate matched, **say so and say which one you used** — do not silently take the first. When every candidate is `osaline`, report that the address was not found rather than answering about a neighbour. ## Where the rest of a property question comes from Resolve the address here, then hand the key on: - `cadastralUnit` → `land-register` for the register part, area and land use. Searching it by katastritunnus returns **one exact row**; searching the same place by address text returns the neighbours too. - `cadastralUnit` → `geospatial-open-data` for geometry, restrictions and map layers. - `ehrCode` → `construction-register` for the building, its permits and kasutusluba — keyed directly, skipping that register's own fuzzy address search. - The resolved address or district → `planning-decisions` for detail plans and public-display windows. - `omavalitsus` tells you which local authority the case belongs to, which is what decides where a letter goes. ## Limits - ADS is **address data only**: no owner, no price, no building detail, no encumbrance. It says what a place is called and what it is keyed by; everything else is another register. - Apartment notation in the query string does not work — `Sõle 23-15` returns nothing. Query the building and use `appartment=1` (or `apartments()`) for the units. - A settlement name alone (`Tallinn`) returns nothing under `EHITISHOONE,KATASTRIYKSUS`; those features are buildings and plots, not places. - Not every address has both keys. A plot with no building has no `ehrCode`; some buildings return no `cadastralUnit` in the same call. - Historical addresses are not returned by default, so an old address may legitimately miss. ## Verify - A retrieval counts only when the response has an `addresses` array with at least one element. **A no-match response has no `addresses` key at all** — it is `{"host": "…"}` — so `res.addresses.length` throws on a miss instead of reporting zero. - Before stating an address, check `kvaliteet` on the hit you used. Never state an `osaline` hit as the address that was asked for. - Before answering about "the" address, check whether more than one candidate came back. More than one is the normal case for a bare street and number, and picking silently is how an answer ends up about a different town. - Take `tunnus` as an EHR code only from a row whose `liikVal` is `EHITISHOONE`, and as a katastritunnus only from `KATASTRIYKSUS`. The field name is the same on both. ## Module API findAddress(text) -> {source, query, unique, exact, note, candidates}