ErmiData API — Документация

REST API и WebSocket для получения ML-скоров по инструментам. Основной идентификатор — ISIN. Публичные таргеты: 7 alias, обновляются ежедневно.

Интерактивный Swagger UI (OpenAPI) открывается в отдельном окне: там можно выполнять запросы прямо из браузера с вашим API-ключом (кнопка Authorize).

Быстрый старт

1. Получите API-ключ

  1. Зарегистрируйтесь на ERMIAS Platform
  2. Перейдите в личный кабинет → Подписка
  3. Активируйте тариф (Trial / Lite / Standard / Pro)
  4. Скопируйте API-ключ из личного кабинета

2. Сделайте запрос

import requests

API_KEY = "your_api_key_here"
headers = {"X-API-Key": API_KEY}

# Получить сегодняшние таргеты по всем инструментам
response = requests.get(
    "https://api.app-trading.ru/v1/targets/today",
    headers=headers
)
payload = response.json()

# envelope: {"success": true, "data": {...}, "meta": {...}}
if not payload["success"]:
    raise RuntimeError(payload["error"]["message"])

data = payload["data"]
print(f"Снимок от {data['generated_at']} до {data['valid_until']}")
print(f"Инструментов: {data['count']}")

first = data["targets"][0]
print(f"{first['ticker']} ({first['isin']}):")
for alias, score in first["probabilities"].items():
    print(f"  {alias}: {score:.4f}")

3. Формат ответа

Пример — реальный ответ GET /v1/targets/today. В probabilities каждого объекта попадают только alias, выбранные для вашего тарифа (значения 0.0–1.0); остальные ключи отсутствуют и не заполняются null.

{
  "success": true,
  "data": {
    "date": "2026-09-08",
    "generated_at": "2026-09-08T11:05:56+03:00",
    "valid_until": "2026-09-08T18:50:00+03:00",
    "count": 77,
    "targets": [
      {
        "isin": "RU0009029540",
        "ticker": "SBER",
        "name": "SBER",
        "sector": "financial",
        "probabilities": {
          "base_vector": 0.5202,
          "lower_progression": 0.3821
        }
      }
    ]
  },
  "meta": {
    "generated_at": "2026-09-08T22:22:57+03:00",
    "request_id": "938fb707-97e6-4f50-959b-776f453d0d30",
    "disclaimer": "Информация предоставляется только для аналитических целей..."
  }
}

Формат ответа и ошибки

Единый envelope

Все JSON-ответы имеют одну форму. Успех: success=true, данные — в блоке data. Ошибка: success=false, объект error с кодом, сообщением и контекстом. Блок meta всегда присутствует: generated_at, request_id (совпадает с заголовком x-request-id) и disclaimer.

{
  "success": false,
  "error": {
    "code": 401,
    "message": "Missing X-API-Key header",
    "details": null,
    "limit": null,
    "reset_at": null
  },
  "meta": {
    "generated_at": "2026-09-08T22:19:42+03:00",
    "request_id": "455ebbfc-93ec-4f5f-81a3-348aed752eb1",
    "disclaimer": "Информация предоставляется только для аналитических целей..."
  }
}

При превышении дневной квоты (429) в полях error.limit и error.reset_at приходят размер квоты и время её сброса. CSV-endpoints при ошибке возвращают тот же JSON, а не пустой файл.

Rate-limit заголовки

Заголовок Описание
x-ratelimit-limit Дневная квота запросов тарифа (500 / 500 / 2 000 / 10 000)
x-ratelimit-remaining Осталось запросов в текущем московском дне
x-ratelimit-reset Время сброса счётчика, ISO 8601 со смещением Europe/Moscow
x-request-id Уникальный идентификатор запроса; совпадает с meta.request_id

REST API Endpoints

Публичные (без ключа)

GET /auth/plans

Тарифный каталог. Возвращает список доступных планов и их параметров.

GET /v1/targets/meta

Каталог всех семи публичных alias с маркетинговыми названиями и описаниями.

С X-API-Key

GET /v1/status

Статус ключа и текущего снимка: состояние рынка, время генерации ML-скоров и срок их действия, количество активных инструментов, индикатор торгового дня. Остаток квоты виден в заголовках x-ratelimit-*.

{
  "success": true,
  "data": {
    "status": "closed",
    "date": "2026-09-08",
    "targets_generated_at": "2026-09-08T11:05:56+03:00",
    "targets_valid_until": "2026-09-08T18:50:00+03:00",
    "instruments_count": 93,
    "is_trading_day": true
  },
  "meta": { /* ... */ }
}
GET /v1/instruments

Каталог активных инструментов MOEX — одинаковый для всех тарифов. Поля объекта: id, isin, ticker, name, sector, class_code, flag_work, flag_work_inday, wave. FIGI и внутренние колонки не публикуются.

{
  "success": true,
  "data": {
    "count": 93,
    "instruments": [
      {
        "id": 65,
        "isin": "RU000A0DQZE3",
        "ticker": "AFKS",
        "name": "AFKS",
        "sector": "financial",
        "class_code": "TQBR",
        "flag_work": 1,
        "flag_work_inday": 0,
        "wave": 2
      }
    ]
  }
}
GET /v1/targets/today

