rustata экономическая статистика РФ - поиск по 2 млн рядов

методы API

Срез по территориям, поиск рядов и показателей, значения одного ряда, происхождение цифр и справочники - чтобы знать, что подставлять в параметры. У каждого метода - готовый пример на Python, R и Julia.

Во всех примерах токен передаётся заголовком Authorization: Bearer; подходит и X-API-Key. Тарифы и квоты - на отдельной странице.

Срез по территориям Поиск рядов Значения ряда Происхождение Показатели Справочники Правила

GET /api/v1/data - срез по территориям

Главный метод. Отдаёт показатель сразу по многим территориям, поэтому «цены на пиво по всем регионам» - это один запрос, а не восемьдесят пять. Так же выгоднее по квоте: она считается в значениях, и один срез стоит ровно столько, сколько в нём точек.

Параметры

ПараметрЧто задаёт
datasetнабор данных, например fedstat/indicator_31448. Обязателен
geoуровень территории или перечень кодов через запятую
classкод продукта, несколько через запятую, либо all - все продукты набора
measure, transformationвид показателя и преобразование
freq, unitчастота и единица измерения
start, endпериод; можно писать месяцем: 2024-01
formatjson (по умолчанию) или csv
limit, offsetсколько рядов взять и с какого начать
bomтолько для CSV: 1 добавляет BOM для Excel на Windows

Уровни территории

geoЧто вернётсяТерриторий
countryРоссия целиком1
districtфедеральные округа13 с историческими
macroregionэкономические районы (прежнее деление)11
regionсубъекты федерациидо 85
cityгорода и районы внутри субъектовсотни
allвсе уровни сразу-
46000000000,RU-50только перечисленные территориипо списку

Коды принимаются в обоих видах: исходный ОКАТО и нормализованный (RU-46). Полный перечень - в справочнике территорий.

Продукты

В наборе обычно не один продукт, а сотни: в ценах на продовольствие их 875. Параметр class работает так же, как geo - можно один код, можно список, можно всё сразу.

classЧто вернётся
3203один продукт
3203,7802только перечисленные, порядок не важен
allвсе продукты набора
не переданто же, что all

Отсюда берётся, например, «все цены по Москве одним запросом»: geo=45000000000&class=all. Ряды при этом отдаются страницами по limit: сколько их всего, видно в поле series_total ответа.

Коды и их названия - в справочнике на сайте и методом /api/v1/reference/codes в программе.

Россия приходит двумя рядами

«Российская Федерация» и «Российская Федерация без учёта новых субъектов (с 01.01.2023)» - оба на уровне country.

Это не дубль, а две методики счёта. Различить их можно по полю geo_label, выбор между ними за вами.

Что приходит в ответе

Ответ состоит из двух частей: список рядов с описанием территории и плоский список значений, по строке на каждое. Такая форма разворачивается в таблицу «период на территории» одной строкой кода и не требует разбирать многоэтажную шапку исходных выгрузок.

{
  "series": [
    { "series_key": "s1_23sqev7ozzer4t73", "geo": "14000000000",
      "geo_label": "Белгородская область", "geo_level": "region",
      "title": "Пиво отечественное, л", "freq": "M", "unit": "RUB" }
  ],
  "observations": [
    { "series_key": "s1_23sqev7ozzer4t73", "period": "2025-06-01",
      "value": 198.23, "as_of": "2025-06-01" }
  ],
  "series_count": 100, "series_total": 470,
  "offset": 0, "has_more": true, "next_offset": 100
}

На каждый период приходит одно значение - последняя опубликованная оценка. Рядом идёт as_of: дата публикации именно этой оценки. Статистику уточняют задним числом, и по этому полю видно, какая версия цифры у вас на руках.

В CSV те же данные с разделителем ; и колонками series_key;geo;geo_label;geo_level;period;value;as_of. Кодировка UTF-8 без BOM: так её ждут pandas, readr и CSV.jl. Для Excel на Windows добавьте &bom=1, иначе кириллица откроется кракозябрами.

Средняя цена пива по регионам с 2024 года, развёрнутая в таблицу «период на территории»

import pandas as pd
import requests

TOKEN = "rst_ваш_токен"
resp = requests.get(
    "https://rustata.ru/api/v1/data",
    params={"dataset": "fedstat/indicator_31448", "class": "3203",
            "geo": "region", "start": "2024-01"},
    headers={"Authorization": f"Bearer {TOKEN}"},
    timeout=60,
)
resp.raise_for_status()
payload = resp.json()

series = pd.DataFrame(payload["series"]).set_index("series_key")
obs = pd.DataFrame(payload["observations"])
obs["region"] = obs["series_key"].map(series["geo_label"])
obs["period"] = pd.to_datetime(obs["period"])

table = obs.pivot(index="period", columns="region", values="value")
print(table.tail())

# у тарифов без месячной квоты этого заголовка нет, поэтому .get, а не []
print("остаток квоты:", resp.headers.get("X-Quota-Values-Remaining", "без ограничения"))

Ищет ряды по строке и возвращает их series_key - с ним потом берутся значения и происхождение. Нужен, когда код продукта заранее неизвестен.

Найти ряд по названию и получить его ключ

import requests

TOKEN = "rst_ваш_токен"
resp = requests.get(
    "https://rustata.ru/api/v1/series/search",
    params={"q": "бензин аи-92", "limit": 5},
    headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30,
)
resp.raise_for_status()

for item in resp.json()["items"]:
    print(item["series_key"], "|", item["title"])

GET /api/v1/series/{series_key}/observations

Все значения одного ряда. Нужен, когда ключ уже известен: получен поиском или взят из адреса страницы ряда.

