Overwatch Tournaments нужны функциональные cookie, чтобы сохранять вход и язык интерфейса. Аналитические cookie необязательны и только показывают, как используются страницы, — подробнее в политике конфиденциальности.

Перейти к содержимому

HTTP API

Весь HTTP платформы — это один шлюз и одна форма пути. Здесь: как устроены версии, как выглядят успех и ошибка, откуда берётся воркспейс запроса, как листать списки и какие ограничения действуют на границе.

Форма пути и версии

Каждый маршрут выглядит как /api/v{n}/<домен>/...: версия — всегда второй сегмент, и рядом с ней ничего не стоит. Шлюз приводит входящий путь к этой форме до маршрутизации, поэтому таблицы маршрутов, спецификации и кэш видят только канонические пути.

ПутьЧто это
/api/v1/...контракт: обычный, «развёрнутый» JSON
/api/v2/...те же пути, обработчики и HTTP-статусы; тело завёрнуто в конверт
/api/docs, /api/openapi.json, /api/openapi.v2.jsonсправочник и спецификации — вне версии, потому что описывают версии
/api/healthпроба, проксируется на фронтенд-контейнер
/bff/...внутренние эндпоинты сайта, авторизуются кукой; это не публичный API

v2 — это не другой набор эндпоинтов. Путь /api/v2/X переписывается на /api/v1/X и попадает в тот же обработчик с тем же статусом; отличается только тело. Переезд с v1 на v2 не меняет ни пути, ни параметры, ни коды ответов.

Полный пример, v1

GET /api/v1/workspaces/by-host?host=owt.craazzzyyfoxx.me HTTP/1.1
Host: owt.craazzzyyfoxx.me
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: private, no-store

{"workspace_id": 1, "slug": "anak"}

Тот же запрос в v2

GET /api/v2/workspaces/by-host?host=owt.craazzzyyfoxx.me HTTP/1.1
Host: owt.craazzzyyfoxx.me
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: private, no-store

{"ok": true, "data": {"workspace_id": 1, "slug": "anak"}}

В v2 успешный ответ — это всегда {"ok": true, "data": ...}, иногда с дополнительным массивом warnings. Пустое тело (204 No Content) остаётся пустым в обеих версиях, а data может быть литеральным null.

Ошибки

Ошибка в v1 — плоский объект: человекочитаемый detail, машинный code и любые структурированные детали, которые прислал сервис, на верхнем уровне:

{
  "detail": "workspace_id query parameter is required",
  "code": "bad_request"
}

Та же ошибка в v2:

{
  "ok": false,
  "error": {
    "code": "bad_request",
    "message": "workspace_id query parameter is required"
  }
}

Ветвиться нужно по code (v1) или error.code (v2); detail и message — текст для человека, парсить его не надо. Структурированные подробности приходят как fields (список: field, msg, code и всё, что добавил сервис) и retry_after; в v2 они лежат внутри error.details.

codeHTTPКогда
bad_request400параметр не назван или не разобран
unauthorized401учётка не предъявлена или не действует
forbidden403учётка распознана, но действие не разрешено
not_found404нет такого объекта, либо он вне вашего воркспейса
conflict409состояние не позволяет: дубликат, гонка, занятый слот
gone410объект существовал и больше недоступен
payload_too_large413тело больше потолка
unprocessable422JSON разобран, но схема или правило не выполнены — смотрите fields
rate_limited429лимит или квота; читайте Retry-After
unavailable503зависимость сервиса недоступна, попробуйте позже
internal500всё остальное

Собственные отказы шлюза (до сервиса дело не дошло) в v1 могут прийти без code — например 404 {"detail": "Not Found"} на несуществующем маршруте /api/v1/... или 503 {"detail": "service unavailable"}. В v2 code там выводится из статуса, то есть присутствует всегда.

Старые префиксы

Раньше auth, analytics, balancer, streams, notifications и announcements стояли рядом с версией: /api/auth/me. Эти написания ещё отвечают — шлюз переписывает их на канонические (/api/v1/auth/me), посегментно, так что /api/authx не считается совпадением. Каждый такой ответ несёт три заголовка:

Deprecation: true
Sunset: Thu, 01 Apr 2027 00:00:00 GMT
Link: </api/v1/auth/me>; rel="successor-version"

Переезд — это только префикс: тело, статусы и параметры не меняются.

Воркспейс запроса

Воркспейс — корень аренды, и в API он называется явно, параметром workspace_id. Хост запроса выбирает сообщество для сайта (поддомен платформы или подтверждённый собственный домен), но не подставляет аренду в API: собственный домен сообщества отвечает по тем же путям /api/..., что и платформенный хост, и доменные чтения там точно так же ждут workspace_id. Сопоставление хоста и воркспейса отдаёт публичный GET /api/v1/workspaces/by-host?host=... — он же в примере выше; неизвестный или неподтверждённый хост даёт null.

