L logiover
data · Jul 9, 2026 · 7 min read

How to Scrape Eurostat Data: EU Statistics & Indicators

Pull any Eurostat dataset as flat rows — GDP, population, employment, trade — with geo and time filters. How the JSON-stat dissemination API works, no key needed.

Eurostat is the European Union’s statistical office, and it publishes authoritative data on GDP, population, employment, trade, inflation, energy, and hundreds of other topics across EU member states and partner countries. The catch: the raw dissemination API returns JSON-stat, a compact multi-dimensional format that packs an entire dataset’s dimensions into nested index arrays. It’s machine-readable, but it’s not a table — you can’t drop it into a spreadsheet or a dataframe without decoding it first. This guide covers how the Eurostat API is shaped, how to flatten it into rows, and where the practical limits are.

What’s worth extracting

The scraper produces one flat row per non-null data cell, with about 12 fields. Grouped logically:

  • Dataset identitydataset (the Eurostat code, e.g. demo_pjan) and datasetLabel (the human-readable title).
  • Geographygeo (ISO 2-letter country/region code like DE or the aggregate EU27_2020) and geoLabel (the name, e.g. Germany).
  • Timetime, which adapts to the dataset’s frequency: 2023 for annual, 2023-Q1 for quarterly, 2023-01 for monthly.
  • The measurementvalue (the numeric value, returned as a string so no precision is lost), unit (code like NR, PC, EUR_HAB), and unitLabel (Number, Percentage, Euro per inhabitant).
  • Metadatafreq (frequency code: A annual, Q quarterly, M monthly), status (Eurostat flag: b break, e estimated, p provisional, empty = normal), and updatedAt (ISO date Eurostat last updated the dataset).
  • Everything elseextraDimensions, a JSON object carrying any dataset-specific dimensions (age, sex, NACE industry code, and so on) with their codes and labels.

That extraDimensions field is the key to Eurostat’s structure: the four “standard” dimensions are geo, time, unit, and freq, but a dataset like population-by-age-and-sex has additional age and sex dimensions, and those get serialized there so you can filter downstream.

How the Eurostat dissemination API works

The scraper connects directly to the official Eurostat dissemination API, which is fully public and keyless:

https://ec.europa.eu/eurostat/api/dissemination/statistics/1.0/data/{dataset}

Swap {dataset} for a dataset code and the API returns the data in JSON-stat. Under the hood, JSON-stat describes each dimension (its categories, their order, their labels) and then stores the values in a single flat array indexed by the Cartesian product of all dimensions. To turn that into a table you have to read the dimension metadata and expand the product back out — which is exactly what the actor does automatically: it reads the dimension metadata, performs the Cartesian expansion, and emits one row per valid cell.

Country and time filters are applied as query parameters on the API request, so only the slice you ask for is transferred — that’s a real performance lever, not just a convenience.

The actor has two modes:

  • datasetData — fetch and flatten a specific dataset.
  • catalog — fetch the full Eurostat Table of Contents (over 6,000 datasets organized in a hierarchical theme tree) so you can discover dataset codes without leaving the actor.

A typical filtered extraction looks like this:

{
  "mode": "datasetData",
  "dataset": "demo_pjan",
  "geoFilter": ["DE", "FR", "IT", "ES", "PL"],
  "timeFrom": "2015",
  "timeTo": "2023",
  "maxResults": 0
}

Here maxResults: 0 means no cap. To discover codes first, run catalog mode:

{
  "mode": "catalog",
  "maxResults": 500
}

Each catalog row carries a code, title, type (folder / table / dataset), data range, and value count — use it to find the code you want, then switch to datasetData.

Run the Eurostat Data Scraper on Apify — pick a dataset code, add optional geo and time filters, get clean flat rows with country, period, value, unit, and every extra dimension. No API key, no login. Export to CSV, JSON or Excel.

The access reality

There’s no anti-bot wall — Eurostat’s API is designed for programmatic access — but the data itself has properties you have to plan around.

  • Datasets can be enormous. A single dataset spans dozens of countries and decades of periods. Monthly series are the ones to watch: ei_cphi_m (harmonised consumer prices, monthly) contains over a million cells across 30+ countries back to 1996. Without filters, one run can stream millions of rows.
  • Filter at the source, not downstream. Because geoFilter, timeFrom, and timeTo are pushed into the API query, filtering shrinks the response payload before transfer — cutting both run time and memory. Passing geoFilter: ["DE","FR"] and timeFrom: "2010" is far cheaper than pulling everything and discarding rows later.
  • Null cells are skipped for you. Eurostat uses null where a country didn’t report a given period. The actor excludes those entirely, so every row you receive has a real numeric value — no empty cells to clean out.
  • Data is not real-time. Eurostat publishes on a release calendar: annual data updates once or twice a year, quarterly every three months, monthly indicators within roughly 30–60 days of the reference period. The updatedAt field on each row tells you when the dataset was last refreshed, so you can gauge cadence and schedule accordingly.

