Документация SportWire API
REST-интерфейс к спортивным данным: расписание, live, события,
статистика, xG, составы, коэффициенты, таблицы, история и справочники. Базовый URL —
https://api.sportwire.ru. Все ответы — JSON в UTF-8; даты — ISO-8601 (UTC).
Обзор
REST API поверх единой модели данных: эндпоинты /v1/…, ключ в заголовке X-API-Key.
Каждая сущность (матч, турнир, команда, игрок) — единая и недублированная; названия доступны на нескольких языках, включая русский (см. Локализация).
Аутентификация
API-ключ выдаётся мгновенно в личном кабинете (тариф Trial — бесплатно).
REST — заголовок:
curl https://api.sportwire.ru/v1/sports \ -H "X-API-Key: ВАШ_КЛЮЧ"
Для встраивания картинок в <img> ключ можно передать как ?key=.
Лимиты и тарифы
| Тариф | Запросов / сек | Цена |
|---|---|---|
| Trial | 1 | бесплатно |
| Starter | 5 | ₽15 990 / мес |
| Pro | 20 | ₽39 900 / мес |
| Business | 100 | ₽89 900 / мес |
Текущий расход виден в личном кабинете. При превышении частоты запросов (запросов/сек) — ответ 429. Суточных и месячных лимитов нет.
Ошибки
| Код | Значение |
|---|---|
401 | Ключ не передан или недействителен |
404 | Сущность не найдена |
422 | Неверные параметры запроса |
429 | Превышена частота запросов (лимит запросов/сек тарифа) |
Локализация (RU)
Названия лиг, турниров, команд и стран отдаются объектом name_i18n с ключами по локали —
русский лежит рядом с оригиналом:
"name_i18n": { "en": "Premier League", "ru": "Премьер-лига" }
Берите name_i18n.ru для русского интерфейса или name_i18n.en для
оригинала. Где русского ещё нет — поле может отсутствовать (заполнение идёт постоянно).
В лентах матчей русское название лиги приходит отдельным плоским полем
tournament_ru рядом с tournament — и в
/v1/events, и в карточке
/v1/event/{id}. Второй запрос за русским названием турнира
делать не нужно.
Виды спорта
Список видов спорта с идентификаторами и названиями.
{ "sports": [
{ "id": 1, "slug": "football", "name_i18n": {"en":"Football","ru":"Футбол"} },
{ "id": 2, "slug": "basketball", "name_i18n": {"en":"Basketball","ru":"Баскетбол"} }
]}
Матчи и расписание
Матчи с фильтрами. Параметры:
| Параметр | Описание |
|---|---|
sport | slug вида спорта (например football) |
date | дата YYYY-MM-DD (по времени начала) |
status | scheduled · live · finished |
top | true — только сильнейшие турниры: importance ≥ 90. Это топ-дивизионы ведущих стран, континентальные клубные турниры и соревнования сборных. Вторые дивизионы, региональные и любительские лиги сюда не входят — для них задайте свой порог через min_importance (например min_importance=82 вернёт и вторые дивизионы). На фиде по дате топ-матчи идут первыми; в ответе у каждого матча есть importance (0–100).Порог поднят с 50 до 90 17.08.2026: прежний пропускал в «топ» вторые дивизионы наравне с сильнейшими лигами. Если вам нужна прежняя широта — min_importance=50. |
min_importance | свой порог важности лиги (0–100) |
limit / offset | пагинация (limit до 1000) |
GET /v1/events?sport=football&date=2026-06-02
{ "count": 312, "events": [{
"id": 1662352, "scheduled_start": "2026-06-02T18:00:00Z",
"status": "finished", "round_name": null,
"tournament": "Premier League", "tournament_ru": "Премьер-лига",
"sport": "football",
"participants": [
{"participant_id": 4504, "name": "Liverpool", "side": "home",
"score": 2, "image_url": "/v1/participant/4504/image"},
{"participant_id": 4509, "name": "Arsenal", "side": "away",
"score": 1, "image_url": "/v1/participant/4509/image"}
]
}]}
Не у всех событий две стороны. Мотоспорт и биатлон — это заезды: в
participants[] приходят все участники (до 90+), у каждого заполнено
position (место), а side и score —
null. Массив отсортирован по месту. Подробности и сам протокол заезда —
раздел «Гонки».
Пустой ответ объясняет себя. Сезонные виды (биатлон — с апреля по
ноябрь) законно молчат месяцами. Если фильтрам не соответствует ни одно событие и
задан sport=, в ответ добавляются next_event_at /
last_event_at (ISO-время ближайшего будущего и последнего прошедшего
события вида, либо null) и человекочитаемый hint.
В непустых ответах этих полей нет.
Live-матчи
Только идущие сейчас матчи (опционально ?sport=). Счёт и статус обновляются каждые ~20 секунд.
Особое завершение матча — status_detail
Значений у status пять: scheduled · live ·
finished · postponed · cancelled. Снятие по травме,
неявка и присуждённый результат попадают в тот же finished, что и честно
доигранный матч, — по статусу их не отличить. Чем именно кончился матч, лежит в отдельном
поле status_detail.
status_detail | Что произошло | Матчей в базе |
|---|---|---|
retired | снятие по ходу матча (травма, отказ продолжать) — сыгранная часть осталась в счёте по партиям | 8 113 |
walkover | неявка: матч не игрался, результат присуждён | 1 316 |
removed | участник снят (метка источника Removed) | 68 |
defaulted | дисквалификация участника по ходу матча | 15 |
awarded | результат присуждён — техническое поражение | 6 |
abandoned | матч прерван и не доигран | 7 |
Замер 02.08.2026 по всей базе: 9 525 матчей с признаком — 9 519 со
status: "finished" и 6 со status: "cancelled". Значения — это
нормализованные метки источника (sofascore отдаёт числовой код завершения, flashscore —
метку стадии); своих смыслов мы к ним не добавляем и наугад не переименовываем. Чаще всего
признак встречается там, где снятие в порядке вещей: теннис — 7 659 retired и
555 walkover, бадминтон — 363 walkover, настольный теннис —
249 retired.
status_detail в ответе нет вовсе — ни null, ни пустой
строки. Проверяйте наличие ключа, а не его значение; ветку «особое завершение» стройте
на присутствии поля.Поле приходит на тех же ручках, что и сам матч:
| Ручка | Что вернёт |
|---|---|
GET /v1/events | лента матчей (и её частный случай /v1/events/live) |
GET /v1/event/{id} | карточка матча |
GET /v1/tournament/{id}/events | календарь турнира по сезонам |
GET /v1/sync | инкрементальная синхронизация |
// снятие по ходу матча (теннис): счёт по партиям остался в extra.period_scores
{ "id": 12088167, "status": "finished", "status_detail": "retired",
"participants": [ {"side":"home","score":1}, {"side":"away","score":0} ] }
// обычное завершение — КЛЮЧА status_detail НЕТ
{ "id": 1662352, "status": "finished",
"participants": [ {"side":"home","score":2}, {"side":"away","score":1} ] }
- Матч, прерванный ПРЯМО СЕЙЧАС, приходит как
live, и отдельного признака «идёт, но остановлен» у нас нет. Признак появляется только когда источник закрыл матч: тогда этоabandoned— обычно соstatus: "cancelled"(5 матчей из 7), реже сfinished. Пока матч идёт, отличить остановку от обычного хода игры по API нельзя. - Фильтра по
status_detailв/v1/eventsнет — параметрstatusотбирает только по пяти статусам. Отбирайте признак на своей стороне. - Итоговый счёт у снятия бывает нулевым. У
removed,defaultedи частиretiredвparticipants[].scoreстоит 0:0, хотя партии игрались — сыгранное лежит вextra.period_scores. Показывать такой матч как «0:0» нельзя: это не результат, а отсутствие присуждённого счёта.
Детализация матча
Полная карточка матча со всей собранной детализацией:
Соперники ещё не определены. У матчей плей-офф, где сетка не сыграна,
массив participants пуст, а рядом стоит
"participants_pending": true. Это НЕ потеря данных: источник
сообщает «TBD», и мы не заводим команду с таким названием, чтобы на витрине
не появлялись призраки. Матч действительно запланирован — запросите его
ближе к дате, стороны появятся. У обычных матчей поля нет вовсе.
Управление весом ответа. Параметр exclude убирает
перечисленные через запятую поля верхнего уровня — карточка матча бывает
объёмной, и лишнее проще не получать, чем игнорировать. Например
?exclude=odds_bookmakers,odds_best убирает роспись котировок и
сокращает ответ примерно на треть, а
?exclude=odds_bookmakers,odds_best,commentary,shotmap — вдвое.
Поля id, status, sport и
scheduled_start отдаются всегда. Без параметра ответ прежний,
менять существующие запросы не нужно.
{ "id": 1662352, "status": "finished", "sport": "football",
"tournament": "Premier League", "tournament_ru": "Премьер-лига",
"season_label": "25/26",
"venue": "Anfield", "venue_city": "Liverpool", "venue_capacity": 53394,
"referee": "Michael Oliver", "attendance": 53221,
"participants": [ … счёт по сторонам … ],
"incidents": [ {"minute":23,"kind":"goal","period":"1H","player_name":"…","assist_name":"…"} ],
"statistics": [ {"period":"ALL","group_name":"Possession",
"stat_name":"Ball possession","home_value":58,"away_value":42} ],
"lineups": [ {"side":"home","player_participant_id":…,"position":"GK",
"shirt_number":"1","is_substitute":false,"formation":"4-3-3","stats":{…}} ],
"player_stats":[ {"participant_id":…,"player_name":"…","side":"home","position":"M",
"stats":{"rating":8.1,"accuratePass":76,"keyPass":4,"totalTackle":1,
"expectedGoals":0.32,"expectedAssists":0.42,"touches":103}} ],
"coaches": [ {"side":"home","coach_name":"…","coach_name_ru":"…"} ],
"missing_players":[ {"side":"home","player_name":"…","position":"D",
"type":"missing","status":"…","reason_text":"…"} ],
"shotmap": [ {"player":{…},"xg":0.074,"shotType":"goal","situation":"assisted"} ],
"momentum": { "graphPoints":[…] },
"odds": [ {"market_name":"Full time","choice_name":"1","fractional_value":"1.73",
"bookmaker":"…","opening_value":"1.80","movement":"down"} ],
"odds_bookmakers":[ {"bookmaker":"…","market":"1x2","side":"home","odds":2.77,
"opening":2.80,"is_live":false} ],
"odds_best": { "1x2":{"home":{"odds":2.77,"bookmaker":"…"},"draw":{…},"away":{…}},
"total_2_5":{…}, "btts":{…}, "double_chance":{…} },
Коэффициенты букмекеров: как собираются и сколько живут
Что отдаём. Максимально широкую роспись по каждому букмекеру:
исход (1x2), тотал, фора, двойной шанс, «обе забьют», точный счёт,
чёт/нечет — всё, что букмекер публикует. Каждая линия отдаётся отдельной
строкой с рынком, периодом, значением линии и стороной.
Как обновляются. Мы опрашиваем букмекеров непрерывно, и коэффициент
каждого рынка перезаписывается поверх прежнего, как только он изменился —
и до матча, и в лайве. В ответе вы всегда видите актуальное значение
(odds), а не срез какой-то давности.
Что такое opening. Первое значение, которое мы увидели по
этой линии — как правило прематчевое. Оно фиксируется один раз и дальше не
меняется, что и позволяет считать движение линии: поле movement
показывает направление относительно открытия. Промежуточных значений между
открытием и текущим мы не храним — это осознанное решение, а не ограничение.
Сколько живут. Коэффициенты матча хранятся 7 суток после начала матча, затем удаляются полностью. Отсчёт ведётся от времени начала матча, а не от времени записи, поэтому линия, выставленная за месяц до игры, доживает до самой игры. Если вам нужны коэффициенты сыгранных матчей для бэктеста — забирайте их в течение недели после события.
Снятые линии. Если букмекер перестал предлагать линию (снял рынок или приостановил приём), она убирается из ответа в ближайшем цикле — вы не получите коэффициент, по которому уже нельзя поставить.
Какие книги собираем. Каждая книга отдаётся отдельно, под своим
bookmaker, и несёт поле jurisdiction:
| Юрисдикция | jurisdiction | Книги |
|---|---|---|
| Россия (лицензия, приём через ЦУПИС) | ru |
betcity, fonbet, leon, marathon,
olimp, pari, tennisi, winline,
zenit |
| Беларусь | Беларусь |
fonbet_by, winline_by |
| Казахстан | Казахстан |
fonbet_kz, ubet_kz |
Почему Фонбет РФ, РБ и РК — это три РАЗНЫЕ книги. Это разные юрлица с самостоятельной линией: на один и тот же матч у них расходятся и коэффициенты, и состав рынков (у белорусской и казахстанской росписи есть точный счёт, двойной шанс, индивидуальные тоталы и лесенка альтернативных линий, которых в российской нет). Поэтому они никогда не смешиваются в одну книгу — сравнение линий разных стран это самостоятельный продукт, а не «дубль» одной конторы.
Где зарубежные книги НЕ участвуют. В
odds_bookmakers вы получаете все книги. А вот odds_best
(«лучшая цена») и весь value-контур — прогноз,
value_bets — считаются только по книгам РФ: лучшая цена должна
быть той, по которой клиент реально может поставить, а не лучшей в мире. Если вам
нужна лучшая цена по всем странам — стройте её сами из
odds_bookmakers по полю jurisdiction.
"best_players":[ {"side":"home","rating":"7.6","player_name":"…","position":"M"} ],
"win_probability": { "homeWin":52, "draw":27, "awayWin":21 },
"votes": { "vote":{"vote1":…,"voteX":…,"vote2":…} },
"highlights": [ {"title":"…","url":"…","thumbnail":"…"} ],
"average_positions": { "home":[{"player_name":"…","x":51.2,"y":33.0}], "away":[…] },
"player_heatmaps": { "812041":[[56,7],[56,17],[88,10],…] },
"player_actions": { "812041":{ "passes":[{"playerCoordinates":{"x":56.6,"y":7.3},
"passEndCoordinates":{"x":63.7,"y":17.1},"outcome":true,"keypass":false}],
"dribbles":[…], "defensive":[…] } },
"tv_channels": { "ES":[663], "BR":[7548] },
"points_history": [ {"tab":"Set 1","points":[…]} ],
"point_by_point": [ {"set":1,"games":[{"game":13,"score":{…},"points":[…]}]} ],
"race": { "series":"Formula 1", "session":"Race", "distance":"305.3 km",
"classification":[ {"position":1,"participant_id":1913314,"name":"…","time":"1:27:32.841"} ] },
"fight": { "method":"TKO", "finish_round":3 },
"weather": { "temperature_c":18, "conditions":"…" },
"commentary": [ {"minute":45,"text":"Гол! …","text_en":"Goal! …","type":"goal","side":"home","player_name":"…"} ],
"meta": { "last_detail_at":"…", "last_checked_at":"…", "completeness":{…} }
}
В примере выше ключа stage НЕТ — и это штатный, самый частый для лиговых
матчей случай: источник по такому матчу прислал только номер, а по голому числу мы не решаем,
тур это или внутренний код кубкового раунда. Когда стадия названа, объект появляется рядом:
"stage": {"type":"playoff","code":"round_of_16","name":"Round of 16","name_ru":"1/8 финала"}.
Как читать и где она есть — раздел «Стадия турнира у матча».
Состав полей зависит от вида спорта и наличия данных у матча. Ключевые блоки:
player_stats[].stats — полная статистика по каждому игроку (рейтинг, передачи, отборы,
xG/xA, касания, спорт-специфичные метрики); coaches — тренеры сторон;
missing_players — травмы/дисквалификации (type + reason_text);
referee, venue_city, venue_capacity, attendance —
справка о матче; best_players (MVP), win_probability, votes,
highlights (видео), average_positions, tv_channels,
player_heatmaps (тепловая карта касаний игрока: ключ — id игрока,
значение — точки [x, y] на поле 0–100) и player_actions (координатные
действия игрока: пасы с точками начала/конца, исходом и признаком key pass, дриблинг,
перехваты и подборы; топ-футбол, модуль advanced),
points_history (ряд очков внутри партии — теннис/снукер/дартс/волейбол),
point_by_point (сеты → геймы → очки с подающим; там же счёт
тайбрейка), fight (метод/раунд — ММА),
race (протокол заезда — мотоспорт и биатлон).
Коэффициенты: odds — усреднённые рыночные линии (включая зарубежные конторы) с движением и опенингом;
odds_bookmakers — отдельные линии по каждому рынку: книги РФ (ЦУПИС), Беларуси и Казахстана,
у каждой строки своя jurisdiction (подробнее);
odds_best — лучшая цена по каждому исходу (line-shopping) среди книг РФ. Пре-матч и лайв; отдельный подключаемый модуль «Коэффициенты БК».
Жизненный цикл линий БК. Пре-матч линии появляются заранее — как только контора открывает
роспись на событие (обычно за несколько дней до старта). Лайв-линии приходят с началом матча (доступны
не у всех контор — часть отдаёт только пре-матч). Цена обновляется по мере движения рынка; стартовая
цена (opening_value) и направление движения (movement) сохраняются на протяжении
всего времени. Снятые/приостановленные конторой линии из ответа исчезают. После завершения матча линии
контор по нему удаляются вскоре после финала — актуальны только события в игре и предстоящие.
Блок meta.completeness показывает, какие секции заполнены. Глубина зависит от уровня лиги.
Счёт по партиям и периодам — extra.period_scores
Разбивка итогового счёта по партиям (теннис, волейбол), таймам (футбол), периодам (хоккей),
иннингам (бейсбол). Две стороны — home и away, ключи одинаковые.
// финал Roland Garros 2025, Синнер — Алькарас (event 8416559): 4-6, 6-7(4), 6-4, 7-6(3), 7-6(10-2)
"participants": [ {"side":"home","score":2}, {"side":"away","score":3} ],
"extra": { "period_scores": {
"home": { "period1": 6, "period2": 7, "period3": 4, "period4": 6, "period5": 6,
"period2TieBreak": 7, "period4TieBreak": 3, "period5TieBreak": 2,
"current": 2, "normaltime": 2 },
"away": { "period1": 4, "period2": 6, "period3": 6, "period4": 7, "period5": 7,
"period2TieBreak": 4, "period4TieBreak": 7, "period5TieBreak": 10,
"current": 3, "normaltime": 3 } } }
| Ключ | Описание |
|---|---|
periodN | счёт стороны в N-й партии / тайме / периоде / иннинге. Нумерация с 1, число партий не ограничено списком — пять сетов тенниса и девять (плюс экстра) иннингов бейсбола приходят целиком |
periodNTieBreak | счёт тайбрейка в N-й партии. Появляется только там, где тайбрейк игрался: в примере выше вторая партия 7:6 сложилась из тайбрейка 7:4, а решающая — из чемпионского тайбрейка 2:10 |
current | итог стороны — тот же, что в participants[].score |
normaltime · overtime · penalties | основное время, овертайм, серия пенальти (командные виды) |
periodN, дают ровно participants[].score. Исключение —
особое завершение (снятие, неявка): там присуждённого счёта нет, см.
status_detail.
Историю тайбрейков мы доливаем проходом по архиву — на матчах последних сезонов и на всех крупных турнирах ключи
periodNTieBreak уже есть, в глубине архива
появляются по мере прохода. Отсутствие ключа означает «ещё не добрали», а не «тайбрейка не было»;
сам факт тайбрейка всегда виден по партии 7:6.По-очковая раскладка — point_by_point
Розыгрыш за розыгрышем: партии → геймы → очки, с указанием подающего. Приходит ключом
point_by_point в карточке матча — теннис, настольный теннис, бадминтон, дартс.
Итоговый счёт тайбрейка проще взять из extra.period_scores
(periodNTieBreak); здесь же видно, как тайбрейк складывался очко за очком.
"point_by_point": [
{ "set": 1,
"games": [
{ "game": 13, // тайбрейк — гейм, сыгранный при 6:6
"score": { "homeScore": 7, "awayScore": 6, // счёт по геймам ПОСЛЕ этого гейма
"serving": 1, "scoring": 1 }, // подающий · кто выиграл гейм
"points": [ …,
{ "homePoint": "7", "awayPoint": "3", // ← последняя точка = счёт тайбрейка
"homePointType": 0, "awayPointType": 0, "pointDescription": 0 } ] },
{ "game": 12, // обычный гейм
"score": { "homeScore": 6, "awayScore": 6, "serving": 1, "scoring": 1 },
"points": [ { "homePoint": "15", "awayPoint": "0", "homePointType": 1, "awayPointType": 5 },
{ "homePoint": "40", "awayPoint": "15", "homePointType": 1, "awayPointType": 5 } ] } ] } ]
| Поле | Описание |
|---|---|
set | номер партии |
games[].game | номер гейма в партии |
score.homeScore / awayScore | счёт по геймам в партии после того, как этот гейм доигран (не счёт очков) |
score.serving | кто подавал в гейме: 1 = home, 2 = away. Значения -1, 0 и отсутствие ключа означают «источник не сказал» — таких мало: на 223 661 гейм тенниса приходится 191 гейм без ключа, 94 с -1 и 30 с 0 |
score.scoring | кто выиграл гейм: 1 = home, 2 = away, -1 = не определён (гейм ещё идёт — типично для live) |
points[] | очки гейма по порядку. homePoint / awayPoint — счёт после розыгрыша: в обычном гейме "0" · "15" · "30" · "40" · "A", в тайбрейке — числами "1", "2", "3"… |
homePointType / awayPointType / pointDescription | сырые числовые коды источника. Расшифровки к ним источник не даёт, поэтому и мы их не расшифровываем — отдаём как есть |
Порядок массивов — обратный. Партии и геймы приходят от последнего к
первому: в массиве первой стоит текущая (последняя сыгранная) партия, внутри неё — последний
гейм. Замер за 2 суток: у 742 партий из 764 геймы идут по убыванию, у остальных 22 гейм всего
один; первой в массиве партия №1 оказалась лишь в 13 случаях из 340. Мы порядок источника не
переставляем — сортируйте по set и game у себя.
Как достать счёт тайбрейка
Тайбрейк — это гейм, в котором очки считаются числами ("1",
"2", "3"…), а не «0 / 15 / 30 / 40 / A». Счёт тайбрейка = последняя
точка такого гейма: homePoint: "7", awayPoint: "3" → 7:3.
Не отбирайте тайбрейк по счёту гейма. Правило «score.homeScore
и score.awayScore оба ≥ 6» на замере за 14 дней (теннис, 101 111 геймов с
раскладкой) отбирает 2 674 гейма, из которых тайбрейков только 1 468: остальные 1 206 —
обычные геймы, доигранные при счёте 6:6. И наоборот, 139 тайбрейков это правило теряет —
решающая партия в формате супер-тайбрейка идёт отдельным геймом со счётом по геймам 1:0.
Отбор по виду очков даёт 1 607 тайбрейков — всё, что есть в этой выборке.
Честное покрытие
Раскладку отдаёт источник, и собирается она недавно, поэтому у архива её практически нет. Замер 02.08.2026 по завершённым матчам за 30 дней:
| Вид спорта | Завершённых за 30 дней | С раскладкой | Доля |
|---|---|---|---|
| Настольный теннис | 36 603 | 31 565 | 86% |
| Теннис | 13 909 | 9 769 | 70% |
| Дартс | 1 014 | 434 | 43% |
| Бадминтон | 539 | 449 | 83% |
На годовом окне картина другая: теннис — 10 273 матча с раскладкой из 129 701 завершённого (7,9%), настольный теннис — 31 840 из 353 961 (9,0%). Почти всё это июль–август 2026: за июнь у тенниса раскладка есть у 16 матчей из 15 542. Строить работу на архивной раскладке нельзя — по старым матчам ключа просто не будет.
Живые матчи покрыты заметно хуже завершённых: раскладка наполняется по ходу игры и появляется не у всех. Срез 02.08.2026 — она была у 1 идущего теннисного матча из 23. Это снимок одного момента, а не средняя за период, но порядок величины он показывает честно: рассчитывать на раскладку у любого live-матча нельзя.
point_by_point и points_history — разные ключи.
points_history — плоский ряд очков внутри партии от второго источника:
[{"tab":"Set 1","points":[{"h":"1","a":"0","scored":1}, …]}], без разбивки на
геймы и без подающего. point_by_point — партии → геймы → очки, с подающим и
счётом тайбрейка. Ключи независимы: за 30 дней у тенниса раскладка есть у 9 769 матчей,
points_history — у 5 389, оба сразу — у 4 582, хотя бы один — у 10 576 из
13 911. Берите тот, что пришёл, и не ждите обоих. Оба ключа — в модуле «advanced».Гонки — протокол заезда race новое
Мотоспорт (Формула 1/2/3, Формула E, MotoGP · Moto2 · Moto3, WRC, IndyCar, NASCAR, DTM, Supercars, спидвей) и биатлон приходят не как матч. У заезда нет сторон и счёта: результат — это таблица участников с местом, временем и отставанием. Поэтому:
participants[]— все участники заезда (от 2 до 90+), у каждогоposition= место, аsideиscore—null. Массив отсортирован по месту. Логика «взятьside == "home"» на гонке не найдёт ничего — это сделано намеренно: заезд не притворяется матчем со счётом 0:0;race— сам протокол: серия, сессия, дистанция и классификация.
"race": {
"series": "Formula 1", // серия/дисциплина: "MotoGP", "WRC", "Sprint - Men", "Relay - Women"
"session": "Race", // сессия уик-энда: Race · Qualification 1-3 · Practice 1-3 · Warm Up
"distance": "305.3 km", // дистанция заезда
"classification": [
{ "position": 1, // место в заезде (по нему массив отсортирован)
"participant_id": 1913314, // наш id — джойнится с participants[] этого же матча
"name": "Schumacher M.",
"time": "1:27:32.841", // время победителя / участника
"gap": "+0.000", // отставание от лидера
"grid": 1, // стартовая позиция (мотоспорт)
"laps": 66, // пройдено кругов
"laps_behind": null, // кругов позади лидера
"pit_stops": null, // число пит-стопов
"team": "Ferrari", // команда (мотоспорт)
"country": "Germany",
"shooting_misses": null, // промахи на стрельбе / штрафные круги (биатлон)
"spare_rounds": null } ] } // запасные патроны (биатлонная эстафета)
| Поле | Описание |
|---|---|
position | место в заезде. null у всех строк = это ещё не результат, а стартовый список (предстоящая или отменённая сессия) |
participant_id | наш постоянный id гонщика (или сборной — в биатлонной эстафете участник это команда страны, а не человек). Тот же id, что в participants[] и в /v1/participant/{id} |
time / gap | время и отставание строками, ровно в той форме, в какой их даёт источник ("1:27:32.841", "+13.290", у биатлона — "22:53.1", "13.7"). В секунды не пересчитываем и формат не выравниваем |
grid | стартовая позиция. Есть у сессий, где источник её печатает (гонки, не квалификации) |
laps / laps_behind | пройдено кругов и на сколько кругов отстал от лидера (у сошедших/отставших на круг) |
pit_stops | число пит-стопов за гонку. Времени и раскладки по кругам источник не даёт |
team | команда/конюшня (мотоспорт). У биатлона всегда null |
shooting_misses | биатлон: промахи на стрельбе. В спринте, гонке преследования и масс-старте каждый промах = штрафной круг, то есть «промахи» и «штрафы» здесь одно и то же число — второго поля нет, потому что второго числа нет и у источника |
spare_rounds | биатлон, только эстафеты: израсходованные запасные патроны |
Ключи в строке всегда одни и те же — незаполненные приходят как null,
а не пропадают. Что именно заполнено, зависит от вида: мотоспорту источник даёт круги,
стартовую позицию и пит-стопы, биатлону — стрельбу.
Бой — метод победы method
У боя нет счёта: результат — это победитель, раунд окончания и метод. Метод
приходит полем method верхнего уровня (и дублируется внутри
fight), раунд — полем final_round. Значения приведены к
одному словарю: мы собираем бои из нескольких источников, и каждый писал их
по-своему — «единогласное решение» встречалось четырьмя написаниями. Наружу
отдаётся только список ниже, поэтому по method можно фильтровать.
| Значение | Что означает |
|---|---|
UD | единогласное решение судей |
SD | раздельное решение |
MD | решение большинства |
PTS | победа по очкам — источник не назвал тип решения (кикбоксинг, бокс) |
KO | нокаут |
TKO | технический нокаут, включая остановку врачом |
KO/TKO | нокаут, но источник не различил, какой именно. Значение честное, а не мусорное: так эти бои пришли, и додумывать за источник мы не будем. Встречается у части архива UFC |
SUB | сдача — болевой или удушающий приём |
DQ | дисквалификация |
NC | бой признан несостоявшимся (no contest) |
CNC | боец не смог продолжить. Не то же, что NC — это разные исходы, и мы их не схлопываем |
FF | неявка или отказ от боя |
INJ | остановлен из-за травмы |
OVERTURNED | результат впоследствии отменён |
OTHER | источник назвал исход «иным» |
Метода нет — приходит null: у части боёв (как правило, мелкие
промоушены) источник его просто не печатает. Пустую строку или выдуманное значение
вместо null мы не отдаём.
Честное покрытие
Замер 03.08.2026 по всей базе: 2 101 сессия, 44 805 строк классификации, 56 турниров.
| Вид | Сессий | Строк | Период | Время | Отставание | Круги | Старт. поз. | Пит-стопы | Стрельба |
|---|---|---|---|---|---|---|---|---|---|
| Мотоспорт | 1 985 | 38 278 | 07.03.2004 — 09.08.2026 | 86% | 87% | 94% | 19% | 12% | — |
| Биатлон | 116 | 6 527 | 09.02.2017 — 21.02.2026 | 96% | 96% | — | — | — | 84% |
Внутри мотоспорта основной массив — Формула 1 (1 915 сессий с 2004 года,
по 17–23 сезона на Гран-при); MotoGP · Moto2 · Moto3, WRC, IndyCar, NASCAR, DTM, Формула E,
Формула 2/3, Supercars и спидвей собираются с 26.07.2026, поэтому истории у них пока нет —
только текущий сезон. Стартовая позиция и пит-стопы стоят низко не из-за пропусков сбора:
источник печатает их только в гонках, а гонка — одна сессия уик-энда из семи-восьми
(310 сессий типа Race против 1 675 квалификаций, практик и прогревов).
Запасные патроны есть у 13 178 строк — это ровно эстафеты, в личных гонках их не бывает.
- Биатлон — только Олимпиады и чемпионаты мира. Кубка мира и Кубка IBU у нас нет: источник их не отдаёт. И зимний вид законно молчит с апреля по ноябрь — пустой календарь летом это межсезонье, а не сбой;
- биатлон: нет посегментной стрельбы (лёжа/стоя), рубежей, скорости на круге и промежуточных отсечек — только итог гонки;
- мотоспорт: нет покруговки, телеметрии, стратегии шин, интервалов по кругам, штрафов и причин схода;
- Строки без места — это стартовый список, а не результат. У ещё не состоявшейся
или отменённой сессии
raceприходит, ноposition,timeиgapу всехnull— есть только заявка (имя, команда, страна). На 03.08.2026 таких сессий 33: 31 предстоящая и 2 отменённые. Отличайте результат от заявки по наличиюposition, а не по статусу матча; incidents,statistics,lineupsу заезда пусты по устройству — их место занимаетrace. Вmeta.completenessдля этого есть отдельный флагrace.
Турнирные таблицы новое
Таблица сезона: позиция, игры, В/Н/П, забито/пропущено, очки. type =
total (по умолч.) · home · away · form;
season — id сезона (иначе самый свежий).
{ "tournament":"Premier League", "season_label":"25/26", "standing_type":"total",
"table": [ {"position":1,"participant_id":42,"name":"Arsenal","played":38,
"wins":26,"draws":7,"losses":5,"scores_for":71,"scores_against":27,"points":85} ] }
Бомбардиры новое
Таблица бомбардиров сезона: ранг, игрок, команда, голы. season — id сезона
(иначе самый свежий); limit ≤ 500.
{ "tournament_id":47325, "count":20,
"scorers": [ {"rank":1,"participant_id":…,"player_name":"…","team_name":"…","goals":8} ] }
Турнирная сетка (кубки) новое
Сетка плей-офф для кубковых турниров: раунды → пары (команды, счёт, результат, сыграно).
season — id сезона (иначе самый свежий). Доступно для футбольных/баскетбольных кубков.
Стадию отдельного матча смотрите в поле stage.
{ "tournament":"FA Cup 25/26", "rounds": [
{ "name":"...", "matchups": [
{"finished":true,"result":"1:0","home_score":"1","away_score":"0",
"teams":[{"name":"Blackpool","code":"BLP"},{"name":"Scunthorpe United","code":"SCU"}]} ] } ] }
Стадия турнира у матча новое
Где матч стоит внутри турнира: квалификация, групповой этап, 1/8 финала, финал — либо тур
регулярного сезона. Приходит объектом stage у матча — в /v1/events,
/v1/event/{id} и /v1/sync. Поле необязательное: если источник
стадию не назвал, ключа stage в ответе не будет — см. «Голое число — не стадия».
// плей-офф — именованная стадия (теннис ITF, кубки)
"stage": { "type":"playoff", "code":"round_of_16",
"name":"Round of 16", "name_ru":"1/8 финала" }
// квалификация кубка
"stage": { "type":"qualification", "code":"preliminary",
"name":"Preliminary", "name_ru":"Предварительный раунд" }
// регулярный сезон — источник НАЗВАЛ стадию туром («Round 19»)
"stage": { "type":"regular", "code":"regular",
"name":"Round 19", "name_ru":"19-й тур", "round":19 }
// регулярный сезон, но источник стадию НЕ назвал — прислал одно число.
// Класс выведен нами по структуре сезона (см. ниже), поэтому name/code нет:
// выдумывать подпись за источник мы не будем
"stage": { "type":"regular", "name_ru":"2-й тур", "round":2 }
// источник прислал только число, а вывести класс не из чего (кубковая сетка,
// теннис, бейсбол, товарищеский матч) -> ключа stage в ответе НЕТ ВОВСЕ
| Поле | Описание |
|---|---|
type | машинный класс стадии, ровно одно из пяти: regular · qualification · group · playoff · placement. Список закрытый — новых значений без анонса не появится. Ключа нет, если стадия не распозналась однозначно. Ориентируйтесь на него, а не на разбор английской строки. Финал — это type: "playoff" с code: "final"; отдельного типа final не существует |
code | машинный код КОНКРЕТНОЙ стадии: final · semifinal · quarterfinal · round_of_16 · round_of_32 · round_of_64 · round_of_128 · round_of_256 · third_place · playoff · qualification · preliminary · group · regular · placement. Считается по машинной шкале сетки, а не по разбору строки. ⚠️ round_of_16 — это 16 команд = русское «1/8 финала»: английский счёт по командам, русский по парам |
name | название стадии как его даёт источник, английское: Round of 16, 1/16-finals, Semi-finals, Qualifications, Preliminary, Round 19 |
name_ru | русское название по словарю. Незнакомое название не переводим — ключа просто нет, показывайте name |
round | номер тура. Приходит только при type: "regular" (и при квалификации с явным номером круга — Qualification round 2). У названной стадии кубка номер из фида служебный — у Preliminary в MOL Cup там 300, у Qualifications — 10, — и наружу он не отдаётся вовсе, чтобы его нельзя было показать как «тур 300». Сырое число фида, если оно вам нужно для сортировки, остаётся в extra.round |
Где приходит стадия и как по ней фильтровать
Объект stage приходит на четырёх ручках — везде в одной и той же форме:
| Ручка | Что вернёт |
|---|---|
GET /v1/event/{id} | стадия конкретного матча (+ объект season) |
GET /v1/events | лента; поддерживает фильтр по стадии, см. ниже |
GET /v1/sync | инкрементальная синхронизация — стадия едет вместе с матчем |
GET /v1/tournament/{id}/events | матчи турнира по сезонам |
Фильтр: ?stage= принимает одно из пяти значений
regular · qualification · group ·
playoff · placement. Незнакомое значение вернёт
400 unknown_stage со списком допустимых — а не пустую ленту,
которую легко принять за «матчей нет».
# все матчи стадии плей-офф по футболу curl -H "X-API-Key: $KEY" \ "https://api.sportwire.ru/v1/events?sport=football&stage=playoff&limit=50" # ближайшие матчи квалификации curl -H "X-API-Key: $KEY" \ "https://api.sportwire.ru/v1/events?stage=qualification&status=scheduled" # архив финалов и полуфиналов сезона: фильтруйте по stage=playoff, # конкретную стадию берите из stage.code (final / semifinal / …) curl -H "X-API-Key: $KEY" \ "https://api.sportwire.ru/v1/events?sport=basketball&stage=playoff&status=finished"
⚠️ Фильтр отбирает по stage.type (класс), а не по stage.code.
Отдельного фильтра «только финалы» нет намеренно: финал — это
type=playoff с code=final, и разделять их в параметре
значило бы плодить два способа спросить одно и то же. Отбирайте класс запросом,
конкретную стадию — по code на своей стороне.
Матчи без стадии фильтр не вернёт вовсе — у них поля нет, и это штатно
(см. следующий раздел). Если нужен полный список матчей, не задавайте
stage=.
Голое число само по себе — не стадия
Из одного лишь номера раунда мы никогда не выводим класс на лету. Причина не теоретическая:
в теннисе регулярного сезона не существует, а номер есть почти у каждого матча (за 45 дней —
22 492 матча с номером против 39 с именем), и round=5 там означает «Round of 16», а
вовсе не «5-й тур». В ММА номер раунда доходит до 74 при пяти раундах в бою. Один и тот же «номер»
у разных видов значит разное, поэтому догадка по нему была бы правдоподобной ложью.
Но там, где номер ДОКАЗУЕМО является туром, поле заполняется — отдельным проходом по
истории, а не разбором на лету, и только когда выполнены сразу четыре условия: вид — командная
лига; внутри сезона встречается не менее пяти разных номеров и самый частый из них держит меньше
30% матчей; номер лежит до первого разрыва в ряду (кубковые коды сидят за разрывом: у Лиги
Европы туры 1–8, дальше сразу 27/28/29); имя турнира не кубковое. Такие матчи несут
type и round, но не несут name и code —
по ним и отличайте выведенный класс от названного источником. Проверка правила: номер сверен с
подписью флеш-твина «Round N» на 77 238 парах — совпало 96.9%.
| Что пришло | Как читать и что показывать |
|---|---|
есть stage.type | стадия распознана. Показывайте name_ru → name; ветвление в коде стройте на type/code |
есть только stage.name | источник назвал стадию, но название не в нашем словаре. Показывайте name как есть — не угадывайте класс сами |
stage нет | стадии по этому матчу нет. Не рисуйте «Стадия: —» — поля не существует, интерфейс должен просто обойтись без него. Номер раунда, если он был в фиде, лежит в extra.round — но что он означает, гарантировать нельзя |
Честное покрытие по видам спорта
Стадию отдаёт источник, а не мы: где он молчит — поля не будет, сколько ни ждать. Замер по всем матчам за 14 дней (не по выборке): сколько матчей вида имеют имя стадии.
| Вид спорта | Матчей за 14 дней | С именем стадии | Что это значит на практике |
|---|---|---|---|
| Валорант | 273 | 273 | стадия названа у каждого матча |
| Пляжный волейбол | 365 | 93 | круги турнира названы у четверти матчей |
| Бадминтон | 589 | 107 | 1/8-finals, Quarterfinals — примерно каждый пятый |
| Регби-лиг | 489 | 73 | плей-офф назван, регулярка идёт номером |
| Водное поло | 80 | 41 | около половины |
| Теннис | 11 963 | 162 | имя приходит редко, номер круга — почти всегда, но что он значит, фид не сообщает |
| Настольный теннис | 18 788 | 188 | см. врезку ниже — имя приходит с флеш-твина |
| Футбол | 51 628 | 284 | имя есть у кубков и еврокубков; у лиговых матчей стадии, как правило, не будет |
| Баскетбол · хоккей · гандбол · волейбол | 18 619 | 87 | имя — у плей-офф; регулярка приходит номером, то есть без стадии |
| Снукер · ММА · бокс · гольф · автоспорт | 1 001 | 0 | за две недели имя стадии не пришло ни разу |
По ряду видов — настольный теннис, крикет, футзал, бадминтон, дартс, снукер, пляжный волейбол,
ММА — основной фид стадию не передаёт вообще. Но это не значит «стадии не будет никогда»:
часть таких матчей связана с записью второго источника, и оттуда название стадии приходит.
Замер за 14 дней: настольный теннис — 188 матчей с именем (1/8-finals 40,
1/16-finals 33, 1/32-finals 32, Quarterfinals …),
бадминтон — 107, пляжный волейбол — 93. Правило простое и одинаковое для всех видов:
есть stage — пользуйтесь, нет — считайте это нормой, а не ошибкой.
stage было штатным состоянием.Числа выше — факт на дату замера, а не обязательство: покрытие определяет источник. Станет отдавать шире — вырастет само, без изменения контракта; обратной гарантии нет, планируйте на то, что стадии может не быть у любого матча любого вида.
Русские названия стадий
Названия стадий приходят от источника по-английски. Мы переводим их по словарю и кладём в
name_ru. Примеры соответствий:
| Источник (EN) | name_ru |
|---|---|
Final | Финал |
Semifinals / Semi-finals | 1/2 финала |
Quarterfinals | 1/4 финала |
Round of 16 · Round of 32 · Round of 64 | 1/8 финала · 1/16 финала · 1/32 финала |
Qualification / Qualifications | Квалификация |
Preliminary | Предварительный раунд |
Group Stage / Group A | Групповой этап / Группа A |
Play-off / Playoffs / Knockout stage | Плей-офф |
3rd place / Bronze medal match | Матч за 3-е место |
Placement match / 5th place match | Матч за место / Матч за 5-е место |
1/16-finals (форма второго источника) | 1/16 финала |
Round 19 / Matchday 5 | 19-й тур / 5-й тур |
Словарь закрытый: незнакомое название источника мы не переводим наугад — тогда
name_ru в ответе отсутствует, а оригинал остаётся в name. Надёжный порядок
показа: stage.name_ru → stage.name → ничего (объекта stage
нет — не подставляйте заглушку).
season_label и tournament_stage — это СЕЗОН, а не стадия.
Слово «stage» в нашей модели исторически занято изданием турнира: season_label =
«25/26», а параметр season у таблиц,
бомбардиров и сеток — это id сезона, а не стадии.
Стадия матча (1/8 финала, квалификация, тур) живёт только в поле stage у матча.
Отдельно не путайте со словом «стадия» в гайде по миграции: там оно
означает status матча (scheduled / live / finished) —
состояние, а не место в сетке.round_num / round_name. round_num API не отдавал никогда —
это была ошибка документации, и из инвентаря полей он убран. Что появилось и что осталось:
stage— новый объект, описан выше. Аддитивно: ни одно существующее поле не изменило смысл и ни одно не убрано.round_nameостаётся в ответе как раньше и равенstage.name. Новый код пишите наstage.extra.roundостаётся как раньше — это сырое число из фида, и оно не равноstage.round: у названной стадии кубка там служебный код (300у предварительного раунда), аstage.roundв этом случае не отдаётся вовсе. Показыватьextra.roundпользователю как «тур» нельзя.
/v1/rankings для
футбольных видов (fifa, uefa_clubs, uefa_countries)
поле participant.id теперь содержит наш канонический идентификатор и
джойнится с /v1/participant/{id}. Раньше там по недосмотру отдавался
внутренний идентификатор поставщика: он пересекается с нашим пространством id, поэтому
переход по нему приводил в другую команду. Если вы сохраняли эти значения —
их нужно перезапросить. Там, где сопоставление с нашей сущностью отсутствует,
поле равно null (так же ведут себя теннисные рейтинги).Рейтинги новое
kind: fifa · uefa_clubs · uefa_countries ·
atp_singles · atp_doubles · atp_singles_race ·
atp_doubles_race · wta_singles · wta_doubles ·
wta_singles_race · wta_doubles_race. Каждая запись:
ранг, участник, очки, movement (со знаком, где источник даёт прошлый срез).
{ "kind":"fifa", "count":211,
"rankings": [ {"rank":1,"participant":{"id":…,"name":"Argentina","name_ru":"Аргентина",
"country":"ARG"},"points":1877.3,"movement":2} ] }
Трансферы beta
История трансферов участника. Для игрока — его переходы (откуда/куда, дата, тип, сумма);
для команды — входящие и исходящие. type = transfer ·
loan · loan_return · other. Beta —
глубина и полнота зависят от лиги.
{ "participant_id":…, "type":"player", "count":3,
"transfers": [ {"date":"2025-08-16","type":"transfer","player_name":"…",
"from_team":"…","to_team":"…","fee":12.5,"fee_currency":"EUR"} ] }
Глубина покрытия по лигам
Базовые данные — расписание, счёт, статус, участники — есть по всем матчам всех видов спорта. Глубина детализации (статистика, составы, шотмап/xG, предматчевая модель) зависит от уровня лиги: по топ-лигам доступно всё, по низшим дивизионам обычно только счёт и ключевые события. Это отражает наличие данных у источника, а не пробел в нашем сборе — глубокой статистики низших дивизионов и молодёжных турниров не существует ни у одного провайдера.
| Данные | Топ-лиги | Нац. дивизионы / кубки | Низшие / молодёжь |
|---|---|---|---|
| Live счёт + статус (~20 сек) | ✓ | ✓ | ✓ |
| Live инциденты (голы, карточки, замены) | ✓ | ✓ | ✓ |
| Live шотмап + xG (растут по ходу матча) | ✓ футбол | частично | — |
| Статистика матча | ✓ ~95% | ~12% | редко |
| Составы (lineups) + предматч-составы (предполагаемый XI, флаг confirmed) | ✓ ~95% | ~11% | редко |
| Травмы / дисквалификации (missing players) | ✓ где есть составы | частично | — |
| Карта ударов (shotmap) | ~30–40% (футбол) | ~6% | — |
| xG по игрокам (из шотмапа: xG/xGOT/удары/голы) | ✓ футбол | частично | — |
| Текстовый онлайн (commentary) плей-бай-плей, live, премиум | ✓ топ live | частично | — |
| Коэффициенты | ✓ | частично | редко |
| Предматч: модель, форма, H2H, таблица | ✓ | ✓ * | редко * |
| Погода на стадионе | ✓ | ✓ | ✓ если есть координаты |
Проценты — реальное измеренное покрытие среди уже детализированных матчей (футбол): топ-лиги —
~95% статистика / ~95% составы / ~30–40% шотмап; остальные — 12% / 11% / 6%. Для live и недавних матчей топ-лиг детализация полная; исторический архив покрыт на измеренную выше глубину и пополняется регламентными до-сборами.
Предматч-составы, травмы/дисквалификации и xG по игрокам доступны там же, где собираются составы и шотмапы.
* Предматчевая модель считается автоматически, когда у обеих команд достаточно истории
(≥ 4 матча на сторону) — для редких или молодёжных команд её может не быть (поле model = null).
Лиги с расширенными данными (составы / предматч-составы / травмы / статистика; для футбола ещё шотмап, xG, xG по игрокам):
футбол — АПЛ, Ла Лига, Серия A, Бундеслига, Лига 1, Эредивизи, Лига Чемпионов / Европы / Конференций,
Saudi Pro League, Бразилейрао, Чемпионшип, MLS и др.; баскетбол — NBA, WNBA, Евролига, Еврокубок, ABA, NCAA, NBB и др.;
хоккей — NHL, КХЛ; бейсбол — MLB; плюс топ-турниры по регби, гандболу и волейболу.
Точный текущий список и покрытие по каждой лиге — через GET /v1/tournaments.
Детальное покрытие по ведущим лигам
✓ — полное покрытие (≈90%+ finished-матчей) · % — доля finished-матчей с этим типом данных · — — данные этого типа у источника не существуют (напр. шотмап/xG есть только для футбола; моментум — для футбола и баскетбола). Окно — последние 365 дней; для live и недавних матчей детализация полная.
| Лига | Вид | Таймлайн | Составы | Статистика | Шотмап | Моментум | xG игроков |
|---|---|---|---|---|---|---|---|
| Premier League | футбол | ✓ | ✓ | ✓ | 30% | 30% | 30% |
| LaLiga | футбол | ✓ | ✓ | ✓ | 36% | 36% | 36% |
| Serie A | футбол | ✓ | ✓ | ✓ | 39% | 38% | 39% |
| Bundesliga | футбол | ✓ | ✓ | ✓ | 35% | 35% | 35% |
| Ligue 1 | футбол | ✓ | ✓ | ✓ | — | 38% | — |
| Лига Чемпионов | футбол | ✓ | ✓ | 77% | — | 16% | — |
| Лига Европы | футбол | ✓ | ✓ | ✓ | 16% | 16% | 16% |
| Лига Конференций | футбол | ✓ | ✓ | 55% | 11% | 11% | 11% |
| Saudi Pro League | футбол | ✓ | ✓ | ✓ | 39% | 38% | 39% |
| Championship | футбол | ✓ | ✓ | ✓ | 34% | 34% | 34% |
| MLS | футбол | ✓ | ✓ | ✓ | 35% | 34% | 35% |
| Brasileirão Série A | футбол | ✓ | ✓ | ✓ | 28% | 28% | 28% |
| J1 League | футбол | ✓ | ✓ | ✓ | 38% | 39% | 38% |
| NBA | баскетбол | ✓ | ✓ | ✓ | — | 41% | — |
| WNBA | баскетбол | ✓ | ✓ | ✓ | — | 32% | — |
| Евролига | баскетбол | ✓ | ✓ | ✓ | — | 30% | — |
| China CBA | баскетбол | ✓ | ✓ | ✓ | — | 47% | — |
| Brazil NBB | баскетбол | ✓ | ✓ | ✓ | — | 43% | — |
| NHL | хоккей | ✓ | ✓ | ✓ | — | — | — |
| КХЛ | хоккей | ✓ | ✓ | ✓ | — | — | — |
| AHL | хоккей | ✓ | 12% | ✓ | — | — | — |
| MLB | бейсбол | — | ✓ | ✓ | — | — | — |
Срез по матчам за последние 365 дней (на момент генерации). Шотмап, xG и xG по игрокам — футбольные метрики;
моментум доступен для футбола и баскетбола. Для live и недавних матчей топ-лиг детализация полная.
Полный машиночитаемый список лиг и покрытие — через GET /v1/tournaments.
Турниры и лиги
Список турниров/лиг (опционально ?sport=), с количеством матчей, страной и полом.
{ "count": 18, "tournaments": [
{ "id": 17, "name": "Premier League",
"name_i18n": {"en":"Premier League","ru":"Премьер-лига"},
"sport": "football", "gender": "M", "country": "EN", "events": 14023 }
]}
Поиск
Нечёткий поиск команд и игроков по названию (с транслитерацией). Параметры: q,
sport, type (team|player), limit.
GET /v1/participants/search?q=зенит&type=team
Команда / игрок
Профиль: тип, вид спорта, страна, пол, дата рождения (для игроков), name_i18n,
image_url и дополнительные поля (стадион, амплуа, рост — где доступно).
Связанные команды (related_teams, только для type=team).
У киберспортивных организаций рядом с основной командой живут отдельные
коллективы: академия, женский и молодёжный составы, а также состав, ушедший
из организации. Это РАЗНЫЕ команды в разных турнирах, поэтому мы их не
объединяем — но показываем родство, чтобы вы могли решить сами. Каждая связь
несёт своё доказательство:
| relation | что означает |
|---|---|
division | подразделение той же организации: Academy, Talent, Female, NXT, Challengers, Youth |
former_roster | бывший состав: «ex-X» рядом с «X» |
shared_roster | четыре и больше общих игрока за 180 суток; число в поле shared_players. Это НЕ та же команда — в киберспорте состав переходит целиком |
{ id: 451856, name: Vivo Keyd Stars Academy, type: team, sport: league_of_legends,
related_teams: [
{ participant_id: 458100, name: Vivo Keyd Stars, relation: division,
evidence: «Vivo Keyd Stars Academy» — подразделение «Vivo Keyd Stars», matches: 133 } ] }
Персональная статистика ИГРОКА: агрегат по всем матчам с детализацией (голы, удары, удары в створ, xG, xGOT) и последние матчи с per-match числами. Модуль «xG · shotmap».
{ "participant_id": 543227, "type": "player", "available": true,
"totals": { "matches": 19, "goals": 18, "shots": 92, "shots_on_target": 44, "xg": 10.90, "xgot": 11.58 },
"matches": [
{ "event_id": 10982017, "scheduled_start": "2026-07-04T…", "goals": 1, "shots": 9, "xg": 1.33,
"home": "Argentina", "away": "Cabo Verde", "home_score": 3, "away_score": 2 } ] }
Изображения
Готовое изображение (логотип команды / фото игрока), 150×150. Можно встраивать напрямую:
<img src="https://api.sportwire.ru/v1/participant/4504/image?key=ВАШ_КЛЮЧ">
Матчи участника
Матчи конкретной команды/игрока (прошедшие и будущие), с пагинацией.
Страны
Список стран (ISO) с числом лиг и набором видов спорта. Категории вида «Amateur / Women / Youth»
сведены к одной стране. Опционально ?sport=.
{ "count": 221, "countries": [
{ "iso": "BR", "name": "Brazil", "name_ru": "Бразилия",
"leagues": 1266, "sports": ["basketball","football","volleyball"] },
{ "iso": "EN", "name": "England", "name_ru": "Англия", "leagues": 699, "sports": ["football"] }
]}
Лиги по странам
Полный справочник всех лиг и турниров в разрезе по странам и видам спорта — отдельная
страница с поиском и сворачиваемыми списками, у каждой лиги указан id:
→ /leagues.html — более 43 000 лиг и турниров, 221 страна.
Программный доступ — через /v1/tournaments с фильтрами:
| Параметр | Описание |
|---|---|
country | ISO-код страны (например ES, RU) |
sport | slug вида спорта |
q | поиск по названию (EN или RU) |
limit / offset | пагинация |
Предматчевая аналитика новое
Готовый аналитический пакет по матчу: форма и тренды обеих команд, очные встречи, турнирный контекст, математическая модель (Пуассон) с вероятностями исходов, «справедливыми коэффициентами» и value-сигналами против рынка, а также готовые трендовые ярлыки. Всё считается из исторических данных (≈2,9 млн матчей со счётом) — без чьих-либо «прогнозов».
Блок довстречный — и говорит об этом сам. Форма, H2H и модель считаются
строго as-of времени начала матча (as_of), то есть о ходе и счёте уже
начавшегося матча они не знают ничего. Поэтому в ответе есть scope:"prematch",
actionable и готовый заголовок title_ru: пока матч не начался —
«Что модель считает важным перед матчем», после стартового свистка — «Довстречный расклад»
плюс note с прямым предупреждением. Не подписывайте блок «перед матчем» на
идущем матче — берите title_ru. Для не-actionable матча
value_bets = null: довстречная модель против уже изменившейся
картины ценности не показывает.
{ "event_id": 1651128, "sport": "football",
"scope": "prematch", "as_of": "2026-08-03T18:30:00+00:00",
"status": "scheduled", "actionable": true,
"title_ru": "Что модель считает важным перед матчем", "note": null,
"tournament": { "id": 31384, "name": "Serie A", "name_ru": "Серия А" },
"home": {
"name": "Torino", "name_ru": "Торино",
"form": {
"last10": { "matches":10, "ppg":1.0, "gf_avg":1.0, "over_2_5":60.0,
"btts":60.0, "clean_sheet":20.0, "form":"DLWLL" },
"home": { "matches":10, "ppg":1.4, "gf_avg":1.3, "over_2_5":50.0 } },
"discipline": { "matches":10, "cards_avg":2.3 },
"standing": { "position":12, "played":38, "points":45, "goals_for":44, "goals_against":63 } },
"away": { "name":"Juventus", "name_ru":"Ювентус", "form": { … }, "standing": { … } },
"h2h": { "matches":3, "team_a_wins":0, "draws":2, "team_b_wins":1, "goals_avg":1.33, "btts":33.3,
"recent":[{"date":"2025-11-09","score":"1:1"}] },
"model": {
"expected_goals": { "home":1.78, "away":1.51, "total":3.29 },
"probabilities": { "home_win":44.4, "draw":22.8, "away_win":32.8,
"over_2_5":63.9, "under_2_5":36.1, "btts":64.8 },
"fair_odds": { "home_win":2.25, "draw":4.38, "away_win":3.05, "over_2_5":1.56, "btts":1.54 },
"likely_scorelines":[ {"score":"1:1","prob":10.0}, {"score":"2:1","prob":8.9} ] },
"value_bets": [ {"market":"total","side":"over","line":2.5,"market_odds":2.03,"fair_odds":1.87,
"edge_pct":8.6,"bookmaker":"fonbet"} ], // bookmaker = ЧЬЯ это цена
"value_bets_market": { "source":"линия легальных БК РФ (приём через ЦУПИС)", "line":"prematch",
"bookmakers":["fonbet","olimp"], "note":null },
"trends": [
{"label":"Тотал больше 2.5","detail":"Торино: 60% из 10","side":"home"},
{"label":"Обе забивают","detail":"Торино: 60% из 10","side":"home"} ],
"disclaimer":"Статистические оценки на основе исторических данных. Не является ставкой, прогнозом или инвестиционной рекомендацией." }
| Блок | Что внутри |
|---|---|
form | В/Н/П, очки за матч, забито/пропущено, % тоталов (1.5/2.5/3.5), ОЗ, «на ноль», не забивает — общие и дома/в гостях, за последние 5/10 и в целом; серии |
discipline | средние карточки команды (по матчам с детализацией) |
standing | позиция, очки, игры, забито/пропущено (где есть таблица) |
h2h | личные встречи: счёт, ОЗ, тоталы |
model | модель Пуассона: ожидаемые голы, вероятности П1/Х/П2, тоталов, ОЗ, «справедливые» кэфы, вероятные счета |
value_bets | исходы, где рынок платит больше модельной «справедливой» цены (edge_pct). Сравнение — только с доматчевой линией легальных БК РФ, и у каждой строки есть bookmaker — чья именно это цена. На идущем/сыгранном матче блок = null |
value_bets_market | с чьей линией сравнивали: источник, тип линии (prematch), список задействованных букмекеров |
scope / as_of / actionable / title_ru / note | область действия блока: что и на какой момент посчитано, можно ли трактовать это как оценку «сейчас», и готовый честный заголовок |
trends | готовые человекочитаемые ярлыки трендов |
Блоки формы — form.overall / last5 / last10 / home / away
Каждый блок формы — один и тот же набор метрик на разной выборке матчей (все / последние 5 / последние 10 / только дома / только в гостях). Для предстоящего матча выборка берётся строго ДО его даты — результат самого матча в расчёт не попадает (нет «подглядывания» в будущее).
| Поле | Описание |
|---|---|
matches | число матчей в выборке |
w / d / l | победы / ничьи / поражения |
ppg | очков за матч |
gf_avg / ga_avg / goals_avg | забито / пропущено / суммарно голов за матч |
win_pct / draw_pct / loss_pct | % исходов |
over_1_5 / over_2_5 / over_3_5 | % матчей с тоталом больше N |
btts | % матчей, где забили обе команды |
clean_sheet / failed_to_score | % «на ноль» / % без своих голов |
streak | текущая серия: {type: win|draw|loss, len} |
unbeaten_run / scoring_run / cleansheet_run | длина текущих серий: без поражений / с голами / сухих |
form | строка последних исходов, напр. "WWDLW" (новые слева) |
Модель — model (методология)
Модель Пуассона. Для каждой команды считается сила атаки и обороны относительно среднего по лиге (отдельно для домашних и гостевых матчей); из них — ожидаемые голы (λ хозяев и гостей), а из распределения Пуассона — матрица вероятностей счёта, и уже из неё все вероятности, «справедливые» коэффициенты и наиболее вероятные счета. Доступна для футбола и хоккея.
| Поле | Описание |
|---|---|
expected_goals.home / away / total | ожидаемые голы (λ) |
probabilities.home_win / draw / away_win | вероятности исхода, % |
probabilities.over_1_5 / over_2_5 / over_3_5 / under_2_5 / btts | вероятности тоталов и ОЗ, % |
fair_odds.* | «справедливый» коэффициент = 1 / вероятность |
likely_scorelines | топ-5 наиболее вероятных счетов с их вероятностью |
value_bets сравнивает fair_odds с реальными коэффициентами рынка
и показывает исходы с положительным перевесом: edge_pct = на сколько % рынок «щедрее» модели.
Очные встречи — h2h
| Поле | Описание |
|---|---|
matches | число личных встреч |
team_a_wins / draws / team_b_wins | счёт по встречам (team_a = команда home в карточке) |
goals_avg / over_2_5 / btts | средние голы, % тоталов >2.5, % ОЗ |
recent | последние встречи: дата и счёт |
disclaimer присутствует
в каждом ответе.Тренды команды и профиль лиги новое
Форма и тренды команды отдельно (общие / дома / в гостях / последние N), дисциплина и последние матчи.
Профиль лиги: средние голы, доля П1/Х/П2, % тоталов и ОЗ — за последние ~2,5 года.
{ "tournament_id":31384, "name":"Serie A", "name_ru":"Серия А", "sport":"football",
"matches":772, "avg_goals":2.52, "avg_home_goals":1.32, "avg_away_goals":1.19,
"home_win_pct":39.4, "draw_pct":28.1, "away_win_pct":32.5,
"over_2_5_pct":47.3, "over_3_5_pct":24.5, "btts_pct":49.5 }
Live-тренды (in-play вероятности) новое
Живая вероятностная картина матча — отдельный виджет-фид: после каждого значимого эпизода
(гол, удаление, пенальти) модель пересчитывает вероятности всех исходов — 1x2, двойной
шанс, тоталы 0.5–6.5, форы, «обе забьют», следующий гол — и отдаёт вероятность +
fair-коэффициент (1/p), рассчитанный независимо от букмекерских линий, движение
вероятностей с прошлого пересчёта и краткий ИИ-комментарий (text_ru + highlights). Live-футбол
топ-лиг (tier 1–2); пересчёт по инцидентам и каждые ~2,5 минуты. Списочный
/v1/live-trends отдаёт все живые матчи с трендами одним ответом (limit ≤ 200) —
удобно для ленты. Отдельный подключаемый модуль «Live-тренды».
{ "event_id": 10977319, "minute": 78, "phase": "2H", "score": { "home": 1, "away": 0 },
"trigger": { "kind": "goal", "minute": 76, "side": "home" },
"expected_remaining_goals": { "home": 0.21, "away": 0.35 },
"outcomes": [
{ "market": "1x2", "selection": "home", "p": 0.82, "fair_odds": 1.22 },
{ "market": "double_chance", "selection": "1x", "p": 0.93, "fair_odds": 1.08 },
{ "market": "total", "line": 2.5, "selection": "over", "p": 0.18, "fair_odds": 5.56 },
{ "market": "handicap", "line": -1.5, "selection": "home", "p": 0.14, "fair_odds": 7.14 },
{ "market": "next_goal", "selection": "away", "p": 0.27, "fair_odds": 3.70 } ],
"movement": [ { "market": "1x2", "selection": "home", "p_prev": 0.55, "p": 0.82, "delta": 0.27 } ],
"prematch_1x2": { "home": 0.44, "draw": 0.27, "away": 0.29 },
"ai": { "text_ru": "Гол на 76-й перевернул матч: хозяева ведут и контролируют темп…",
"highlights": [ { "market": "total", "line": 2.5, "selection": "under",
"note": "низовой сценарий заметно укрепился" } ] },
"model": "lt-poisson-1.0", "updated_at": "2026-07-03T14:20:07+00:00" }
xT — зоны угрозы (expected threat) новое
Собственная модель угрозы на нашей истории ударов (455 721 удар / 53 122 гола с координатами): эмпирическая поверхность вероятности гола по зонам поля, грид 16×8 (x — глубина от линии ворот, y — поперёк), сглаженная к базовой конверсии. По матчу отдаёт xt_created по сторонам, разбивку по зонам, вклад игроков и finishing-дельту (голы − xT) — кто реализует выше/ниже качества своих моментов. По команде — профиль за N последних матчей (сколько угрозы создаёт/допускает, из каких зон). Модуль analytics.
Покрытие: 18 188 матчей в 291 турнире (везде, где собрана карта ударов — футбол, топ- и средние лиги). Методология честная: это shot-based зоны угрозы, не possession-xT по владениям (позиционных пасов с координатами источник не отдаёт).
{ "event_id": 11225833, "available": true, "model": "xt_v1",
"home": { "xt": 2.29, "shots": 20, "goals": 1, "finishing": -1.29,
"zones": { "box_center": 2.09, "mid_center": 0.20 } },
"away": { "xt": 0.65, "shots": 7, "goals": 1, "finishing": 0.35, "zones": { … } },
"players": [ { "player": "Ондржей Лингр", "is_home": true,
"xt": 0.40, "shots": 2, "goals": 0, "finishing": -0.40 }, … ] }
# finishing < 0 → команда/игрок недореализует качество моментов; > 0 → бьёт эффективнее ожидания.
Прогнозы матча (CatBoost ML) + value vs РФ-ЦУПИС новое
Собственная ML-модель прогноза (CatBoost, градиентный бустинг) поверх пуассоновской: вероятности 1X2, тотал 2.5 и обе забьют с «справедливыми» коэффициентами (1/p), процентом уверенности и — где есть рыночная линия — value-анализом против БК РФ-ЦУПИС. Модель обучена на нашей истории (десятки тысяч матчей, футбол) и учитывает остаток поверх Пуассона: форму команд (PPG/забито/пропущено last-10 с home/away-сплитом), серию, отдых между матчами, H2H, рейтинги атаки/обороны и текущую букмекерскую линию. Модуль analytics.
Честность (бэктест). Все фичи считаются строго по данным ДО матча (as-of, без утечки будущего), оценка — на свежих матчах, которых модель не видела при обучении. На отложенной выборке (n≈11 000) наш CatBoost по 1X2 даёт log-loss 0.960 против 0.972 у букмекерского опенинга и 1.006 у чистого Пуассона — то есть модель калибрована точнее рынка на момент открытия линии. Тотал 2.5 — точность ≈65%, «обе забьют» — ≈59%. Прогноз — это честная вероятность и value, а не «обыграй БК»: используйте его как оценку справедливой цены и поиск расхождений с рынком, а не как гарантию.
Прогноз довстречный. Все фичи модели — строго до стартового свистка, счёта
идущего матча она не видит. Поэтому в ответе есть scope:"prematch",
as_of и actionable: до начала матча actionable:true,
после — false, и тогда мы не выставляем ни одного флага
value и не отдаём best_value. Сравнение всегда идёт с
доматчевой линией (line:"prematch") — ставить довстречную модель против
live-котировки, которая уже знает счёт, значит получать фантомную «ценность».
{ "event_id": 12345678, "available": true, "model": "catboost-1x2-v1", "status": "notstarted",
"scope": "prematch", "as_of": "2026-08-03T18:30:00+00:00", "actionable": true, "note": null,
"confidence": 54.3, // % уверенности = max вероятность фаворит-исхода
"markets": {
"match_result": { "prob_home": 54.3, "prob_draw": 24.1, "prob_away": 21.6, "predicted": "H",
"fair_odds": { "home": 1.84, "draw": 4.15, "away": 4.63 } },
"over_under_2_5": { "prob_over": 58.2, "prob_under": 41.8, "fair_over": 1.72 },
"btts": { "prob_yes": 55.0, "prob_no": 45.0, "fair_yes": 1.82 } },
"poisson_1x2": { "prob_home": 51.0, "prob_draw": 25.3, "prob_away": 23.7 }, // база для сравнения
"market": { // сравнение с доматчевой линией легальных БК РФ
"source": "линия легальных БК РФ (приём через ЦУПИС)", "line": "prematch",
"bookmakers": ["betcity","fonbet","leon","marathon","olimp","pari","winline","zenit"],
"n_books": 8, "actionable": true,
"selections": [
{ "selection": "home", "model_prob": 54.3, "market_prob": 51.2, "best_odds": 1.95,
"bookmaker": "pari", // ЧЬЯ это лучшая цена
"fair_odds": 1.84, "edge_pct": 3.1, "ev_pct": 5.9, "value": true }, // +EV → есть value
{ "selection": "draw", "model_prob": 24.1, "market_prob": 25.0, "best_odds": 3.90,
"bookmaker": "olimp",
"fair_odds": 4.15, "edge_pct": -0.9, "ev_pct": -6.0, "value": false }, … ],
"best_value": { "selection": "home", "ev_pct": 5.9, "best_odds": 1.95, "bookmaker": "pari", … } } }
Как читать value. model_prob — вероятность исхода по нашей модели;
market_prob — маржа-очищенная вероятность рынка (медиана по книгам);
best_odds — лучшая цена исхода среди книг (что реально доступно игроку), а
bookmaker — чья именно это цена (fonbet / winline / olimp / pari / betcity /
zenit / leon / marathon — легальные БК РФ, приём через ЦУПИС; полный список задействованных
книг — в market.bookmakers);
edge_pct = наша вероятность − рыночная; ev_pct = мат.ожидание ставки по
лучшей цене (p·odds − 1). value:true — когда EV выше порога шума (+2%).
best_value — исход с максимальным EV (если есть). Value считается только до
начала матча и только против доматчевой линии: при actionable:false все
value = false, best_value = null, а цифры
остаются справочными.
Live: пересчёт уверенности. Для идущего матча прогноз доступен и внутри
live-виджета (/v1/event/{id}/live-widget) полем
model_prediction, а live_confidence — это уверенность, пересчитанная по
in-play-вероятностям 1X2, которые наш live-движок обновляет после каждого значимого эпизода
(гол, удаление, пенальти) с учётом счёта и оставшегося времени. Так «процент уверенности движется
вместе с матчем». Сам model_prediction при этом остаётся довстречным и несёт
scope / as_of / actionable:false / note:
счёт учитывает только live_confidence, а model_prediction.market —
справочное сравнение с доматчевой линией, без вывода о ценности ставки.
Прогноз доступен по футболу (топ- и средние лиги, где есть история). Если модели
недостаточно данных по матчу — available:false с причиной; клиент должен это
обрабатывать (модель никогда не «выдумывает» прогноз на пустой истории).
Погода на стадионе новое
Погода в момент матча по координатам стадиона: температура и «ощущается», осадки и их вероятность,
ветер и порывы, влажность, облачность, текстовое описание. Также доступна в блоке
weather карточки матча /v1/event/{id}. Собирается для предстоящих матчей
(прогноз) и доступна по сыгранным (архив), где известна площадка.
{ "event_id":1651128, "available":true,
"weather": {
"stadium":"Stadio Olimpico Grande Torino", "city":"Turin",
"temperature_c":28.8, "feels_like_c":29.0, "conditions":"Ясно",
"wind_speed_ms":1.5, "wind_gusts_ms":5.8, "precipitation_mm":0.0,
"precipitation_prob_pct":null, "humidity_pct":39, "cloud_cover_pct":6,
"weather_code":0, "is_forecast":false, "observed_hour_utc":"2026-05-24T19:00" } }
Виджеты новое
Готовые встраиваемые виджеты — серверный рендер в <iframe>, ключ остаётся на нашей
стороне (ничего не светится в браузере, CORS не нужен). Имена команд и турниров — на русском.
Турнирная таблица
<iframe src="https://sportwire.ru/widget/standings?tournament=17"
width="100%" height="520" frameborder="0"></iframe>
Профиль / тренды лиги
<iframe src="https://sportwire.ru/widget/trends?tournament=17"
width="100%" height="190" frameborder="0"></iframe>
| Параметр | Описание |
|---|---|
tournament | id турнира (обязательный) |
theme | light (по умолчанию) · dark |
title | 0 — скрыть заголовок (для своей вёрстки) |
type (standings) | total · home · away · form |
limit (standings) | сколько строк таблицы показать (0 = все) |
Демо и конструктор кода — на странице Продукты → Виджеты.
Способы доставки данных
beta Доступно по запросу — напишите info@sportwire.ru. Тарифы: Pro и выше.
SportWire отдаёт данные двумя способами — выбирайте под свою задачу:
| Способ | Как работает | Когда выбирать |
|---|---|---|
| REST (pull) /v1 | Вы сами опрашиваете эндпоинты (/v1/events/live, /v1/event/{id} …) | Отчёты, витрины, периодическая синхронизация, ручные запросы |
| Вебхуки (push) | Мы сами шлём вам HTTP POST в момент изменения — без опроса | Оперативные сценарии: live-табло, оповещения, ставки, боты («оперативно, без сложностей») |
| SSE-стрим /v1/stream | Держите одно HTTP-соединение — мы шлём события по мере поступления (Server-Sent Events), с докачкой по Last-Event-Id | Live-табло и дашборды, длинные соединения без своего вебхук-эндпоинта |
| WebSocket /v1/stream/ws | Тот же поток по WebSocket с докачкой по курсору | Интерактивные и мобильные клиенты |
Все каналы едины: один формат конверта и один event-bus. Доставка построена на транзакционном outbox: каждое каноническое изменение матча записывается в шину событий в той же транзакции, что и сами данные — поэтому событие не теряется даже при сбое. Отдельный воркер разбирает шину и доставляет её подписчикам с подписью и повторами; те же события можно читать стримом (SSE/WS) или добрать через API повторной выдачи (replay).
Сверка полноты (reconciliation). Для REST-режима и для «двойного прогона» при
миграции есть две сверочные точки: GET /v1/tournament/{id}/events — полный календарь
турнира одним запросом (cron-diff вашей БД против нашей, без обхода ленты по дням), и
GET /v1/sync — дельта-синхронизация изменённых матчей по курсору
(since, since_id). Событие в пачке несёт деталь (события, статистику, составы,
статистику игроков) и дополнительные блоки вашего тарифа — h2h, form,
streaks, votes, best_players, win_probability,
highlights, tv_channels, fight, points_history,
point_by_point, games/rounds/map_bans (киберспорт),
race (протокол заезда). Тяжёлая координатная графика (shotmap,
momentum, average_positions, player_heatmaps,
player_actions) и построчный commentary — только в карточке
/v1/event/{id}: на пачку в 500 матчей это десятки мегабайт.
Подробнее — в разделе «Миграция».
Вебхуки (push-доставка) beta
Зарегистрируйте URL — и мы будем присылать на него подписанный JSON при каждом изменении подписанных вами матчей: смена статуса, изменение счёта, гол/карточка, подтверждение состава. Управление — в личном кабинете или через API кабинета (нужна сессия кабинета).
1. Регистрация вебхука
{ "url": "https://your-app.example.com/hooks/sportwire",
"event_types": ["match.goal","match.score_changed"], // [] = все типы
"sports": ["football","basketball"], // [] = все виды
"leagues": [34, 35072] } // id турниров, [] = все
// Ответ (secret показывается ОДИН раз — сохраните его):
{ "ok": true, "id": 12, "secret": "whsec_…", "event_types": ["match.goal","match.score_changed"],
"sports": ["football","basketball"], "leagues": [34,35072] }
Ещё эндпоинты кабинета: GET /account/api/webhooks — список (со статусом и здоровьем доставки) · DELETE /account/api/webhooks/{id} — удалить ·
POST /account/api/webhooks/{id}/test — тестовый webhook.ping · POST …/{id}/rotate-secret — сменить secret ·
POST …/{id}/pause и …/{id}/resume — пауза/возобновление · GET …/{id}/deliveries — лог доставки ·
GET /account/api/webhooks/catalog — каталог событий.
Безопасность URL. Адрес вебхука обязан быть публичным https://…. Частные, loopback,
link-local и метадата-адреса (10/8, 172.16/12, 192.168/16, 127/8, 169.254/16, ::1, fc00::/7,
169.254.169.254 и т.п.) отклоняются при регистрации и повторно проверяются перед каждой отправкой; редиректы
не выполняются. Если ваш endpoint N раз подряд уходит в dead-letter, подписка автоматически ставится на
паузу («предохранитель»). Дальше мы сами пробуем восстановиться: подписанный пробный webhook.ping
через 15 мин, затем реже (до 1 раза в 6 ч). Как только endpoint ответил 2xx — подписка
снова активна, а пропущенные за время разрыва события доотправляются автоматически (по порядку
sequence). Ручной resume делает то же самое сразу. Обычный деплой consumer-сервиса
на 10–20 минут, таким образом, не требует никаких действий с вашей стороны. Изредка (только в «тихие» периоды,
когда реальных доставок нет) может прийти проверочный webhook.ping — просто ответьте 2xx.
2. Каталог событий
| Тип | Когда шлётся | delta содержит |
|---|---|---|
match.status_changed | Смена статуса (запланирован → идёт → перерыв → завершён) | from, to |
match.score_changed | Изменение счёта (какая сторона забила) | scored: [{side,from,to}] |
match.goal | Гол (в т.ч. пенальти/автогол), in-play | kind, side, minute, player, assist |
match.card | Карточка (жёлтая/красная), in-play | kind, side, minute, player |
match.lineup_confirmed | Стартовый состав подтверждён | confirmed: true |
match.detail_changed opt-in | Деталь матча реально изменилась (статистика/составы/лента) — сигнал «пора перечитать /v1/event/{id}». Дебаунс ~90 с на матч | last_detail_at |
odds.changed opt-in | Обновились БК-котировки матча (модуль odds). Дебаунс ~60 с на матч | bookmaker, changed |
Opt-in типы. Высокочастотные match.detail_changed и odds.changed
доставляются только если явно перечислены в event_types подписки — пустой список
(«все события») их не включает. Это защита от неожиданного потока на существующие подписки.
Паттерн использования: по match.detail_changed перечитайте /v1/event/{id} один раз —
вместо пулла детали по каждому голу (деталь может не успеть измениться) и вместо слепых периодических
пуллов (статистика меняется и без голов).
3. Формат доставки (payload) — пакетная
Все события вашей подписки, готовые к отправке за один цикл, приходят одним POST —
в массиве events (по возрастанию, в порядке возникновения). Это держит доставку масштабируемой:
всплеск в 1000+ изменений котировок = один запрос, а не 1000. Каждый элемент events[] —
самостоятельный конверт (тот же формат, что и раньше); идемпотентность и порядок — по-прежнему на уровне
отдельного события (id, sequence). Ответьте 2xx на пачку целиком.
POST https://your-app.example.com/hooks/sportwire
Content-Type: application/json
User-Agent: SportWire-Webhooks/1.0
X-SportWire-Timestamp: 1793456789
X-SportWire-Signature: sha256=<hex>
X-SportWire-Batch-Size: 3
X-SportWire-Event-Id: evt_1662352_12 // id первого события пачки (у каждого события свой id внутри)
Idempotency-Key: evt_1662352_12_3 // ключ пачки; дедуп делайте по id КАЖДОГО события
{ "count": 3, // сколько событий в пачке
"events": [ // ← события подписки за цикл, по возрастанию
{ "id": "evt_1662352_12", // стабильный ключ идемпотентности (у каждого события свой)
"type": "match.goal",
"created_at": "2026-07-10T18:03:18Z",
"sequence": 12, // порядковый номер В РАМКАХ матча
"data": {
"match": { "id": 1662352, "sport": "football", "status": "live",
"tournament": { "id": 34, "name": "Premier League" },
"home": { "name": "Arsenal" }, "away": { "name": "Chelsea" },
"last_detail_at": "2026-07-10T18:03:11Z", // watermark реального изменения детали
"has_detail": true }, // есть ли лента событий вообще
"delta": { "kind": "goal", "side": "home", "minute": 23, "player": "…", "assist": "…" },
"snapshot": { "home_score": 1, "away_score": 0 } // состояние на момент события
} },
{ "id": "evt_1662352_13", "type": "match.score_changed", "sequence": 13, "data": { "…": "…" } },
{ "id": "evt_990877_4", "type": "odds.changed", "sequence": 4, "data": { "…": "…" } }
] }
Обработка. Проверьте подпись (над сырым телом — раздел 4), затем пройдите events
по порядку и примените каждый элемент как отдельное событие. Проверочный webhook.ping приходит
одиночным объектом (без обёртки events) — просто ответьте 2xx. Устойчивый приёмник
принимает оба вида: есть events[] — это пачка, иначе — одиночный конверт.
Нужно ли пуллить деталь? Сравните data.match.last_detail_at с тем, что вы
уже применяли: не изменился — деталь перечитывать не нужно. has_detail=false — ленты у матча
(пока) нет вовсе.
delta — что изменилось, snapshot — состояние сразу после изменения (зафиксировано в момент события, а не при доставке). Маркеры источника данных в payload не передаются.
4. Проверка подписи (обязательно)
Подпись — HMAC-SHA256 над строкой «{timestamp}.{тело}» вашим secret.
Проверяйте её на каждом запросе и сверяйте, что X-SportWire-Timestamp не старше ~5 минут (защита от повторной отправки).
Ротация без простоя. Заголовок может содержать несколько подписей через запятую:
X-SportWire-Signature: sha256=<новая>,sha256=<старая> — так происходит в течение
~24 ч после rotate-secret. Проверяйте любой из токенов (как в примерах ниже) —
тогда смена секрета не рвёт доставку: обновите secret у себя в удобный момент внутри окна.
# Python
import hmac, hashlib
def verify(secret: str, headers, raw_body: bytes) -> bool:
ts = headers["X-SportWire-Timestamp"]
expect = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
for tok in headers["X-SportWire-Signature"].split(","): # 1..2 подписи (окно ротации)
if hmac.compare_digest(tok.strip().removeprefix("sha256="), expect):
return True
return False
// Node.js
const crypto = require("crypto");
function verify(secret, headers, rawBody /* Buffer */) {
const ts = headers["x-sportwire-timestamp"];
const expect = crypto.createHmac("sha256", secret)
.update(ts + "." + rawBody.toString()).digest("hex");
return (headers["x-sportwire-signature"] || "").split(",") // 1..2 подписи (окно ротации)
.some(tok => {
const sig = tok.trim().replace("sha256=", "");
return sig.length === expect.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expect));
});
}
# Shell / curl-приёмник — та же проверка через openssl (RAW_BODY — сырое тело запроса как есть) TS="$HTTP_X_SPORTWIRE_TIMESTAMP" # заголовок X-SportWire-Timestamp SIG="$HTTP_X_SPORTWIRE_SIGNATURE" # заголовок X-SportWire-Signature (1..2 подписи через запятую) EXPECT=$(printf '%s.%s' "$TS" "$RAW_BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //') case ",$SIG," in *",sha256=$EXPECT,"*) echo ok ;; *) echo REJECT ;; esac
5. Порядок, идемпотентность, повторы
| Гарантия | Как обрабатывать |
|---|---|
| Порядок | Внутри пачки events уже отсортированы по возрастанию. sequence монотонно растёт в рамках матча (data.match.id) — применяйте по возрастанию; меньший номер, пришедший позже (повтор/докачка между пачками), игнорируйте. |
| Идемпотентность | Одно и то же событие (id) может прийти повторно (at-least-once) — в т.ч. если пачка была повторена целиком. Дедуплицируйте по id каждого события (а не по Idempotency-Key пачки). |
| Повторы | Отвечайте 2xx (на всю пачку) в течение ~8 сек. Иначе — повтор всей пачки с нарастающей паузой (≈1с → 5с → 30с → 2м → 5м); уже применённые события отсеются по id. После исчерпания попыток — dead-letter. |
| Ответ | Любой 2xx = пачка принята. Тело ответа не важно. Отвечайте быстро, обработку делайте асинхронно. |
Все доставки (и повторы, и dead-letter) видны в логе доставки — в кабинете и через API кабинета.
Стриминг — SSE beta
Одно длинное HTTP-соединение вместо своего вебхук-эндпоинта: те же события того же event-bus приходят потоком (Server-Sent Events) по мере поступления. Тариф Pro и выше. Формат конверта — тот же, что у вебхуков (см. «Формат доставки»).
Авторизация — вашим API-ключом: заголовок x-api-key или, для браузерного
EventSource, параметр ?key=. Фильтры (необязательно, через запятую):
sport=football,basketball · league=34,35072 (id турниров) ·
event_type=match.goal,match.score_changed. Отдаются только виды спорта из вашего тарифа.
# поток (флаг -N = без буферизации curl)
curl -N -H "x-api-key: YOUR_KEY" \
"https://api.sportwire.ru/v1/stream?sport=football&event_type=match.goal,match.score_changed"
# то, что приходит:
: connected cursor=845213
retry: 1000
id: 845214
event: match.goal
data: {"id":"evt_1662352_12","type":"match.goal","created_at":"2026-07-10T18:03:18Z","sequence":12,
"data":{"match":{"id":1662352,"sport":"football","status":"live",
"tournament":{"id":34,"name":"Premier League"},
"home":{"name":"Arsenal"},"away":{"name":"Chelsea"}},
"delta":{"kind":"goal","side":"home","minute":23},"snapshot":{"home_score":1,"away_score":0}}}
: keep-alive 1793456800 cursor=845214
Докачка (resume). В строке id: каждого события — курсор (глобальный, монотонный).
При обрыве переподключитесь с заголовком Last-Event-Id: <курсор> (или ?last_event_id=) —
мы доотдадим всё, что новее (в пределах окна свежести; для большого догона — Replay API ниже). Без курсора
поток начинается «с текущего момента». Строки-комментарии : keep-alive раз в ~15 с держат
соединение живым.
// Браузер — EventSource сам присылает Last-Event-Id при переподключении → докачка автоматическая
const es = new EventSource("https://api.sportwire.ru/v1/stream?key=YOUR_KEY&sport=football");
es.onmessage = (e) => { const evt = JSON.parse(e.data); /* e.lastEventId = курсор */ };
es.addEventListener("match.goal", (e) => { /* именованный тип */ });
Стриминг — WebSocket beta
Тот же поток по WebSocket. Тариф Pro и выше.
Авторизация — ?key=YOUR_KEY (или заголовок x-api-key для серверных клиентов).
Те же фильтры sport/league/event_type. Докачка — ?cursor=<курсор>.
Каждое событие — JSON-кадр {"cursor": <id>, …тот же конверт…}; служебные кадры
{"type":"connected"|"heartbeat","cursor":…}.
const ws = new WebSocket("wss://api.sportwire.ru/v1/stream/ws?key=YOUR_KEY&sport=football&cursor=845214");
ws.onmessage = (e) => {
const m = JSON.parse(e.data);
if (m.type === "heartbeat" || m.type === "connected") return;
// применяйте m (m.cursor — последний курсор для докачки)
};
API повторной выдачи (replay) beta
Догон/реконсиляция после простоя: страницами вернуть события из шины начиная с курсора. Ограничено по объёму и глубине (окно свежести). Тариф Pro и выше.
Авторизация API-ключом (x-api-key/?key=). Параметры: since — курсор
(число) или ISO-время (2026-07-10T18:00:00Z); limit (с потолком); те же
sport/league/event_type. Есть и вариант из кабинета по сессии:
GET /account/api/webhooks/events.
GET /v1/stream/replay?since=845000&limit=200&sport=football
{ "events": [ { …тот же конверт… }, … ],
"count": 200, "since": 845000, "next_cursor": 845200, "has_more": true }
# Догон без потерь: страницами по next_cursor, пока has_more=false,
# затем откройте SSE/WS с этого же курсора (Last-Event-Id / ?cursor=) — стык без дыр.
MCP-сервер — для Claude, ChatGPT, Cursor новое
Наш API втыкается в LLM напрямую по протоколу MCP (Model Context Protocol). Ваш ассистент (Claude Desktop, ChatGPT, Cursor, Gemini) получает 16 типизированных инструментов — искать матчи, брать карточку матча с картой ударов, таблицы, бомбардиров, статистику игрока, трансферы, модель Пуассона — и отвечает на обычном языке, сам ходя за данными. Один endpoint, ваш API-ключ.
Авторизация — тем же API-ключом в заголовке Authorization: Bearer <ключ>.
Данные и покрытие ограничены вашим тарифом ровно как в REST: недоступный модуль вернёт ошибку с
пометкой о тарифе. Инструменты: list_sports, search, list_leagues,
live_matches, list_matches, match_details,
match_shotmap, match_live_widget, match_predictions,
league_standings, league_scorers, league_matches,
entity, player_stats, entity_matches, transfers.
Claude Desktop / Cursor — добавьте в конфиг MCP (мост mcp-remote проксирует HTTP):
{
"mcpServers": {
"sportwire": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://api.sportwire.ru/mcp",
"--header", "Authorization: Bearer ВАШ_API_КЛЮЧ"]
}
}
}
Клиенты с нативной поддержкой удалённого MCP (без моста) — просто URL и заголовок:
{ "mcpServers": { "sportwire": {
"url": "https://api.sportwire.ru/mcp",
"headers": { "Authorization": "Bearer ВАШ_API_КЛЮЧ" }
} } }
Проверка вручную (без клиента) — обычный JSON-RPC:
curl -X POST https://api.sportwire.ru/mcp \
-H "Authorization: Bearer ВАШ_API_КЛЮЧ" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"search","arguments":{"query":"Холанд","type":"player"}}}'
Миграция с enetpulse / другого провайдера гайд
Переходите с XML-фида enetpulse (или другого провайдера)? Наш путь — не «мост XML», а те же
события, только чище: тот же поток изменений приходит через вебхуки или стриминг, но с
канонизированными сущностями — без дубликатов, с единым i18n-именем (ru/en)
и устойчивым id. Ниже — карта соответствия сущностей, соответствие событий и схема
«двойного прогона» для проверки паритета без потерь.
1. Соответствие сущностей (field mapping)
| У провайдера (напр. enetpulse) | У SportWire | Где взять |
|---|---|---|
event (матч) | каноническая единица — data.match.id | вебхук/стрим · /v1/event/{id} · /v1/tournament/{id}/events |
participant / team / competitor | participants[] (сторона + счёт) → participant | data.match.home/away · /v1/participant/{id} |
tournament / tournament_template / season | tournament + сезон (tournament_stage, он же season_label — это издание турнира, а не стадия матча) | data.match.tournament · /v1/tournament/{id} |
| стадия матча в сетке (раунд / круг) | stage у матча — класс, код, имя, номер тура. Ключ необязательный: появляется, только когда источник назвал стадию | stage в /v1/events · /v1/event/{id} · /v1/sync |
incident (гол, карточка…) | инцидент → вебхуки match.goal / match.card | вебхук/стрим · incidents[] в /v1/event/{id} |
result / event_stage / статус | status + snapshot счёта. Их «стадия» = состояние матча; стадия турнира — это отдельное поле stage | match.status_changed · match.score_changed |
lineup | состав → match.lineup_confirmed | вебхук · lineups в /v1/event/{id} |
odds / outcome | усреднённые рыночные + отдельные линии букмекеров РФ (ЦУПИС), Беларуси и Казахстана + лучшая цена среди книг РФ | odds, odds_bookmakers, odds_best в /v1/event/{id} |
их id сущностей | наш канонический id (стабильный, без дубль-твинов) | кросс-уолк по натуральным ключам — п. 3 |
2. Соответствие событий (их push → наш каталог)
| Событие у провайдера | Наш тип |
|---|---|
| смена стадии / статуса матча | match.status_changed |
| изменение результата / счёта | match.score_changed |
| гол (incident) | match.goal |
| карточка (incident) | match.card |
| подтверждение состава | match.lineup_confirmed |
3. Кросс-уолк идентификаторов
У нас единый канонический id матча / команды / турнира — не привязанный к
провайдеру и без дублей-твинов. На время миграции сопоставьте свои прежние id с нашими по натуральным
ключам (дата матча + команды + турнир) и сохраните нашу id рядом со своей; дальше работайте
по нашей — она стабильна. Полный список матчей для сопоставления — /v1/tournament/{id}/events
(весь календарь турнира одним запросом, с count_total для проверки полноты).
4. Двойной прогон и проверка паритета
Безопасный переход — параллельно принимать оба фида и сверять, пока расхождений не станет ноль:
- Подключите вебхуки (или SSE/WS) на наши события параллельно текущему провайдеру.
- Полнота: ночным cron сверяйте свою БД против
GET /v1/tournament/{id}/events— так вы ловите пропущенные/лишние матчи без обхода ленты по дням. - Дельта:
GET /v1/sync?since=…&since_id=…отдаёт матчи, чья деталь изменилась после курсора — удобно догонять расхождения по счёту/статусу. - Догон после простоя:
GET /v1/stream/replayдобирает пропущенные события из шины (страницами поnext_cursor), затем открываете стрим с того же курсора — стык без дыр. - Когда расхождения на нуле N дней подряд — отключаете старый фид.
Итог: вы получаете те же события оперативнее (push вместо опроса XML) и чище (канонизированные сущности, единый i18n, устойчивые id), а сверочные эндпоинты гарантируют, что при переключении ничего не потеряется. Нужна помощь с маппингом под ваш прежний фид — info@sportwire.ru.
Схема данных
Модель данных SportWire — основные сущности и связи. Матч (EVENT) связан с турниром,
сезоном и сторонами (со счётом) и обогащается событиями, статистикой, составами, коэффициентами,
картой ударов с xG, графиком моментума и погодой. Названия — мультиязычные (name_i18n:
EN/RU); сущности единые и недублированные.
Полный инвентарь данных
Что именно доступно — по каждому типу данных, до отдельного поля. Состав полей зависит от вида спорта и наличия детализации у конкретного матча; состав полей для конкретного матча отражают флаги полноты в ответе.
Матч — event
| Поле | Описание |
|---|---|
id | идентификатор матча |
scheduled_start | время начала (ISO-8601, UTC) |
rescheduled / original_start | true + первоначальная дата, если матч был перенесён (иначе false / null) |
status | scheduled · live · finished · postponed · cancelled |
status_detail | особое завершение: retired · walkover · removed · defaulted · awarded · abandoned. При обычном завершении ключа в ответе нет; матч, прерванный прямо сейчас, признака не несёт и идёт как live. Как читать + покрытие |
tournament_id / tournament / tournament_ru | турнир матча: id, название от источника и русское название. tournament_ru едет и в ленте /v1/events, и в карточке /v1/event/{id} — отдельный запрос к /v1/tournament/{id} ради русской строки не нужен. Где перевода нет — null: английское название под видом русского не подставляется |
stage | стадия турнира: type (класс: regular · qualification · group · playoff · placement) · code (машинный код стадии) · name / name_ru (имя стадии) · round (номер тура — только у регулярного сезона). Ключ появляется, только если источник назвал стадию; по одному номеру раунда мы её не выдумываем, и у большинства лиговых матчей ключа не будет. Как читать + покрытие по видам |
round_name | имя стадии строкой — дубль stage.name, оставлен для совместимости |
venue | площадка (стадион) |
lineup_confirmed | false — предварительный состав, true — официальный, null — нет |
extra | счёт по партиям/периодам с тайбрейками (period_scores, как читать), сезон, round — сырое число раунда из фида (у кубка это внутренний код стадии, а не тур; см. стадию), время начала текстом |
weather | погода на стадионе (см. ниже) |
race | протокол заезда — мотоспорт и биатлон: серия, сессия, дистанция и классификация (место, время, отставание, круги, пит-стопы, стрельба). Модуль «Детали матча». Как читать + покрытие |
Стороны и счёт — participants
| Поле | Описание |
|---|---|
participant_id / name / name_ru | команда или игрок, локализованное имя |
side | home / away, либо null у многосторонних событий (гонки — см. раздел «Гонки») |
score | итоговый счёт стороны. У гонки счёта нет — null |
position | место участника в заезде (мотоспорт, биатлон). У обычного матча — null; массив отсортирован по этому полю |
image_url | логотип / фото |
События матча — incidents
Голы, карточки (жёлтые/красные), замены, VAR, пенальти, начало/конец таймов и др. — по всей истории.
| Поле | Описание |
|---|---|
minute / minute_plus | минута (+добавленное). Бывает отрицательной — см. врезку ниже |
period | тайм / период |
kind | тип события (goal, card, subst, var, penalty …) |
side | сторона |
player_name / assist_name | игрок и ассистент |
payload | детали (тип карточки, счёт после события, описание) |
Отрицательная минута: -1 и -5
minute — сырое значение источника, и оно бывает отрицательным. Смысл ровно
такой, без дополнительных трактовок:
| Значение | Что означает | Событий в базе |
|---|---|---|
-1 | минута неизвестна — источник её не прислал | 61 759 788 |
-5 | событие вне игрового времени: карточка после финального свистка | 24 064 |
Замер 02.08.2026 по всем 105 243 382 инцидентам. -1 — это 59% всех
событий, и почти все они из видов, где источник вообще не привязывает розыгрыш ко времени:
настольный теннис — 53 160 317, волейбол — 4 580 628, гандбол — 1 972 015, бадминтон —
1 264 872. В футболе -1 стоит у 231 760 событий, в баскетболе — у 222 584.
У -5 из 24 064 событий 23 999 — карточки (20 431 жёлтая, 3 568 красных),
и 23 972 из них футбольные.
Встречаются и другие отрицательные значения — -4 у 580 событий,
-3 у 516, -2 у 74, плюс по несколько десятков на значениях от
-6 до -30 (всего таких событий 2 319). Это то же сырьё источника;
никакого дополнительного смысла мы им не приписываем, и читать их как минуту нельзя.
Ещё у 144 283 событий minute = null.
/v1/event/{id}
отсортированы по minute по возрастанию, поэтому отрицательные идут первыми,
до 1-й минуты. Если вы рисуете таймлайн в порядке массива, события с неизвестной минутой
окажутся в его начале. Отфильтруйте minute < 0 или покажите их отдельной
группой — но не как «событие на −1-й минуте».Статистика матча — statistics
Владение, удары (всего/в створ), угловые, фолы, офсайды, передачи, отборы, сейвы и десятки метрик; по таймам и за весь матч.
| Поле | Описание |
|---|---|
period | единый словарь периодов для всех видов: ALL — за весь матч; 1ST · 2ND · 3RD · 4TH … — таймы, четверти, партии, иннинги по номеру; ET1 / ET2 — дополнительное время, OT1…OT5 — овертайм / экстра-иннинг, PEN — серия пенальти. Диалекты источников (1H, 1Q, Extra time) приведены к этому словарю на отдаче |
group_name / stat_name | группа и название метрики |
home_value / away_value | числовые значения сторон |
home_text / away_text | текстовое представление (напр. «58%») |
Бейсбол: хиты и ошибки приходят разными наборами
В бейсболе статистика идёт двумя наборами от двух источников, и в одном матче они
практически не встречаются вместе (пересечение — 0,5% матчей, цифры ниже): у матча есть
либо один набор, либо другой. Hits есть в обоих, Errors — только
во втором, и только там же они разложены по иннингам.
| Набор | Что внутри | period | Матчей |
|---|---|---|---|
Боксскорgroup_name = Batting · Pitching · Fielding |
Hits, At bats, Runs, RBI, Home runs, Doubles, Triples, Base on balls, Strike outs, Left on base, AVG/OBP/SLG/OPS; у питчеров — Innings pitched, Earned runs, ERA, Outs, Batters faced; в филдинге — Put outs и Assists. Errors в этом наборе нет вовсе |
ALL | 33 824 |
Иннинговый наборgroup_name = MAIN |
Errors и Hits — итог матча плюс разбивка по иннингам |
ALL — итог,1ST…9TH — иннинги | 4 168 |
// ошибки по иннингам: stat_name = Errors, group_name = MAIN
{"period":"ALL","group_name":"MAIN","stat_name":"Errors","home_value":0,"away_value":3}
{"period":"1ST","group_name":"MAIN","stat_name":"Errors","home_value":0,"away_value":2}
{"period":"4TH","group_name":"MAIN","stat_name":"Errors","home_value":0,"away_value":1}
// хиты из боксскора — другой набор, другой матч (Errors там не будет)
{"period":"ALL","group_name":"Batting","stat_name":"Hits","home_value":8,"away_value":5}
Замер 02.08.2026: 242 198 завершённых бейсбольных матчей, статистика есть у
38 556 из них. Боксскор — у 33 824, и это почти целиком MLB (31 975 матчей).
Errors — у 4 921 матча: 12,8% матчей со статистикой и 2,0% всех
завершённых, — и почти все они, наоборот, НЕ MLB, а европейские и азиатские лиги;
у MLB ошибки есть лишь у 206 матчей. Пересечение наборов — 195 матчей (0,5%):
рассчитывать, что у матча найдутся оба, нельзя.
У части матчей (≈790) тот же иннинговый набор приходит под другими именами
групп — Match для итога и 1st Inning / 2 /
3… для иннингов. Это второй диалект того же источника, значения те же.
Поэтому отбирайте по stat_name (Errors, Hits)
и по period (ALL — итог, 1ST…9TH —
иннинги), а не по имени группы.
Составы — lineups
| Поле | Описание |
|---|---|
side / formation | сторона и схема (напр. 4-3-3) |
player_participant_id | игрок |
position / shirt_number | амплуа, номер |
is_substitute | в запасе / в старте |
stats | индивидуальная статистика игрока в матче (рейтинг, голы, передачи …) |
Коэффициенты — odds
| Поле | Описание |
|---|---|
market_name / market_group | рынок (исход, тотал, фора …) |
choice_name | исход (1 / X / 2 и т.д.) |
fractional_value | текущий коэффициент |
opening_value | открывающий коэффициент (где известен — для анализа движения линии) |
bookmaker | букмекер котировки, где источник его называет (bet365 / William Hill / Unibet); null для агрегированной рыночной цены |
movement | направление движения линии: up · down · null |
winning | сыграл ли исход (для сыгранных матчей) |
Карта ударов + xG — shotmap
GET /v1/event/{id}/shotmap — по каждому удару, отсортировано по минуте:
| Поле | Описание |
|---|---|
player / is_home | бивший игрок и сторона |
minute / added_time | время удара |
shot_type / situation / body_part | тип (гол/сейв/мимо/блок/штанга), ситуация (с игры/штрафной/пенальти/угловой/контратака), часть тела |
xg / xgot | ожидаемые голы и xG по ударам в створ |
x / y | точка удара на поле (координаты 0–100) |
trajectory | полилиния полёта мяча: start→end→goal (+ block у заблокированных), каждая точка {x,y} |
goal_mouth / goal_mouth_xy | зона попадания в ворота (low-left…) и точные координаты {x,y,z} (z — высота) |
block_xy | точка блока {x,y} (у заблокированных ударов) |
shotmap
убраны поля player_id и id (идентификатор удара), а из
/v1/event/{id}/xt — players[].player_id. Это были внутренние
идентификаторы поставщика данных, попавшие в ответ по недосмотру.
Не используйте их как идентификатор игрока: их числовые значения пересекаются с
нашим пространством participant.id, и запрос
/v1/participant/{player_id} с высокой вероятностью возвращал другого
человека. Если вы связывали удары с игроками по этому полю — переходите на связку по
player (имя) в паре с is_home, либо на составы
/v1/event/{id} → lineups, где у игрока стоит наш канонический
participant_id, стабильный и переживающий слияние дублей.shotmap у вратаря
(goalkeeper) теперь остаются только name, short_name,
position и jersey_number. Внутренние поля карточки поставщика
(id, slug, userCount, fieldTranslations)
из ответа убраны — это продолжение правки 27.07 по полевым игрокам.
Не используйте прежний goalkeeper.id как идентификатор игрока: его
числовые значения пересекаются с нашим пространством participant.id, и
/v1/participant/{id} по нему возвращал другого человека. Связка вратаря с
каноническим игроком — через составы /v1/event/{id} → lineups.momentum — график моментума по ходу матча (футбол, баскетбол).
Погода — weather
| Поле | Описание |
|---|---|
stadium / city | площадка и город |
temperature_c / feels_like_c | температура и «ощущается», °C |
precipitation_mm / precipitation_prob_pct | осадки и вероятность |
wind_speed_ms / wind_gusts_ms | ветер и порывы, м/с |
humidity_pct / cloud_cover_pct | влажность, облачность |
conditions / is_forecast | описание; прогноз или факт (архив) |
Турниры и таблицы — tournament / standings
| Поле | Описание |
|---|---|
name_i18n / country / gender / tier | название (EN/RU), страна, пол, уровень |
| сезоны | season_label (издание турнира, напр. «25/26»), даты начала/конца, текущий ли. Это не стадия матча — она в поле stage у матча |
| таблица | позиция, игры, В/Н/П, забито/пропущено, очки |
Команды и игроки — participant
| Поле | Описание |
|---|---|
type | team / player |
name_i18n / country / gender | имя (EN/RU), страна, пол |
birth_date | дата рождения (игроки) |
image_url | логотип / фото |
extra | стадион, амплуа, рост и др. (где доступно) |
Аналитика (вычисляемое)
Поверх данных доступны вычисляемые блоки — форма и тренды, очные встречи, профиль лиги, модель Пуассона с вероятностями и «справедливыми» кэфами, value-сигналы. См. Предматчевую аналитику и Тренды.