Roschart — Russian statistics in charts

Official Rosstat statistics turned into charts instead of CSV and XLSX

RU

API roschart.ru

Rosstat statistics in JSON — the same dataset used to draw the website pages: 26 thousand datasets, 49 million values, updated every day

Quick start

Без ключа отвечает только первый запрос — сводка о базе. Для остальных нужен ключ: он бесплатный и выдаётся сразу после регистрации.

# what is available in the database and when it was updated
curl 'https://roschart.ru/api/meta'

# find datasets on inflation
curl -H 'X-API-Key: rk_…' 'https://roschart.ru/api/catalog?q=инфляция&limit=5'

# download one dataset with rows and values
curl -H 'X-API-Key: rk_…' 'https://roschart.ru/api/dataset/ipc-mes-06-2026-indeksy-potrebitelskih-cen-na-neprodovolstve'

Без ключа такие запросы получают ответ 401 со ссылкой на получение ключа.

Key and limitations

The key is passed in one of three ways:

ModeRequests per minute Per dayRecords per request
without key только /api/meta, остальное — 401
with key 120 no cap 1000

Access is free, including for commercial projects. A per-minute threshold is set to protect the server from looped requests; there is no limit on the volume of data retrieved

The limit is calculated per account, not per key: keys under the same account share a common quota. Using a separate key for each application is standard practice to track usage more clearly

Each response includes X-RateLimit-Tier, X-RateLimit-Limit and X-RateLimit-Remaining. On failure — 429 and Retry-After in seconds: time to wait before retrying

Register and personal account.

General rules

All endpoints accept GET and return JSON in UTF-8 with the okfield

{"ok": false, "error": "dataset not found"}
CodeWhat happened
200everything is fine
304response unchanged since last time (see ETag below)
400required parameter missing
401key not found or disabled
404no such dataset or path
429frequency limit exceeded
500server-side error

Caching

Data is updated daily, but queried more frequently. Each response ETag, calculated from the body itself. Send it back If-None-Match and get 304 in a few bytes instead of a megabyte of JSON:

curl -H 'If-None-Match: "a1b2c3…"' 'https://roschart.ru/api/meta'

Even cheaper: remember upload_id from /api/meta and do not re-download anything at all if it remains the same. This is one line for the entire export — if it has not changed, no value in the database has changed

Pages

Where there is a list, limit and offsetwork, and the response contains total (total number of matches), count (number returned) and offset. limit is silently truncated to the ceiling of your level — check count, not hoping that you requested a thousand and received a thousand

Cross-domain requests

Allowed from anywhere: Access-Control-Allow-Origin: *, so you can make a request directly from the browser. Note that the key in the page code is visible to everyone — anyone can spend your quota

Identifiers

The dataset identifier is a readable Latin string like ipc-mes-06-2026-indeksy-potrebitelskih-cen-na-neprodovolstve. It is also in the website page address: /dataset/<id>. Identifiers are stable but not eternal: when correcting the name of a dataset, the address changes, and the old one starts redirecting to the new one. If you store the id yourself — check against /api/changes.

What is a dataset

A dataset is a single sheet from a Rosstat book standardized to a common format. It is not an indicator: if Rosstat published industrial production in three tables, there will be three datasets

Within a dataset, everything is based on two parallel arrays:

Two types of datasets differ by the kind:

A row, in addition to label, may have row and col: in the original table the header is often two-tiered ('men / urban population'), and we keep both parts separately, while label — what is shown to the user

Unit of measurement (unit) may occur null: at Rosstat it is often written in the table title, there is no separate field in the book. Then see title and footnotes.

Field title — cleaned-up name suitable for display title_raw — exactly as printed in the book, with footnote numbers and years in brackets. Link to the dataset — source_url, it leads to the dataset page on the website; a direct link to the Rosstat book is in source_file.

Handles

GET /api/meta — database status

Weighs less than a kilobyte. It is convenient to start synchronization with it: if upload_id has not changed, the rest does not need to be

{
  "ok": true, "ready": true,
  "upload_id": "90ae18251ef86537",
  "generated_at": "2026-08-11T18:23:48+00:00",
  "committed_at": "2026-08-11T19:02:51+00:00",
  "source": "https://rosstat.gov.ru/",
  "n_datasets": 26461,
  "n_values": 49100146,
  "breakdown": [{"kind": "crosssection", "freq": "none", "c": 18141, "v": 44485654}, …]
}

generated_at — when the parser parsed the books, committed_at — when the upload was accepted by the site ready: false means that the upload is currently in progress: the data being provided at this moment are the previous, complete data

GET /api/sections — sections

Rosstat rubrics with the number of datasets and values in each. Section names differing only by the year of publication of the collection are merged into one: the year of publication within the name is irrelevant

{"ok": true, "sections": [ {"section": "Industrial production", "c": 2694, "v": 6329150}, …]}

GET /api/catalog — dataset search

ParameterValue
qsearch string by name; can be in Russian, case does not matter
kindtimeseries or crosssection
freqannual, quarterly, monthly
sectionsection exactly as it came in /api/sections
flat1 — do not collapse releases of the same indicator into one row
groupgroup key from group_key: all editions of one indicator
limit, offsetpage; default 60

Rosstat publishes the same table every month as a new book, and without grouping, a search for the word "inflation" would return forty almost identical lines. By default, such editions are collapsed into one most recent set with the field n_editions; expand group — group= with its group_key, disable collapsing altogether — flat=1.

