Подписки и топики
После подключения с 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 → Лимиты).