Liigu sisu juurde

Statistika, teated ja muud andmed

Statistikaamet

Ametlik statistika ajaridadena koos tabelite metoodikaga Statistikaameti liidesest.

Statistikaamet avaldab ametliku statistika andmebaasis andmed.stat.ee, kus üks näitaja on üks tabel ja tabelit saab lugeda aastate, kvartalite või kuude kaupa. Iga tabel katab oma perioodi ja tabeli vahetumine tähendab tavaliselt ka metoodika vahetumist: kaks tabelit sama näitaja eri aastate kohta on kaks eri aegrida ja Statistikaamet ütleb oma metoodikalehel, mis aastast alates arvud omavahel võrreldavad on. Kuudest või kvartalitest aastakeskmist ei tohi ise arvutada, sest nii tuleb arv, mida Statistikaamet ise ei avalda.

Mida su agent peab oskama

POSTVajab vormipäringut. Vähemalt üks vajalik päring on vormi saatmine (POST). Küpsiseid ega turvamärgiseid vahele ei jää, aga ainult lehti tõmbav agent seda päringut teha ei saa.

Milline agent selle läbi teeb

Vastused, mis on siit tulnud

Juhend ise

Inglise keeles, sest seda loeb mudel.

Statistics Estonia API

Vetted module (use this first)

stat.mjs ships in https://kodanikukratt.ee/kratt-agent.tar.gz (plain ESM, Node 22+) and is re-verified against the live source. It hides the PXWeb POST contract and flattens json-stat2 into printable rows.

There are thousands of tables and this guide names none of them. Find the one the question needs; do not recall an id.

The method

  1. findTables(term) — search the catalogue by what the statistic is called. Every hit comes back with what it COVERS: periods, the period codes first and last, and annualFrom/annualTo, the years it publishes as ANNUAL values.
  2. Check the coverage against the period asked about, and check it on the right field. annualFrom/annualTo are numbers: compare them. first/last are period codes like "2022Q4" and do not compare — a table whose quarters end in 2022Q4 may still publish annual values, and last >= "2025" throws it away. A table that does not list the years you need is not an answer, and querying it for them returns HTTP 400.
  3. A current table that starts too late is not the end of the search. discontinued: true means the table is in the archive, not that it is wrong: it still answers for the years it covers, and earlier years usually exist nowhere else. Many archived quarterly or monthly tables also list bare YEARS, which the title does not say.
  4. Match the granularity the question asks for. An annual figure comes from a table with an annual span; a table of months or quarters is a different statistic. Never average months or quarters into a year yourself — that publishes a number Statistikaamet does not, and the office's own annual value is usually not the mean of its quarters. subPeriod names a second dimension that splits the year (Kuu, Kvartal); a table that has one holds only months or quarters, reports annualFrom: null however annual its year list looks, and a query must name that dimension too.
  5. tableMeta(id) for the exact variable and value codes, then tableRows(id, {query}). Codes differ between tables measuring the same thing, so read the contract of every table you query.
claim A table that does not list the years you need is not an answer, and querying it for them returns HTTP 400
# IA001 (consumer price index) lists 1999 onwards, so 1990 is a year it will
# never hold. PXWeb reads the JSON body whatever Content-Type is sent, which is
# why this posts JSON under the probe runner's form encoding.
POST https://andmed.stat.ee/api/v1/en/stat/IA001
body {"query":[{"code":"Aasta","selection":{"filter":"item","values":["1990"]}}],"response":{"format":"json-stat2"}}
expect-status 400
expect /Bad Request/
# The control is the identical query for 1999, the table's own first year. It
# must come back 200 json-stat2: a 400 from a table that had been withdrawn
# would otherwise read as the coverage rule working.
control POST https://andmed.stat.ee/api/v1/en/stat/IA001
body {"query":[{"code":"Aasta","selection":{"filter":"item","values":["1999"]}}],"response":{"format":"json-stat2"}}

findTables also PRINTS a line of its own, starting [stat.mjs], when the annual years it found are split between the live tables and the archive. That line is about the question you are answering — read it before choosing tables.

A series that changes table changes methodology

Two tables covering different years of "the same" indicator are two series. Their overlap years normally disagree, because the statistic was remeasured — that is usually why the old table was closed.

tableMethodology(id) fetches what Statistikaamet itself publishes about it (ESMS section 15.2, "Ajaline võrreldavus"): comparableSince is the year from which the office says its own figures are comparable. Quote that document — never a break you inferred. Then, in the answer itself:

  • Say that the methodology changed, name the year, and cite the ESMS page. An answer that lists both series without a word about the break is wrong even when every figure in it is right.
  • Give all the years the question asked for, labelled by which series each came from.
  • Never state a change, growth rate or trend measured across the join — not a year-on-year step over it, and not the total over the whole span. Both are artefacts of the remeasurement: they compare two different definitions and come out several points away from the truth.
  • The reader still asked how much it changed, so answer that within each series ("X → Y over 2021-2025"). That is a real number and the cross-break one is not.
import { show } from "./kratt.mjs";
import { findTables, tableMeta, tableRows, tableMethodology } from "./stat.mjs";

// 1. Find the table by the statistic's name, and read what it covers.
const found = await findTables("keskmine brutokuupalk");
show({ note: found.note, total: found.total });
show(found.tables.map(({ id, discontinued, timeVariable, subPeriod, first, last, annualFrom, annualTo }) => ({ id, discontinued, timeVariable, subPeriod, first, last, annualFrom, annualTo })));

// 2. Pick tables by the YEARS they list — numbers, never the period codes.
//    Whatever years the question needs, on both sides of the archive line.
const covers = (t, from, to) => t.annualFrom !== null && t.annualFrom <= from && t.annualTo >= to;
const t = found.tables.find((x) => covers(x, 2016, 2020)); // often an archived table

