Разбор матчей
Войти
К поиску матчей

API

Ваши ключи

Как обращаться

Ключ передаётся заголовком — любым из двух. Первый понимают готовые клиенты HTTP без единой строки кода, второй короче набрать руками.

curl -H "Authorization: Bearer <ключ>" \
     https://wlogs.tf/api/v1/player/76561198221384832/last-log

curl -H "X-API-Key: <ключ>" \
     https://wlogs.tf/api/v1/player/76561198221384832/model
В адресе ключ не принимается

Ни ?key=, ни любого другого параметра. Адрес запроса попадает в журнал службы при первой же внутренней ошибке, оседает в журнале прокси, в истории браузера и уезжает в заголовке Referer. Заголовок не печатает никто — поэтому только он.

Ответ всегда application/json в UTF-8. Ошибка — тот же JSON с полями error (текст) и err (короткий код для кода клиента).

Ручки

GET /api/v1

Опись: список ручек, чей ключ пришёл и какой у него предел. Удобно первым запросом — сразу видно, что ключ живой.

GET /api/v1/player/{steamid64}/last-log

Последний матч игрока на logs.tf: номер, название, карта, время, число игроков и ссылка. Если этот матч разобран у нас — рядом номер разбора и адрес страницы.

{
  "player": { "steamid": "76561198221384832", "name": "…", "avatar": "…", "about": "" },
  "log": {
    "id": 4085161, "title": "…", "map": "cp_process_f12",
    "at": 1758200000, "players": 12,
    "url": "https://logs.tf/4085161",
    "report": "1505559",
    "report_url": "https://wlogs.tf/m/1505559/"
  }
}
GET /api/v1/player/{steamid64}/model

Во что игрок был одет в последнем матче и чем это рисовать: класс, команда, предметы, краски, необычные эффекты — и следом готовое описание модели: тело, части с путями моделей, что скрыть под ними.

{
  "look": { "cls": "soldier", "team": "red", "items": [30367], "guns": [18],
            "paints": {"30367": "#7d4071"}, "effects": {}, "match": "1505559" },
  "draw": {
    "class": "soldier",
    "body":  "models/player/soldier.mdl",
    "pieces": [ { "id": 30367, "name": "…", "model": "models/workshop/…mdl",
                  "region": "head", "paint": "#7d4071", "skin": null } ],
    "hide": ["hat"]
  },
  "assets": { "base": "https://wlogs.tf/game/" },
  "page": "https://wlogs.tf/u/76561198221384832"
}

Пути в draw считаются от assets.base: файлы игры (.mdl, .vvd, .vtx, .vmt, .vtf) отдаются оттуда как есть — тот же источник, из которого рисует сцену сам сайт. Посмотреть результат глазами можно по адресу из поля page.

Набора может ещё не быть

Мы собираем его из разобранного матча или из демки последней игры, и эта сборка запускается заходом человека на страницу игрока — ключом её не запустить, иначе список из тысячи steamid превратился бы в тысячу закачек. Нет набора — ответ 404 с кодом noLook и ссылкой на страницу, с которой сборка начнётся.

GET /api/v1/match/{id}

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

{
  "match": "1508441", "map": "koth_bagel_rc12", "log": "4122859",
  "url": "https://wlogs.tf/m/1508441/",
  "replay_url": "https://wlogs.tf/replay?replay=1508441&map=koth_bagel_rc12",
  "format": { "format": "6v6", "mode": "koth", "confidence": "likely" },
  "minutes": 13.8, "rounds": { "in_log": 3, "in_demo": 2 },
  "mvp": { "top": [ … ], "basis": { "weights": { … }, "outside_log": [] } },
  "players": [ { "steam": "[U:1:236011667]", "name": "…", "class": "Scout",
                 "role": "pocket scout",
                 "deaths": { "total": 7, "solo": 2, "untraded": 3, "opening": 1 },
                 "positioning": { "ahead_median": 641, "ahead_p90": 1598 } } ]
}
GET /api/v1/match/{id}/highlights

Моменты матча: вид момента, подпись, границы в тиках и секундах, кто его сделал и ссылка на хайлайты этого игрока.

{
  "match": "1508441", "count": 9,
  "moments": [ { "tick": 7533, "from": 7266, "to": 7700,
                 "seconds": 108.9, "length": 6.5,
                 "kind": "airshot", "title_key": "airshot",
                 "title": "Эйршот", "title_en": "Airshot",
                 "note": "жертва в воздухе, 162 над землёй",
                 "note_en": "victim airborne, 162 above the ground",
                 "notes": [ { "k": "airshot", "n": 162 } ],
                 "player": "76561198202237540", "name": "gunk",
                 "class": "Soldier", "team": "Red", "kills": 1,
                 "url": "https://wlogs.tf/hl/1508441?player=76561198202237540" } ]
}