{"ok": true, "total": 437, "count": 2, "offset": 0, "items": [ {"ds_id": "ipc-mes-06-2026-…", "title": "consumer price index…", "unit": null, "freq": "monthly", "kind": "timeseries", "section": "Prices, inflation", "n_series": 2, "n_periods": 432, "n_values": 462, "period_first": "1991-01", "period_last": "2026-12", "content_hash": "489e37d1b955a6e4", "group_key": "f9aa1ee825d27982", "n_editions": 1, "source_url": "https://roschart.ru/dataset/…", "source_file": "https://rosstat.gov.ru/storage/…xlsx"}, …]}

content_hash is calculated from the values of the set. If matched — the content is exactly the same as what you have already downloaded, no need to retrieve it

GET /api/ids — all identifiers

Compact list: id, title, kind, frequency. Needed to set up your own mirror of the catalogue without downloading the entire sets

Parameters: limit, offset and updated_since — date in the format 2026-08-01, after which the set changed. With it, the sweep becomes additional: once every couple of days you ask what has changed since the last reconciliation day, and only transfer that

GET /api/dataset/{id} — entire dataset

ParameterValue
max_seriesnumber of rows to return; regional datasets can have up to two thousand
qselect rows by substring in the label — for example, only "Moscow
limit, offsetpage by rows
format=csvreturn as table, not JSON
{"ok": true, "dataset": { "id": "…", "title": "…", "unit": null, "freq": "monthly", "kind": "timeseries", "section": "Prices, inflation", "source_url": "https://roschart.ru/dataset/…", "source_file": "https://rosstat.gov.ru/…xlsx", "source_via": "https://roschart.ru/go/ds/…", "periods": ["1991-01", "1991-02", …], "series": [{"label": "by the end of the previous month", "row": "by the end of the previous month", "col": null, "values": [109.0, 105.9, 104.6, …]}], "series_total": 2, "series_shown": 1, "n_series": 2, "n_periods": 432, "n_values": 462, "footnotes": […], "related": […], "intro": "…", "tags": […], "title_raw": "…", "description": "…"}}

If series_shown less series_total, you did not receive the entire dataset related — adjacent datasets of the same indicator, footnotes — footnotes from the book; they contain Rosstat disclaimers such as "excluding Crimea"

CSV is convenient for export to a table and is provided with a header Content-Disposition, i.e. downloaded as a file:

curl -o ipc.csv 'https://roschart.ru/api/dataset/ipc-mes-06-2026-…?format=csv'

GET /api/series/{id}/{i} — one series

Series number i: numbering starts from zero, the order is the same as in the dataset. Useful when only one line is needed instead of the entire dataset

GET /api/chart/{id} — ready-made chart

Provides the same as shown on the website page: chart type, selected series, axis labels, colors, footnote. Eliminates the need to decide how to visualize the dataset — as a line, bar chart, or heatmap for two thousand regions

Parameters: q — series selection, theme=dark — colors for dark theme. In addition to the periods and series, the response includes chart_kind, y_axis, row_label and note.

GET /api/revisions/{id} — retroactive revisions

Rosstat revises already published figures: if the methodology is refined, last year's value changes. We store previous values and provide what exactly changed: period, old value, new value, and the date of the revision. The original source does not retain the old value

GET /api/changes — change log

What happened between exports: datasets were added, changed, or removed. Parameters: kind (added | changed | removed), id — history of a single dataset, limit, offset.

{"ok": true, "total": 1123,
 "batches": [{"changed_at": "2026-08-10T08:11:13+00:00", "added": 140, "changed": 2, "removed": 1}, …],
 "items": [{"changed_at": "…", "ds_id": "…", "kind": "removed", "title": "…",
            "old_values": 96, "new_values": 0, "old_last": "2017"}, …]}

The same is available as a feed: /changes/rss.xml.

GET /api/suggest — search suggestions

By the beginning of the word returns clarifications and suitable sets — the same as falls under the search line on the site. For a full-fledged search — /api/catalog.

Recipes

Download the entire catalogue

26 thousand sets — this is 27 queries with a key by 1000 records. Will fit in about three minutes

off=0
while :; do
  n=$(curl -s -H "X-API-Key: $KEY" \
        "https://roschart.ru/api/ids?limit=1000&offset=$off" \
      | tee -a ids.jsonl | grep -o '"count":[0-9]*' | cut -d: -f2)
  [ "$n" -lt 1000 ] && break
  off=$((off + 1000))
done

Keep your copy up to date

  1. ask once a day /api/meta;
  2. if upload_id the previous one — finish with this
  3. if the new one — take /api/changes and update only the sets listed there
  4. compare content_hash before uploading: it changes only together with the values

A full traversal with each download is also possible, but excessive: only a few hundred sets out of twenty-six thousand change with each download

Display the chart on your side without drawing it

For this, the API is not needed: /embed/<id> — a page with iframe, /og/<id>.png — a 1200×630 image for social networks

What is not in the data

Limitations you should know about in advance:

Rosstat data are open and available for free use, including commercial use. Requirements of the primary source — to retain the reference to the source and not to distort the figures — also apply to those who take data from us. Details on the page terms of use. What the site does with the data of those who created a key — in personal data processing policy.

Linking to roschart.ru is encouraged: the reader will reach the original table and its footnotes

Machine-readable description of the same endpoints — /openapi.json or /openapi.yaml (OpenAPI 3.0.3, identical content), it is used to generate clients for Swift, Kotlin, Dart and other languages

The catalogue composition is published as a separate snapshot with a permanent identifier: doi.org/10.5281/zenodo.22107658 — metadata for all datasets in a table, with links to each download

Need a higher limit or non-standard download — contact us.