// 3. Read that table's own contract, then query it.
show((await tableMeta(t.id)).variables.map((v) => ({ code: v.code, values: v.values.slice(0, 5) })));
const { source, page, rows } = await tableRows(t.id, { query: { [t.timeVariable]: ["2016", "2017"] } });
show({ source, page, rows }); // `source` is the URL queried; `page` is where a person reads it

// 4. Taking the remaining years from a second table? Check the break before joining them,
//    then say in the answer what it says and give the growth within each series, not across.
show(await tableMethodology(t.id));
  • findTables(term, {limit}) returns up to 30 hits (60 max), split between live and archived tables, with total saying how many matched. Zero hits means the WORD is wrong, not that the source is down.
  • tableRows(tableId, {query}) POSTs the query and returns {source, page, rows} where rows are {<dimension label>: value label, …, value}. query is a plain object like {Aasta: ["2025"]}; omitted dimensions come back aggregated by the table's own default.
  • Link page, and never build a stat.ee path yourself. page is https://andmed.stat.ee/<lang>/stat/<tableId>, which PXWeb redirects to the table's own page.
  • tableMeta(tableId) returns the variable contract. Variable codes may be non-ASCII (Vanuserühm) and must be sent exactly as returned.
  • Also exported: browse(path) (walk the folder tree), queryTable() (raw json-stat2) and toRows(dataset).
  • Confirm units, reference period and suppression markers before analysis.

The endpoints below are the fallback if a module call throws.

Access

Public PXWeb JSON API. No authentication. Use https://andmed.stat.ee/api/v1/en/stat for English or replace en with et for Estonian.

Retrieve

  1. Search for a table with GET /api/v1/en/stat?query=<term>; each hit is {id, path, title, published} and a path under Lepetatud_tabelid is an archived table. GET /api/v1/en/stat with no query lists the top folders; descend by appending returned folder IDs.
  2. Fetch a table contract with GET /api/v1/en/stat/{TABLE_ID}. It returns title and variables; use each variable's code and allowed values in the query. The time variable's values are exactly what the table covers.
  3. POST the query to the same table URL:
{
  "query": [
    {"code": "Aasta", "selection": {"filter": "item", "values": ["2025"]}}
  ],
  "response": {"format": "json-stat2"}
}
  1. Request with Content-Type: application/json. Use csv, json-stat2, or another format accepted by the table service.
  2. When methodology matters, tableMethodology(TABLE_ID) walks the published chain: search for the id to get its path, then read the table's own web page at https://andmed.stat.ee/en/stat/<path with / replaced by __, leading __ stripped>/<ID>, which links esms-metadata/<n>. That number addresses the ESMS document at https://www.stat.ee/en/find-statistics/methodology-and-quality/esms-metadata/<n> — in Estonian the link reads esms-metaandmed/<n> and the document lives at https://www.stat.ee/et/avasta-statistikat/metoodika-ja-kvaliteet/esms-metaandmed/<n>. Section 15.2 is comparability over time. Cite that URL when you state a methodology break.

Working sample: GET https://andmed.stat.ee/api/v1/en/stat/IA001, then POST the payload above. The response is JSON-stat 2 with table ID IA001, year 2025, and a numeric value.

Return

Preserve the table ID, source and update metadata, dimension codes and labels, selected values, units/decimals, observations, exact POST payload, and retrieval time. When an answer spans two tables, say which years came from which.

Limits

  • The figures come out of a POST, and only a POST. A GET on the table URL hands back the variable contract, never the observations, so an agent that can only issue GET requests can find a table here and describe what it covers but cannot read a value out of it — that is a limit on what we can retrieve for such a reader, not on the service. The query POST is stateless: no cookie, no token, no prior request, and the body is accepted under either a JSON or a form content type (checked 2026-08-02).
  • The API root itself returns 404; include the language and database path.
  • Table IDs are not enough to construct a query: always read the current variable contract first.
  • Confirm units, seasonal adjustment, reference period, and suppression markers before analysis.
  • Variable codes may contain non-ASCII characters (e.g. Vanuserühm); send them exactly as returned by the table contract.
  • Dimension values are codes too, and the response echoes their labels instead — send "1", not "Kokku", or the query 400s.
  • tableRows(id, {...}) silently returns the WHOLE table if the filter is not nested under query. It must be tableRows(id, { query: {...} }); the wrong shape produces no error, just every row.
  • Discontinued tables still return contracts and data. Check the periods, not the title: a table titled "(2002–2022, KVARTALID)" can list bare years as well as quarters.
  • The reverse trap is the older tables that split time across TWO dimensions — a year variable holding bare years, plus Kuu or Kvartal. Every cell is then a month or a quarter and the table publishes no annual value at all. subPeriod names that dimension and annualFrom is null for such a table; a query must select from both.
  • Property prices: per-transaction data is not published anywhere public (tehingud.maaamet.ee no longer resolves and the current Maa-amet service is behind auth). A wage-versus-property question is answered from the published dwelling price index and the wage tables — search for both.

Verify

Require HTTP 200 JSON, variables in the table contract, and matching id, dimension, size, and value fields in a JSON-stat response. A portal HTML page or API-base 404 is not successful retrieval. A search that matches nothing returns [], so an empty result is a real answer about the term, not a failure.

Module API

findTables(query) -> {source, total, shown, truncated, note, tables} tableRows(tableId, {query}) -> {source, page, rows} tableMeta(tableId) -> {title, variables} tableMethodology(tableId) -> {source, page, esmsId, comparableSince, comparability, note}