Hagstofan (Statistics Iceland)

SkillDev tools

Hagstofan / Statistics Iceland PX-Web API. Use for Icelandic national statistics, GDP, CPI, population, foreign trade by tariff code, wages, education, health.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Hagstofan (Statistics Iceland) skill

What this skill tells your AI

The instructions your AI receives, as published by jokull/icelandic-data in .agents/skills/hagstofan/SKILL.md and read by ahel’s review.

Requires: Tier 0 (core).

Official statistics for Iceland via PX-Web API.

API

Base URL: https://px.hagstofa.is/pxis/api/v1/is/

No authentication required. Returns JSON metadata (GET) or CSV/JSON data (POST).

Fetching Data

Browse categories

curl -s "https://px.hagstofa.is/pxis/api/v1/is/" | jq
curl -s "https://px.hagstofa.is/pxis/api/v1/is/Efnahagur/" | jq

Get table metadata

curl -s "https://px.hagstofa.is/pxis/api/v1/is/{path}/{TABLE}.px" | jq

Fetch all data as CSV

curl -X POST "https://px.hagstofa.is/pxis/api/v1/is/{path}/{TABLE}.px" \
  -H "Content-Type: application/json" \
  -d '{"query": [], "response": {"format": "csv"}}'

Fetch filtered data

curl -X POST "https://px.hagstofa.is/pxis/api/v1/is/{path}/{TABLE}.px" \
  -H "Content-Type: application/json" \
  -d '{
    "query": [
      {"code": "Tollskrárnúmer", "selection": {"filter": "item", "values": ["87116011", "87120000"]}}
    ],
    "response": {"format": "csv"}
  }'

API Categories

CategoryPathDescription
PopulationIbuar/Demographics, migration, families
EconomyEfnahagur/GDP, national accounts, trade
PricesVerdlag/CPI, price indices
IndustriesAtvinnuvegir/Fishing, agriculture, tourism
SocietySamfelag/Education, health, crime
EnvironmentUmhverfi/Climate, waste, energy
HistoricalSogulegar/Long-term historical series

Key Datasets

Foreign Trade (Tollskrá)

Path: Efnahagur/utanrikisverslun/1_voruvidskipti/

TablePeriodDescription
03_inntollskra/UTA03803.px2023-2025Imports by tariff, chapters 84-99 (monthly)
06_tollskrarnumereldra/UTA13813.px2017-2022Imports by tariff, chapters 84-99 (annual)
06_tollskrarnumereldra/UTA13823.px2012-2016Imports by tariff, chapters 84-99 (annual)

Units available: kg (weight), cif (value ISK), vEin (units)

GDP / National Accounts

Path: Efnahagur/thjodhagsreikningar/landsframl/1_landsframleidsla/

TableDescription
THJ01103.pxGDP at constant prices 1980-2024
THJ01000.pxKey GDP figures 1945-2024
THJ01401.pxGDP per capita 1980-2024

Population

Path: Ibuar/mannfjoldi/

TableDescription
1_yfirlit/Yfirlit_mannfjolda/MAN00000.pxKey population figures 1703-2025
1_yfirlit/Yfirlit_mannfjolda/MAN00101.pxPopulation by sex and age

State treasury — ríkissjóður fiscal aggregates

Path: Efnahagur/fjaropinber/fjarmal_rikissjods/

TablePeriodDescription
THJ05211.px1980-2025One-stop state budget balance: tekjur, gjöld, tekjuafgangur/-halli (m.kr), % af VLF, real-2025 prices
THJ05221.px1998-2025Full ESA income statement: rekstrartekjur, rekstrarútgjöld, tekjuafgangur, capital rows
THJ05222.px1998-2025Rekstrarreikningur ríkissjóðs
THJ95200.px2004-2014Cash-basis (greiðslugrunnur) monthly state accounts — month code 0 = annual total
THJ05281.px1998-2025Monetary assets and debt incl. almannatryggingar

The ríkisreikningur (Fjársýsla) API only goes back to 2015 — pre-2015 state budget balance (e.g. the 2008–2011 crisis deficits) comes from these tables. scripts/hagstofan_rikissjod.py fetch builds data/processed/rikissjod_balance.csv (all years, wide, m.kr).

Tariff Codes (Bikes/E-bikes)

Classification changed in 2020:

CodeCategoryDescription
87116010ebikes*Pre-2020: E-bikes + e-scooters combined
87116011ebikesE-bikes (pedal-assist ≤25 km/h)
87116012escootersE-scooters (kick scooters with motor)
87116015ebikesOther small EVs ≤25 km/h
87116090ebikesOther electric motorcycles
87120000bikesRegular bicycles (no motor)

*Discontinued 2020, split into 87116011/12/15

