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

Подписки и топики

После подключения с API-ключом можно подписаться на один из двух типов топиков: весь спорт или конкретный матч.

Формат сообщения subscribe

Любое сообщение клиента должно содержать поле action. Для подписки используется action: "subscribe" плюс описание подписки.

Подписка на весь спорт

{
"action": "subscribe",
"type": "sport",
"sportSlug": "football"
}

Подписка на конкретный матч

{
"action": "subscribe",
"type": "match",
"sportSlug": "tennis",
"matchId": 12345678
}

Ответы сервера

Успешная подписка

{
"type": "subscribed",
"topic": "sport:football",
"timestamp": 1714123200000
}

Или для подписки на матч — следом за subscribed приходит ещё match_snapshot с актуальным состоянием матча:

{
"type": "match_snapshot",
"matchId": 12345678,
"sportSlug": "tennis",
"data": { /* полная структура Match */ },
"timestamp": 1714123200000
}

Уже подписан

{ "type": "already_subscribed", "topic": "sport:football", "timestamp": 1714123200000 }

Ошибки

Возможные ответы при некорректном subscribe: INVALID_MESSAGE (нет нужного поля), NO_ACCESS (спорт не входит в тариф). См. полный список в Error codes.

Формат сообщения unsubscribe

{
"action": "unsubscribe",
"topic": "sport:football"
}
{
"action": "unsubscribe",
"topic": "match:tennis:12345678"
}

Ответ:

{ "type": "unsubscribed", "topic": "sport:football", "timestamp": 1714123200000 }

Если соединение не было подписано на этот топик — придёт { "type": "not_subscribed", "topic": "...", "timestamp": ... }.

Формат топика

Тип подпискиФормат топикаПример
Весь спортsport:{sportSlug}sport:football
Один матчmatch:{sportSlug}:{matchId}match:tennis:12345678

В сообщения subscribe/unsubscribe клиент передаёт компоненты раздельно (type, sportSlug, matchId); сам строковый топик возвращается сервером.

JavaScript пример

const ws = new WebSocket('wss://ws.api.api-sport.ru/?apiKey=YOUR_API_KEY');

ws.addEventListener('open', () => {
console.log('Connected. Waiting for "connected" hello message...');
});

ws.addEventListener('message', (event) => {
const msg = JSON.parse(event.data);

switch (msg.type) {
case 'connected':
// Сразу подписываемся на live-футбол
ws.send(JSON.stringify({
action: 'subscribe',
type: 'sport',
sportSlug: 'football',
}));
break;
case 'subscribed':
console.log('Subscribed to', msg.topic);
break;
case 'match_snapshot':
console.log('Full state for match', msg.matchId, msg.data);
break;
case 'match_delta':
console.log('Delta for match', msg.matchId, msg.changes);
// msg.changes — { added?, updated?, flatChanges? }
break;
case 'error':
console.error('WS error:', msg.code, msg.message);
break;
}
});

ws.addEventListener('close', (event) => {
console.warn('Closed:', event.code, event.reason);
setTimeout(reconnect, 3000); // auto-reconnect, см. snapshot-vs-delta.md
});

Python пример

import json
from websockets.asyncio.client import connect

async def listen():
async with connect('wss://ws.api.api-sport.ru/?apiKey=YOUR_API_KEY') as ws:
async for raw in ws:
msg = json.loads(raw)
if msg.get('type') == 'connected':
await ws.send(json.dumps({
'action': 'subscribe',
'type': 'sport',
'sportSlug': 'tennis',
}))
elif msg.get('type') == 'match_delta':
# msg['changes'] = { 'added'?, 'updated'?, 'flatChanges'? }
print('delta', msg['matchId'], msg.get('changes'))
elif msg.get('type') == 'error':
print('error', msg['code'], msg['message'])

Лимиты

  • Количество подписок внутри одного WS-соединения — не ограничено серверным rate-limit'ом. Используйте здравый смысл и не подписывайтесь на десятки sport:* сразу — нагрузка ляжет на ваш клиент.
  • До 5 параллельных WS-соединений на один API-ключ (Overview → Лимиты).