# PxWeb Statistics Extractor (`datamule/pxweb-statistics-extractor`) Actor

Point at ANY PxWeb statistical service (SCB Sweden, StatFi Finland, Norway, Iceland…): name a table and get flat, fully labeled statistics — one row per cell. Decodes index-encoded JSON-stat2, joining every dimension to its code + name. Discovery walks the subject tree. Pay per record.

- **URL**: https://apify.com/datamule/pxweb-statistics-extractor.md
- **Developed by:** [Datamule](https://apify.com/datamule) (community)
- **Categories:** Developer tools
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## PxWeb Statistics Extractor

**Point at ANY PxWeb statistical service and get flat, fully-labeled statistics — one row per cell.**

PxWeb (the open-source PC-Axis dissemination platform maintained by Statistics Sweden) is
deployed by dozens of national and regional statistical offices — **Statistics Sweden (SCB),
Statistics Finland (StatFi), Norway, Iceland, Greenland** and many more. Every one of them
speaks the **identical REST grammar**: navigate a subject tree → GET a table's metadata →
POST a dimension query → receive a compact **JSON-stat2** dataset.

Most scrapers re-implement one country at a time. This actor is the **generic decoder**: point
it at a PxWeb API root, name a table (or let it discover them), and it turns the compact,
index-encoded JSON-stat2 into flat rows — **one row per statistical cell**, every dimension
decoded to both its **code** and its **human-readable name**.

### The value is the decode

A raw JSON-stat2 dataset is just a flat number array plus a shape:

```json
{ "id": ["Region","Kon","ContentsCode","Tid"], "size": [1,2,1,1],
  "value": [5312519, 5239188], "dimension": { ...category index + label maps... } }
```

That array is meaningless without joining every linear index back to its per-dimension
category. This actor does exactly that — it walks `value[]` in row-major order, maps each
linear index to its per-dimension category position, and emits real columns:

| Region | Region\_label | Kon | Kon\_label | metric | metricLabel | timePeriod | value | unit |
|--------|--------------|-----|-----------|--------|-------------|-----------|-------|------|
| 00 | Sweden | 1 | men | BE0101N1 | Population | 2023 | 5312519 | number of persons |
| 00 | Sweden | 2 | women | BE0101N1 | Population | 2023 | 5239188 | number of persons |

### Input

| Field | Description |
|-------|-------------|
| **serviceRoot** (required) | The PxWeb REST API root, e.g. `https://api.scb.se/OV0104/v1/doris/en/ssd` (SCB Sweden) or `https://pxdata.stat.fi/PxWeb/api/v1/en/StatFin` (StatFi Finland). |
| **tablePath** | Path (relative to the root) to a leaf `.px` table to query, e.g. `BE/BE0101/BE0101A/BefolkningNy` or `synt/12dj.px`. Leave empty or point at a navigation level to **discover** instead. |
| **query** | Optional explicit dimension selection. Simple form `{"Region": ["00"], "Kon": ["1","2"], "Tid": ["2023"]}` or the native PxWeb array. Leave empty to **auto-select** (see below). |
| **lastNPeriods** | When auto-selecting, how many of the most recent time periods to fetch. Default 1. |
| **discoveryRecurse** / **maxDepth** | In discovery mode, walk the subject tree depth-first and emit every leaf table found. |
| **maxRecords** | Cap on rows emitted (each row = one billable record). |
| **bearer** / **extraHeaders** | Optional auth for gated services. Never required for public dissemination, never logged. |

#### Auto-query (no `query` supplied)

The actor builds a small, always-valid query from the table's metadata: the **last N periods**
of the time dimension, **all values** of small mandatory dimensions, and it **aggregates over
eliminable dimensions** (so you get the grand total instead of a combinatorial cell explosion).
This returns real data on any table without you having to know its dimensions up front.

### Modes (auto-selected)

- **Data** — the path points at a leaf table → GET metadata → POST query → decode JSON-stat2
  into one labeled row per cell.
- **Discovery** — the path points at a navigation level (or is empty) → list the subject-tree
  nodes so you can find tables. Turn on **Recurse** to walk levels and emit leaf tables with
  their full paths.

### Output

One flat row per cell (data mode) or per navigation node (discovery mode). Every dimension
becomes a `{CODE}` + `{CODE}_label` column pair; canonical `timePeriod` / `metric` /
`metricLabel` / `value` / `unit` / `decimals` columns are promoted from the JSON-stat2 roles;
`source`, `label`, `updated`, `tablePath` provenance is stamped on every row; and a lossless
`_raw` object keeps the exact cell coordinates, codes, labels and value.

Empty results (a query that matches nothing, an empty table) return **0 records and a clean
exit** — never a crash, never fabricated data.

### Pricing

Pay-per-event: one **record** charge per emitted row.

### Notes

- Two PxWeb dialects exist: the classic grammar (`/PxWeb/api/v1/{lang}/...` and SCB's `doris`
  variant) is fully supported; the Statbank REST variant (`api.statbank.dk/v1/...`) is
  best-effort for navigation.
- PxWeb servers cap the number of cells per query. If a selection exceeds the cap the actor
  automatically retries with a reduced selection.
- JSON-stat2 is requested explicitly (`format: json-stat2`) and decoded by its `size`/`id`/
  `dimension` shape.
- No authentication is needed for public statistics dissemination.

# Actor input Schema

## `serviceRoot` (type: `string`):

The REST API root of ANY PxWeb statistical service. Examples: https://api.scb.se/OV0104/v1/doris/en/ssd (Statistics Sweden) or https://pxdata.stat.fi/PxWeb/api/v1/en/StatFin (Statistics Finland). Also works with Norway, Iceland, Greenland and other PxWeb / PC-Axis dissemination platforms. No trailing slash needed.

## `tablePath` (type: `string`):

The path, relative to the API root, to a leaf .px table to query, e.g. BE/BE0101/BE0101A/BefolkningNy (SCB Sweden population) or synt/12dj.px (StatFi births). Leave EMPTY (or point at a navigation level like BE) to DISCOVER: the actor lists the subject-tree nodes there so you can find tables. When it points at a leaf table the actor GETs its metadata and queries it.

## `query` (type: `object`):

Optional explicit selection of which dimension values to fetch. Two forms: a simple mapping {"Region": \["00"], "Kon": \["1", "2"], "Tid": \["2023"]}, or the native PxWeb array \[{"code": "Region", "selection": {"filter": "item", "values": \["00"]}}]. Leave EMPTY to auto-select: all values of small mandatory dimensions, the last N periods of the time dimension, and aggregate over eliminable dimensions (keeps the cell count small and valid on any table).

## `lastNPeriods` (type: `integer`):

When auto-selecting (no explicit query above), how many of the most recent time periods to fetch per series. Default 1 (the latest). Ignored when you supply an explicit query.

## `discoveryRecurse` (type: `boolean`):

Discovery only (when the path points at a navigation level, not a leaf table). When ON, walk the navigation levels depth-first (bounded by Max depth) and emit every leaf .px table found, each with its full path. When OFF, list only the immediate child nodes.

## `maxDepth` (type: `integer`):

Discovery recursion depth ceiling (only used with "Recurse"). Default 3. Higher values walk deeper subject trees but cost more navigation requests.

## `maxRecords` (type: `integer`):

Optional cap on the number of rows emitted (cells in data mode, nodes/tables in discovery mode). Each emitted row is one billable record. Leave empty to emit everything the query returns.

## `bearer` (type: `string`):

Optional bearer token for gated PxWeb services (sent as Authorization: Bearer \*\*\*). Not required for public dissemination services (SCB, StatFi…). Never logged.

## `extraHeaders` (type: `object`):

Optional extra HTTP headers as a JSON object, e.g. {"Ocp-Apim-Subscription-Key": "..."} for gated services. Not required for public APIs. Header values are never logged.

## Actor input object example

```json
{
  "serviceRoot": "https://api.scb.se/OV0104/v1/doris/en/ssd",
  "tablePath": "BE/BE0101/BE0101A/BefolkningNy",
  "lastNPeriods": 1,
  "discoveryRecurse": false,
  "maxDepth": 3
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "serviceRoot": "https://api.scb.se/OV0104/v1/doris/en/ssd",
    "tablePath": "BE/BE0101/BE0101A/BefolkningNy"
};

// Run the Actor and wait for it to finish
const run = await client.actor("datamule/pxweb-statistics-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "serviceRoot": "https://api.scb.se/OV0104/v1/doris/en/ssd",
    "tablePath": "BE/BE0101/BE0101A/BefolkningNy",
}

# Run the Actor and wait for it to finish
run = client.actor("datamule/pxweb-statistics-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "serviceRoot": "https://api.scb.se/OV0104/v1/doris/en/ssd",
  "tablePath": "BE/BE0101/BE0101A/BefolkningNy"
}' |
apify call datamule/pxweb-statistics-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datamule/pxweb-statistics-extractor"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Xc4aKajnxkNb3aIES/builds/OYZJ08JvaIxwPXKgT/openapi.json