Поля kind, title_key и notes постоянны и не переводятся — по ним различайте моменты в программе и говорите о них своими словами. Подпись и описание едут на двух языках: title и note — по-русски, title_en и note_en — по-английски. У разборов, собранных до 19.09.2026, английских полей нет: там придёт null, и это честнее русской строки под видом английской.

GET /api/v1/feed

Что разобрано недавно, свежее сверху. Параметры: since — отдать только собранное позже этого времени (unix), limit — сколько строк, до 200. Так устроен опрос: запомните время последнего обращения и спрашивайте только то, что появилось после него.

{
  "matches": [ { "match": "1503428", "map": "koth_bagel_rc12",
                 "log": "4116918", "played_at": 1788792930, "at": 1789824927 } ],
  "since": 0, "count": 1
}
GET /api/v1/maps

Карты, для которых у нас есть геометрия: только на них рисуется 3D-повтор.

GET /api/v1/analyze?q={ссылка}

Заказать разбор матча по ссылке — на logs.tf или demos.tf, в любом виде, годится и просто номер. Уже разобранный матч вернётся сразу, с ready: true и адресом страницы; новый встанет в очередь, и его состояние спрашивается по status_url.

{
  "demo": 1508441, "match": "1508441", "map": "koth_bagel_rc12",
  "ready": false, "job": "1508441-1789818929-001",
  "status_url": "https://wlogs.tf/api/status/1508441-1789818929-001",
  "url": "https://wlogs.tf/m/1508441/", "left": 9
}
Это единственная ручка, которая заводит работу

Поэтому она считается так же, как заказ человеком: есть потолок разборов в очереди и суточная квота, и квота идёт на аккаунт Steam — владельца ключа, а не на каждый ключ отдельно. Кончилась — ответ 429 с кодом dailyLimit или tooManyPending.

GET /api/v1/me

Чей это ключ, предел частоты и сколько израсходовано из суточной квоты. Квота считается по аккаунту Steam, а не по ключу.

Ключи и пределы

  • 30 запросов в минуту на ключ. Считается по ключу, а не по адресу: за перебор отвечает тот, кто ключ завёл, а не сосед по одному адресу. Перебор — 429 и заголовок Retry-After.
  • 5 живых ключей на аккаунт. Ключ — это имя для предела частоты, и десяток ключей у одного человека означал бы десятикратный предел. Отозванные не считаются.
  • Ключ показывается один раз. У нас лежит только его отпечаток; утёкшая база ключей не выдаёт. Потеряли — отзовите и заведите новый, это бесплатно.
  • Отзыв мгновенный. Отозванный ключ перестаёт открывать что-либо со следующего же запроса.
  • Ключ открывает только /api/v1/. Ни заказ разбора, ни 3D-повтор, ни управление самими ключами им не открываются: ключи заводит и отзывает человек со страницы, куками.
  • На техработах API закрыт вместе со всем сайтом — ответ 503 и Retry-After.

Коды ответов

Код err Что случилось
200— Всё хорошо.
401— Ключа нет, он не тот или отозван. Три случая отвечают одинаково — разница подсказала бы перебирающему.
404noRoute Такой ручки нет. Список — в /api/v1.
404noGames У этого игрока нет матчей на logs.tf.
404noLook Набора этого игрока мы ещё не собирали.
503busy Сервер уже ищет демки для других. Повторите через минуту.
404noReport Такого разбора у нас нет. Закажите его на главной — ключом заказ не запускается.
400badLook Набор этого игрока записан так, что модель по нему не собирается. Редкий случай; напишите нам.
429— Слишком часто. Ждите Retry-After секунд.
503upstream logs.tf не отвечает. Повторите позже.
503items Схема предметов игры сейчас недоступна. Повторите позже.
503— Технические работы на сайте.

Ответы ручек ключей

Ручки /api/keys и /api/keys/revoke вызывает эта страница, куками вошедшего. Ключом они не открываются, но их отказы видны в консоли браузера — вот что они значат.

Код err Что случилось
403offsite Запрос пришёл не с этой страницы. Так мы не даём чужому сайту выписать ключ вашими куками.
404noKey Такого вашего ключа нет: он уже отозван или никогда вам не принадлежал.
409toomany Живых ключей уже 5 — отзовите ненужный.