Data Caveats

  1. Encoding: CSV output uses UTF-8 with BOM. DuckDB may need ignore_errors=true

  2. Archive vs Current:

    • Archive tables (eldra efni) have annual aggregates
    • Current tables have monthly data by country
  3. Classification changes: Tariff codes change over time. Always check when series definitions shifted.

  4. Wide format: PX tables return time periods as columns. Unpivot to tidy format:

    # Polars unpivot
    df.unpivot(index="Category", variable_name="month", value_name="value")
    
    -- DuckDB UNPIVOT
    UNPIVOT table ON * EXCLUDE (category) INTO NAME month VALUE passengers
    
  5. Icelandic headers: Column names are in Icelandic. Common terms:

    • Ár = Year
    • Mánuður = Month
    • Tollskrárnúmer = Tariff code
    • Cif verð krónur = CIF value in ISK
    • Viðbótar magneining = Additional unit (count)
  6. PX-Web query gotchas:

    • "filter": "all" returns HTTP 400 Bad Request — always enumerate values explicitly ("filter": "item").
    • json-stat2 responses keep id / size at the top level, not under dimension (dimension keys are plain dimension names).
    • The response's category label may echo the requested selection codes rather than the human labels — when you need row labels, read the metadata valueTexts in the same order as its values.
    • Some tables use a "total" code: THJ95200 has month code 0 = annual total (months are 112).

Generic labelled query CLI — start here for any table

uv run python scripts/hagstofan_query.py list                      # root catalogue
uv run python scripts/hagstofan_query.py list Ibuar/buferlaflutningar/buferlaflmillilanda
uv run python scripts/hagstofan_query.py list  <path>/MAN01400.px  # variables + full notes + ready-to-run fetch
uv run python scripts/hagstofan_query.py notes <path>/MAN01400.px  # the notes, readable
uv run python scripts/hagstofan_query.py fetch <path>/MAN01400.px --filters '{"Kyn":["0"]}' --since 2015 --out out.json

Browse it like hypermedia: every listing item carries path (pass it straight back) and next (the command to run). A category listing enriches each table with last_updated, notes_count, value_notes_count, a note_preview and methodology links, so caveats are in view before anything is fetched. A table listing returns the variables plus the full notes and an example fetch.

fetch writes long-form records with original dimension codes and labels, preserves nulls, and puts the table's own documentation next to rows:

FieldSourceMeaning
last_updatedpx LAST-UPDATEDThe real publication stamp. json-stat2 updated is the table's creation date (VIN01002 says 2017, MAN01400 says 2018) — never use it.
notespx NOTE, NOTE[en]Table caveats as paragraphs, keyed by language. Revision dates, definitional breaks, methodology.
value_notespx VALUENOTEPer-value caveats, keyed by value label (not code). Only returned for values in the selection.
linkshrefs in notesMethodology PDFs (greinargerð) on hagstofas3bucket.
units, refperiod, creation_datepx header
fetched_at, raw_sha256this script

Only the native px response format serialises those headers — json-stat2 gives note: null — so fetch makes one json-stat2 request for the data and one px request for the header, with the same selection. The px body is UTF-8 with a BOM even though the HTTP header says Windows-1252; the script decodes by the file's own CODEPAGE.

Vintage. Every fetch appends a line to data/raw/hagstofan/query/index.jsonl (fetched_at, path, filters, since, last_updated, raw_sha256). The vintage of a dataset is (path, last_updated); two fetches of the same table with different hashes are a revision, and both raw responses are still on disk:

uv run python scripts/sql.py "SELECT path, last_updated, count(DISTINCT raw_sha256) AS versions, min(fetched_at), max(fetched_at)
  FROM read_json_auto('data/raw/hagstofan/query/index.jsonl') GROUP BY 1,2 ORDER BY 1,2"

Curated scripts carry the same documentation. Every curated Hagstofan script (hagstofan_cpi.py, hagstofan_income.py, hagstofan_population_wages.py, hagstofan_rikissjod.py, hagstofan.py, housing_completions.py, income_distribution.py) writes a data/processed/{output stem}.meta.json sidecar next to its primary output — a JSON object keyed by table code with path, last_updated, fetched_at, units, refperiod, notes, value_notes and links, fetched with the same selection as the data — and appends every data POST to the shared index.jsonl above, so the vintage query covers curated outputs too. Each run prints one stderr breadcrumb per table (VIS01300: last updated 2026-08-27T09:00, 3 notes); a header failure is printed and skipped, never fatal to the data.

Known note quirks: some English notes have an unterminated <A HREF=... TARGET=_ anchor upstream, so the sentence after the link runs on; the script strips the tag and keeps the href in links.

Current VIS01300 uses CP01–CP13. CP12 is insurance/financial services, CP13 personal care/social protection/other services. hagstofan_cpi.py keeps CP12/CP13 current-only because old IS12 is not comparable. It preserves previous output and exits nonzero if any fetch fails. Other historical category concordances still need care.

Signals

GitHub stars
54
Forks
4
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
hagstofan
Source
github.com/jokull/icelandic-data