Hagstofan (Statistics Iceland)
SkillDev toolsHagstofan / 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.
No other account needed.
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
| Category | Path | Description |
|---|---|---|
| Population | Ibuar/ | Demographics, migration, families |
| Economy | Efnahagur/ | GDP, national accounts, trade |
| Prices | Verdlag/ | CPI, price indices |
| Industries | Atvinnuvegir/ | Fishing, agriculture, tourism |
| Society | Samfelag/ | Education, health, crime |
| Environment | Umhverfi/ | Climate, waste, energy |
| Historical | Sogulegar/ | Long-term historical series |
Key Datasets
Foreign Trade (Tollskrá)
Path: Efnahagur/utanrikisverslun/1_voruvidskipti/
| Table | Period | Description |
|---|---|---|
03_inntollskra/UTA03803.px | 2023-2025 | Imports by tariff, chapters 84-99 (monthly) |
06_tollskrarnumereldra/UTA13813.px | 2017-2022 | Imports by tariff, chapters 84-99 (annual) |
06_tollskrarnumereldra/UTA13823.px | 2012-2016 | Imports by tariff, chapters 84-99 (annual) |
Units available: kg (weight), cif (value ISK), vEin (units)
GDP / National Accounts
Path: Efnahagur/thjodhagsreikningar/landsframl/1_landsframleidsla/
| Table | Description |
|---|---|
THJ01103.px | GDP at constant prices 1980-2024 |
THJ01000.px | Key GDP figures 1945-2024 |
THJ01401.px | GDP per capita 1980-2024 |
Population
Path: Ibuar/mannfjoldi/
| Table | Description |
|---|---|
1_yfirlit/Yfirlit_mannfjolda/MAN00000.px | Key population figures 1703-2025 |
1_yfirlit/Yfirlit_mannfjolda/MAN00101.px | Population by sex and age |
State treasury — ríkissjóður fiscal aggregates
Path: Efnahagur/fjaropinber/fjarmal_rikissjods/
| Table | Period | Description |
|---|---|---|
THJ05211.px | 1980-2025 | One-stop state budget balance: tekjur, gjöld, tekjuafgangur/-halli (m.kr), % af VLF, real-2025 prices |
THJ05221.px | 1998-2025 | Full ESA income statement: rekstrartekjur, rekstrarútgjöld, tekjuafgangur, capital rows |
THJ05222.px | 1998-2025 | Rekstrarreikningur ríkissjóðs |
THJ95200.px | 2004-2014 | Cash-basis (greiðslugrunnur) monthly state accounts — month code 0 = annual total |
THJ05281.px | 1998-2025 | Monetary 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:
| Code | Category | Description |
|---|---|---|
| 87116010 | ebikes* | Pre-2020: E-bikes + e-scooters combined |
| 87116011 | ebikes | E-bikes (pedal-assist ≤25 km/h) |
| 87116012 | escooters | E-scooters (kick scooters with motor) |
| 87116015 | ebikes | Other small EVs ≤25 km/h |
| 87116090 | ebikes | Other electric motorcycles |
| 87120000 | bikes | Regular bicycles (no motor) |
*Discontinued 2020, split into 87116011/12/15
Data Caveats
-
Encoding: CSV output uses UTF-8 with BOM. DuckDB may need
ignore_errors=true -
Archive vs Current:
- Archive tables (
eldra efni) have annual aggregates - Current tables have monthly data by country
- Archive tables (
-
Classification changes: Tariff codes change over time. Always check when series definitions shifted.
-
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 -
Icelandic headers: Column names are in Icelandic. Common terms:
Ár= YearMánuður= MonthTollskrárnúmer= Tariff codeCif verð krónur= CIF value in ISKViðbótar magneining= Additional unit (count)
-
PX-Web query gotchas:
"filter": "all"returns HTTP 400 Bad Request — always enumeratevaluesexplicitly ("filter": "item").- json-stat2 responses keep
id/sizeat the top level, not underdimension(dimension keys are plain dimension names). - The response's category
labelmay echo the requested selection codes rather than the human labels — when you need row labels, read the metadatavalueTextsin the same order as itsvalues. - Some tables use a "total" code: THJ95200 has month code
0= annual total (months are1–12).
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:
| Field | Source | Meaning |
|---|---|---|
last_updated | px LAST-UPDATED | The real publication stamp. json-stat2 updated is the table's creation date (VIN01002 says 2017, MAN01400 says 2018) — never use it. |
notes | px NOTE, NOTE[en] | Table caveats as paragraphs, keyed by language. Revision dates, definitional breaks, methodology. |
value_notes | px VALUENOTE | Per-value caveats, keyed by value label (not code). Only returned for values in the selection. |
links | hrefs in notes | Methodology PDFs (greinargerð) on hagstofas3bucket. |
units, refperiod, creation_date | px header | |
fetched_at, raw_sha256 | this 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