One structural limit: the current version fetches one dataset per run. To pull several, start a run per dataset (or use Apify’s batch run API).

Example output

A single row — population of Germany in 2023 from demo_pjan:

{
  "dataset": "demo_pjan",
  "datasetLabel": "Population on 1 January by age and sex",
  "geo": "DE",
  "geoLabel": "Germany",
  "time": "2023",
  "value": "83118501",
  "unit": "NR",
  "unitLabel": "Number",
  "freq": "A",
  "status": "",
  "extraDimensions": "{\"age\":{\"code\":\"TOTAL\",\"label\":\"Total\"},\"sex\":{\"code\":\"T\",\"label\":\"Total\"}}",
  "updatedAt": "2024-09-25T11:00:00+0200"
}

Notice extraDimensions is a JSON string — the age and sex dimensions with their codes and labels are serialized inside it. Parse that string in your pipeline when you need to filter by, say, a specific age band. The status here is empty, meaning a normal confirmed value; a neighboring row for France might carry "p" (provisional).

Typical use cases

  • Economics & research — pull time-series EU data for modelling, regression, or academic papers without clicking through the Eurostat web interface. Population by age/sex from demo_pjan, or annual GDP from nama_10_gdp.
  • Data journalism — track EU-wide trends in employment, cost of living, or energy prices across many countries and years for a story.
  • Business analysis — compare GDP, purchasing power, or trade flows across EU markets to inform market-entry decisions.
  • Policy & think tanks — monitor indicator updates for policy briefs and advocacy, using updatedAt to catch new releases.
  • Data engineering — build dashboards and pipelines that need fresh EU statistics on a schedule; a monthly cron keeps a Google Sheet of the latest HICP or employment figures current.

Common starting datasets from the docs: demo_pjan (population, back to 1960, with age/sex breakdowns), nama_10_gdp / namq_10_gdp (annual / quarterly GDP), prc_hicp_midx (harmonised consumer price indices), and lfsa_egan (employment).

Cost / effort math

Writing your own JSON-stat decoder is the kind of task that looks like an afternoon and turns into a week. You have to read the dimension metadata, reconstruct the Cartesian index ordering exactly right (get the dimension order wrong and every value maps to the wrong country-year), handle the status flags, thread the geo/time filters into the query string, and page through datasets large enough to blow past memory. The decode is fiddly and the failure mode is silent — misaligned data that looks plausible.

The managed actor runs pay-per-result: you pay only for the rows it actually produces, a fraction of a cent each. A country-filtered slice (say five countries across ten years, annual) is a few hundred rows and costs a rounding error; even a large unfiltered monthly dataset stays cheap because you’re billed on output, not compute time. And the JSON-stat expansion is already solved and tested against real datasets.

Common pitfalls

  • value is a string. It’s returned as a string to preserve precision. Cast it to a number in your pipeline before you do arithmetic — don’t assume it arrives numeric.
  • extraDimensions is a serialized JSON string, not an object. Parse it before you can filter on age, sex, nace_r2, or indic_na. Treating it as a plain string will silently break downstream filters.
  • Status flags change the meaning of a value. p = provisional (may be revised), e = estimated, b = break in series (methodology changed), c = confidential. If you need only confirmed final data, keep rows where status is empty.
  • Unfiltered monthly datasets are huge. Pulling ei_cphi_m or similar with no geo/time filter can mean millions of cells and a long run. Always filter monthly series unless you genuinely need the whole thing.
  • One dataset per run. Don’t expect to merge several dataset codes in a single call — schedule or batch a run per code.
  • Country aggregates sit alongside members. Codes like EU27_2020 and EA19 appear as geo values right next to individual countries. Decide whether you want aggregates or members and filter accordingly, or you’ll double-count when summing.
  • Attribution is required. Eurostat’s copyright policy permits free reuse — including commercial — but asks that you cite Eurostat as the source in derived work.

Wrapping up

Eurostat’s API is public and keyless, but JSON-stat’s packed multi-dimensional format is the wall between you and a usable table — the value is in flattening it correctly with the dimension order intact. Use catalog mode to find your dataset code, filter geo and time at the source to keep runs cheap, and watch the status flags and updatedAt to know exactly what you’re modelling against. If you’d rather not maintain a JSON-stat decoder, the managed actor already handles the expansion and filtering.

Open the Eurostat Data Scraper on Apify — one run turns any Eurostat dataset code into thousands of clean flat rows: country, period, value, unit, and every dimension. No API key, no login, export to CSV / JSON / Excel. Start on Apify’s free monthly credit.

Related guides