API
Ваши ключи
Ключ выдаётся аккаунту Steam
Войдите — и заведите ключ одной кнопкой. Ключ привязан к вашему steamid64, и предел частоты считается по нему же.
Войти через SteamВторой раз мы его не покажем: у нас хранится только отпечаток, а не сам ключ. Потеряли — отзовите и заведите новый.
Ключей пока нет.
Как обращаться
Ключ передаётся заголовком — любым из двух. Первый понимают готовые клиенты 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 (короткий код для кода клиента).
Ручки
Опись: список ручек, чей ключ пришёл и какой у него предел. Удобно первым запросом — сразу видно, что ключ живой.
Последний матч игрока на 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/"
}
}
Во что игрок был одет в последнем матче и чем это рисовать: класс, команда, предметы, краски, необычные эффекты — и следом готовое описание модели: тело, части с путями моделей, что скрыть под ними.
{
"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 и ссылкой на страницу, с которой сборка начнётся.
Разбор матча: формат и режим, длина, раунды, уберное преимущество, лучший игрок с разбором оценки, ролауты и по каждому игроку — роль, смерти по видам, позиция относительно команды, размены.
{
"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 } } ]
}
Моменты матча: вид момента, подпись, границы в тиках и секундах, кто его сделал и ссылка на хайлайты этого игрока.
{
"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, и это честнее русской строки под видом
английской.
Что разобрано недавно, свежее сверху. Параметры:
since — отдать только собранное позже этого времени
(unix), limit — сколько строк, до 200. Так устроен опрос: запомните время
последнего обращения и спрашивайте только то, что появилось после него.
{
"matches": [ { "match": "1503428", "map": "koth_bagel_rc12",
"log": "4116918", "played_at": 1788792930, "at": 1789824927 } ],
"since": 0, "count": 1
}
Карты, для которых у нас есть геометрия: только на них рисуется 3D-повтор.
Заказать разбор матча по ссылке — на
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.
Чей это ключ, предел частоты и сколько израсходовано из суточной квоты. Квота считается по аккаунту Steam, а не по ключу.
Ключи и пределы
- 30 запросов в минуту на ключ. Считается по
ключу, а не по адресу: за перебор отвечает тот, кто ключ завёл, а не сосед
по одному адресу. Перебор — 429 и заголовок
Retry-After. - 5 живых ключей на аккаунт. Ключ — это имя для предела частоты, и десяток ключей у одного человека означал бы десятикратный предел. Отозванные не считаются.
- Ключ показывается один раз. У нас лежит только его отпечаток; утёкшая база ключей не выдаёт. Потеряли — отзовите и заведите новый, это бесплатно.
- Отзыв мгновенный. Отозванный ключ перестаёт открывать что-либо со следующего же запроса.
- Ключ открывает только
/api/v1/. Ни заказ разбора, ни 3D-повтор, ни управление самими ключами им не открываются: ключи заводит и отзывает человек со страницы, куками. - На техработах API закрыт вместе со всем сайтом —
ответ 503 и
Retry-After.
Коды ответов
| Код | err | Что случилось |
|---|---|---|
| 200 | — | Всё хорошо. |
| 401 | — |
Ключа нет, он не тот или отозван. Три случая отвечают одинаково — разница подсказала бы перебирающему. |
| 404 | noRoute |
Такой ручки нет. Список — в /api/v1. |
| 404 | noGames |
У этого игрока нет матчей на logs.tf. |
| 404 | noLook |
Набора этого игрока мы ещё не собирали. |
| 503 | busy |
Сервер уже ищет демки для других. Повторите через минуту. |
| 404 | noReport |
Такого разбора у нас нет. Закажите его на главной — ключом заказ не запускается. |
| 400 | badLook |
Набор этого игрока записан так, что модель по нему не собирается. Редкий случай; напишите нам. |
| 429 | — |
Слишком часто. Ждите Retry-After секунд. |
| 503 | upstream |
logs.tf не отвечает. Повторите позже. |
| 503 | items |
Схема предметов игры сейчас недоступна. Повторите позже. |
| 503 | — |
Технические работы на сайте. |
Ответы ручек ключей
Ручки /api/keys и
/api/keys/revoke вызывает эта страница, куками
вошедшего. Ключом они не открываются, но их отказы видны в консоли
браузера — вот что они значат.
| Код | err | Что случилось |
|---|---|---|
| 403 | offsite |
Запрос пришёл не с этой страницы. Так мы не даём чужому сайту выписать ключ вашими куками. |
| 404 | noKey |
Такого вашего ключа нет: он уже отозван или никогда вам не принадлежал. |
| 409 | toomany |
Живых ключей уже 5 — отзовите ненужный. |