Правила разрешения такие:

  1. Явный workspace_id в запросе побеждает всегда.
  2. Если параметра нет, а предъявленная учётка привязана ровно к одному воркспейсу (API-ключ — всегда, сессия — если участник состоит в одном сообществе), шлюз подставит его сам. Так ключу не приходится повторять в строке запроса то, что уже сказано в самой учётке.
  3. Если воркспейсов у учётки несколько или её нет вовсе, шлюз не угадывает: доменное чтение без workspace_id отвечает 400 bad_request.

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

Пагинация

Списки страничные, по номеру страницы: page (с 1) и per_page. Ответ — конверт из четырёх ключей:

{"page": 1, "per_page": 10, "total": 137, "results": []}

Значения по умолчанию и потолок per_page зависят от эндпоинта (встречаются 10, 20, 25, 30, 50 при максимуме 100–500), поэтому сверяйтесь со справочником эндпоинтов. Особый случай — per_page=-1: «всё», с жёсткой защитой в 10 000 строк. Многие списки принимают ещё sort и order (asc либо desc), а поиск — query.

Два места устроены иначе, и это видно по полям ответа:

  • Уведомления листаются курсором: в ответе есть next_cursor (на последней странице — null), его же и передают обратно параметром cursor.
  • История чата комнат (пик/бан, драфт) догружается параметром after_id — по идентификатору последнего известного сообщения.

Кэш публичных ответов

Часть публичных чтений шлюз держит в собственном кэше в памяти. Правила жёсткие и их стоит знать:

  • Кэшируются только GET и только v1 — запрос в v2 всегда идёт мимо.
  • По умолчанию кэш видит только анонимные запросы: наличие заголовка Authorization его отключает. Исключение — несколько заведомо не зависящих от зрителя чтений (страница турнира, стадии, таблица, списки встреч и команд), где вошедший читатель делит ту же запись.
  • Хранятся только ответы 200. Ошибки и 404 скрытых турниров не кэшируются никогда.
  • Время жизни записи — 30 секунд по умолчанию, но это лишь страховка: записи турнира сбрасываются событием, как только воркер что-то в нём изменил.

Результат виден в заголовке X-Cache: HIT — ответ из кэша, MISS — этот запрос сходил наверх, COALESCED — запрос дождался чужого похода наверх по тому же ключу (параллельные промахи по одному ключу схлопываются в один вызов).

Ключ кэша — путь плюс отсортированная строка запроса, поэтому ?a=1&b=2 и ?b=2&a=1 — одна запись, а разные workspace_id разводятся автоматически.

Снаружи ответы API кэшировать нельзя: если сервис не поставил свой заголовок, шлюз проставляет Cache-Control: private, no-store на всё под /api/ и /bff/. Содержимое зависит от зрителя, и промежуточный кэш отдал бы чужое.

Ограничения на границе

  • Эндпоинты аутентификации ограничены по IP: регистрация, вход, OAuth-callback и обмен SSO-тикета — по умолчанию 10 запросов за 60 секунд. Обновление сессии считает только неудачные попытки, чтобы общий выход в интернет не разлогинивал всех, кто за ним сидит.
  • API-ключи меряются по самому ключу, а не по IP, против его собственного requests_per_minute (при отсутствии значения — 60 в минуту). На каждом таком ответе, успешном или нет, есть тройка заголовков RateLimit-Limit, RateLimit-Remaining и RateLimit-Reset (секунды до сброса) — по ним удобно держать темп, не упираясь в стену.
  • Анонимный трафик может быть ограничен по IP общим бюджетом; включается настройкой деплоя.

Отказ выглядит одинаково у всех слоёв: 429, заголовок Retry-After и тело с code равным rate_limited.

{"retry_after": 60, "detail": "Too many requests", "code": "rate_limited"}

Лимиты на границе — не то же самое, что квоты воркспейса, ключа и сессии: те считают ещё и тяжёлые операции, и приходят с quota_exceeded в fields. Подробнее — в статье Аутентификация и ключи API.

Таймауты и отказы шлюза

Каждый вызов идёт к доменному сервису с бюджетом времени (по умолчанию 120 секунд, у дешёвых чтений — меньше). Снаружи это видно так:

ОтветЧто случилось
503, Retry-After: 1сервис или аутентификация недоступны, запрос даже не начался — повторяйте
504сервис не ответил в отведённый срок
502ответ сервиса не удалось разобрать
404путь под /api/v1/ не совпал ни с одним маршрутом

Тело JSON-запроса читается не более 12 МиБ; всё, что больше, обрывается и приводит к 400. Отдельный потолок на размер тела может задавать квота — тогда ответ будет 413.

Где полный список эндпоинтов

Статьи описывают модель и правила, а перечень маршрутов генерируется из таблиц самого шлюза:

  • /api/docs — интерактивный справочник с переключателем v1/v2.
  • /api/openapi.json — спецификация v1, /api/openapi.v2.json — спецификация v2.

См. также