Для разового скачивания метод не нужен - на странице ряда есть кнопка «скачать CSV», она работает без токена.

История одного ряда с начала 2024 года

import pandas as pd
import requests

TOKEN = "rst_ваш_токен"
KEY = "s1_mydzoqiytm4xqjq3"
resp = requests.get(
    f"https://rustata.ru/api/v1/series/{KEY}/observations",
    params={"start": "2024-01"},
    headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30,
)
resp.raise_for_status()

df = pd.DataFrame(resp.json()["observations"])
df["period"] = pd.to_datetime(df["period"])
print(df.set_index("period")["value"].tail())

GET /api/v1/series/{series_key}/evidence

Происхождение значений: из какого файла источника они пришли, каким релизом опубликованы, каким прогоном загружены. Этим ответ на вопрос «откуда цифра» отличается от «скачал где-то в интернете».

Откуда взялись значения ряда

import requests

TOKEN = "rst_ваш_токен"
KEY = "s1_mydzoqiytm4xqjq3"
resp = requests.get(
    f"https://rustata.ru/api/v1/series/{KEY}/evidence",
    headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30,
)
resp.raise_for_status()

for release in resp.json()["releases"][:3]:
    print(release["published_at"], "|", release["source_file"])

Показатели: /api/v1/concepts/search и /api/v1/concepts/{id}

Поиск по показателям, а не по отдельным рядам, и карточка показателя со сводкой: сколько рядов, по каким территориям, какие преобразования. Удобно, когда нужно понять, что вообще есть по теме, прежде чем забирать данные.

Найти показатель и посмотреть его сводку

import requests

TOKEN = "rst_ваш_токен"
headers = {"Authorization": f"Bearer {TOKEN}"}

found = requests.get("https://rustata.ru/api/v1/concepts/search",
                     params={"q": "молоко", "limit": 3},
                     headers=headers, timeout=30).json()
for concept in found["items"]:
    print(concept["concept_id"], "|", concept["label"], "|", concept["series_count"], "рядов")

first_id = found["items"][0]["concept_id"]
card = requests.get(f"https://rustata.ru/api/v1/concepts/{first_id}",
                    headers=headers, timeout=30).json()
print(card["label"], "- территорий:", card.get("geo_count"))

GET /api/v1/reference/* - справочники

Три списка, из которых берутся значения параметров: наборы для dataset, территории для geo, коды продуктов для class. На сайте они есть страницами, но программе нужен ответ, а не страница - иначе коды переписывают с экрана руками.

МетодЧто отдаёт
/api/v1/reference/datasetsнаборы: код, название, частота, сколько рядов и территорий, за какой период есть данные
/api/v1/reference/geoтерритории: код, название, уровень. Параметр level сужает до одного уровня
/api/v1/reference/codesкоды продуктов одного набора. Параметр dataset обязателен

Все три принимают format=csv и bom=1 - справочник чаще всего нужен таблицей, чтобы положить рядом со своими данными и сматчить. Квоту в значениях они не расходуют: это метаданные, а не наблюдения.

Общего справочника кодов нет намеренно

У каждого набора свой классификатор: код 3203 в ценах и в производстве означает разные вещи.

Объединённый список подталкивал бы подставить код не в тот набор - и запрос вернул бы данные, только не те, что ожидались.

Найти набор, взять его коды и выгрузить нужные продукты

import pandas as pd
import requests

TOKEN = "rst_ваш_токен"
headers = {"Authorization": f"Bearer {TOKEN}"}
BASE = "https://rustata.ru/api/v1"

codes = pd.DataFrame(requests.get(f"{BASE}/reference/codes",
                                  params={"dataset": "fedstat/indicator_31448"},
                                  headers=headers, timeout=60).json()["items"])
beer = codes[codes["name"].str.contains("Пиво", case=False)]
print(beer[["class", "name"]])

# коды списком - один запрос вместо запроса на каждый продукт
data = requests.get(f"{BASE}/data",
                    params={"dataset": "fedstat/indicator_31448",
                            "class": ",".join(beer["class"]),
                            "geo": "region", "start": "2024-01"},
                    headers=headers, timeout=120).json()
print(data["series_total"], "рядов")

Правила, общие для всех методов

Постраничная выдача

За один раз приходит 100 рядов, параметром limit можно взять до 300. Сколько подходит всего, видно в series_total, а next_offset подсказывает, с чего начать следующую страницу.

Постранично идут ряды, а не значения: каждый ряд приходит целиком по времени, склеивать разрезанную историю не нужно.

Если срез выбирает больше 60 000 значений, ответ не придёт - вернётся SLICE_TOO_BIG с подсказкой сузить запрос.

Кэширование

Ответы отдаются с ETag. Передайте его обратно в If-None-Match - если данные не менялись, придёт 304 без тела, и квота не израсходуется. На регулярных обновлениях это экономит и время, и лимит.

Ошибки

Ошибка приходит одинаково устроенным объектом: код, понятное сообщение и подсказка, что делать.

{ "error": { "code": "SLICE_TOO_BIG",
             "message": "Запрос выбирает 1 200 000 значений, максимум 200 000",
             "hint": "Сузьте период или список территорий" } }
КодHTTPКогда
UNAUTHORIZED401токен не передан или отозван
RATE_LIMITED429исчерпана квота тарифа
SLICE_TOO_BIG400запрос выбирает слишком много данных
NOT_FOUND404под фильтры ничего не подошло
INTERNAL500ошибка на нашей стороне

Интерактивный справочник

Запрос можно выполнить своим токеном прямо в браузере - Swagger. Машиночитаемое описание - openapi.json.