Полностью сегодняшний снимок ML-скоров. data.targets — массив объектов по всем активным инструментам, в probabilities каждого — только alias, выбранные для вашего тарифа. data.count равен длине массива. Формат ответа — в разделе «Быстрый старт».

GET /v1/targets/today/{isin}

Таргеты одного инструмента. В отличие от общего снимка, объект лежит в data.target: {isin, ticker, name, sector, probabilities} + метаданные снимка date, generated_at, valid_until. Неизвестный или неактивный ISIN — 404 Instrument not found.

Параметр Тип Описание
isin path ISIN инструмента из каталога /v1/instruments (обязательно)
GET /v1/targets/history/{isin}?days=N

История снимков по инструменту. Строки data.history[]: {date, generated_at, valid_until, probabilities}. Глубина считается в уникальных торговых датах; запрос глубже лимита тарифа — 403.

Параметр Тип Описание
isin path ISIN инструмента (обязательно)
days int Сколько последних торговых дней вернуть (по умолчанию — максимум тарифа; у Pro без параметра — с 2022-01-01)
Trial: 3 дня · Lite: 30 дней · Standard: 90 дней · Pro: с 2022-01-01
GET /v1/targets/available

Текущий выбор alias: план, лимит, разрешённый набор, описание выбранных таргетов и состояние смены.

{
  "success": true,
  "data": {
    "plan": "lite",
    "target_limit": 2,
    "count": 2,
    "allowed_aliases": ["base_vector", "lower_progression"],
    "targets": [
      {
        "api_alias": "base_vector",
        "marketing_name": "Базовый вектор",
        "description": "Оценка основного направления ценового смещения...",
        "sort_order": 1
      }
    ],
    "selection": {
      "can_change": true,
      "changed_at": "2026-08-06T01:00:52+03:00",
      "next_change_at": "2026-08-13T01:00:52+03:00"
    }
  }
}
PUT /v1/targets/available

Смена набора таргетов (только Lite/Standard). Первое изменение — сразу после выдачи ключа, следующие — не чаще одного раза в 7 календарных суток; попытка раньше cooldown возвращает 409. Trial и Pro всегда имеют полный набор и получают 403.

Поле Тип Описание
target_aliases string[] Ровно столько уникальных alias, сколько разрешено тарифу (Lite — 2, Standard — 5). Неизвестный, неактивный или повторяющийся alias — 422
Content-Type: application/json

{"target_aliases": ["base_vector", "sequence_stability"]}
GET /v1/targets/download?from=YYYY-MM-DD&to=YYYY-MM-DD

CSV-выгрузка вероятностей за период — только Pro. Даты раньше 2022-01-01 или неправильный порядок дат — 422. Файл содержит только инструменты, чья волна входит в CSV_ALLOWED_WAVES (по умолчанию 1–5), со всеми семью alias.

Параметр Тип Описание
from date Начало периода, YYYY-MM-DD (обязательно)
to date Окончание периода, YYYY-MM-DD (обязательно)
Только Pro
GET /downloads/history.csv

Одноразовый маркетинговый CSV для любого тарифа (требуется X-API-Key): все семь alias, начиная с 2022-01-01, с лагом 30 торговых дней от последней доступной даты, только инструменты с волнами из CSV_ALLOWED_WAVES. Повторное скачивание того же ключом — 409 Marketing CSV has already been downloaded.

Одноразово · Лаг 30 торговых дней · Требуется X-API-Key

Публичные таргеты

API публикует только 7 alias из внутреннего каталога. Точность, ранки и агрегированный score не публикуются. Тариф определяет количество доступных alias:

Тариф Количество alias Начальный набор
Trial 7 (все) Все 7 alias
Lite 2 на выбор base_vector, daily_impulse
Standard 5 на выбор base_vector, daily_impulse, upper_reference, lower_reference, sequence_stability
Pro 7 (все) Все 7 alias

Lite и Standard могут изменить выбор сразу после выдачи ключа, затем не чаще одного раза за 7 суток.

WebSocket

Push-уведомление о готовности нового снимка ML-скоров. Доступно в тарифах Standard и Pro: соединение Trial/Lite закрывается сразу после handshake кодом 1008.

WS wss://api.app-trading.ru/v1/stream

Ключ передаётся заголовком X-API-Key. После подключения сервер сначала шлёт connected с текущим набором allowed_target_aliases; при появлении нового снимка — targets_ready, после чего значения нужно запросить через GET /v1/targets/today именно по этому набору alias. Сессию поддерживает heartbeat. Неверный, неактивный или просроченный ключ, а также некорректный JSON от клиента — закрытие кодом 1008. Внутренние имена таргетов по каналу не раскрываются.

Протокол сообщений

Сообщение Назначение
connected Первое сообщение после подключения. Поле allowed_target_aliases совпадает с текущим выбором плана и используется для проверки REST-данных
targets_ready Сформирован новый дневной снимок ML-скоров — выполните GET /v1/targets/today
heartbeat Регулярный keep-alive пакет; в бизнес-логике игнорируется
{"type": "connected", "allowed_target_aliases": ["base_vector", "daily_impulse", "upper_reference", "lower_reference", "sequence_stability"]}

Пример подключения (Python)

import json
import websocket

API_KEY = "your_api_key_here"

def on_message(ws, raw):
    message = json.loads(raw)
    event = message.get("type")
    if event == "connected":
        print(f"[WS] Разрешённые alias: {message['allowed_target_aliases']}")
    elif event == "targets_ready":
        print("[WS] Новый снимок готов — запрашиваем GET /v1/targets/today")
    elif event == "heartbeat":
        pass  # keep-alive

def on_close(ws, status_code, reason):
    if status_code == 1008:
        print(f"[WS] Соединение закрыто кодом 1008: {reason}")

ws = websocket.WebSocketApp(
    "wss://api.app-trading.ru/v1/stream",
    on_message=on_message,
    on_close=on_close,
    header=[f"X-API-Key: {API_KEY}"]
)
ws.run_forever()

Примеры интеграции

Python — Получение ML-скоров

import requests

API_KEY = "your_api_key"
headers = {"X-API-Key": API_KEY}

# Список инструментов
response = requests.get("https://api.app-trading.ru/v1/instruments", headers=headers)
instruments = response.json()["data"]["instruments"]
print(f"Инструментов: {len(instruments)}")

# Таргеты одного инструмента: объект лежит в data.target
response = requests.get(
    "https://api.app-trading.ru/v1/targets/today/RU0009029540",
    headers=headers
)
target = response.json()["data"]["target"]
print(f"{target['ticker']} ({target['sector']}):")
for alias, score in target["probabilities"].items():
    print(f"  {alias}: {score:.4f}")

JavaScript — Получение ML-скоров

const API_KEY = "your_api_key";

async function getTodayTargets() {
  const response = await fetch(
    "https://api.app-trading.ru/v1/targets/today",
    { headers: { "X-API-Key": API_KEY } }
  );
  const result = await response.json();
  if (!result.success) {
    throw new Error(result.error.message);
  }
  console.log(`Снимок: ${result.data.generated_at} → ${result.data.valid_until}`);
  console.log(`Инструментов: ${result.data.count}`);
  return result.data.targets.map((t) => ({ ticker: t.ticker, probabilities: t.probabilities }));
}

getTodayTargets();

Python — Смена набора таргетов (Lite/Standard)

import requests

API_KEY = "your_api_key"
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

# Посмотреть текущий выбор
current = requests.get("https://api.app-trading.ru/v1/targets/available", headers=headers).json()
print(f"Можно изменить: {current['data']['selection']['can_change']}")

# Сменить набор (не чаще 1 раза за 7 суток)
new_selection = {"target_aliases": ["base_vector", "sequence_stability"]}
response = requests.put(
    "https://api.app-trading.ru/v1/targets/available",
    headers=headers,
    json=new_selection
)

if response.status_code == 409:
    # cooldown 7 суток: точное время разблокировки в selection.next_change_at
    raise RuntimeError(response.json()["error"]["message"])
if response.status_code == 403:
    # тариф не допускает смены набора (Trial / Pro)
    raise RuntimeError(response.json()["error"]["message"])
print(response.json())

Лимиты и аутентификация

Аутентификация

Все защищённые запросы должны содержать заголовок X-API-Key с вашим API-ключом. Публичные endpoints (/auth/plans, /v1/targets/meta) не требуют ключа.

Тарифные лимиты

Тариф Таргеты REST-история Запросов/день WebSocket Маркетинг CSV
Trial 7 (все) 3 торговых дня 500 ✗ ✓ (одноразово)
Lite 2 на выбор 30 торговых дней 500 ✗ ✓ (одноразово)
Standard 5 на выбор 90 торговых дней 2 000 ✓ ✓ (одноразово)
Pro 7 (все) с 2022-01-01 10 000 ✓ ✓ + CSV за период

Коды ответов

Код Описание
200 Успех
401 Отсутствует заголовок X-API-Key (Missing X-API-Key header)
403 Неизвестный или неактивный ключ; недостаточный тариф: WS на Trial/Lite, PUT на Trial/Pro, история глубже лимита тарифа
404 Ресурс не найден (Instrument not found для неизвестного ISIN)
409 Конфликт: PUT внутри 7-суточного cooldown; повторное скачивание маркетингового CSV
422 Ошибка валидации: неверное количество или названия alias в PUT, некорректные даты выгрузки CSV
429 Превышен дневной лимит запросов; в теле — error.limit и error.reset_at, в ответах — заголовки x-ratelimit-*
500 Внутренняя ошибка сервера
503 ErmiData недоступно: access_config не настроен или имеет неподдерживаемую версию
504 Gateway timeout (длительная операция, например выгрузка CSV за период)