Skip to Content
StreamingWebSocket

Streaming WebSocket

Atualizações de odds e oportunidades em tempo real via WebSocket em wss://ws.sharpapi.io.

Para a documentação completa da API incluindo todos os tipos de mensagens, esquemas e códigos de fechamento, consulte a Referência da API WebSocket.

SSE vs WebSocketPermalink for this section

A SharpAPI oferece dois protocolos de streaming. Ambos entregam os mesmos dados na mesma velocidade.

SSEWebSocket
URLhttps://api.sharpapi.io/api/v1/streamwss://ws.sharpapi.io
DireçãoServidor → ClienteBidirecional
Filtros de atualizaçãoReconexão necessáriaEnvio de mensagem subscribe
ReconexãoAutomática (Last-Event-ID)Manual (implementar backoff)
Melhor paraConsumidores simples, navegadoresAplicações interativas, dashboards

Escolha SSE se você define filtros uma vez e apenas quer que os dados fluam. Escolha WebSocket se você precisa alterar filtros em tempo real ou prefere um protocolo bidirecional.

Início RápidoPermalink for this section

1. ConectarPermalink for this section

Use o parâmetro channels para se inscrever apenas nos dados que você precisa:

// Only EV opportunities + odds — no middles, low_hold, or arbitrage const ws = new WebSocket( 'wss://ws.sharpapi.io?api_key=YOUR_KEY&channels=ev,odds&sport=basketball&sportsbook=draftkings&league=nba' );

Canais disponíveis: ev, arbitrage, middles, low_hold, odds. Omita channels para receber tudo o que seu tier suporta.

Filtros disponíveis: sport, sportsbook, league, market, event_id — todos separados por vírgula.

Filtros de threshold: min_ev (padrão 2.0), min_profit (padrão 0.5, aplica-se apenas a arbitragem, não a low-hold) — filtre oportunidades de baixo valor no servidor.

2. Tratar MensagensPermalink for this section

ws.onmessage = (event) => { const msg = JSON.parse(event.data); switch (msg.type) { case 'connected': console.log(msg.message); // "Welcome to SharpAPI real-time odds stream" break; case 'opportunities_snapshot': if (msg.ev) console.log('EV:', msg.ev); // EV opportunity snapshot break; case 'initial': console.log('Odds:', msg.data); // Per-sportsbook odds snapshot break; case 'snapshot:complete': console.log('All initial data loaded'); // Safe to hide loading state break; case 'odds:update': console.log(msg.source, msg.data); // Incremental odds update break; case 'ev:detected': console.log('+EV:', msg.data); // New or updated +EV opportunities (Pro+) — upsert by id break; } };

3. Manter a Conexão AtivaPermalink for this section

setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'ping' })); } }, 25000);

4. Atualizar Canais e Filtros (Sem Reconexão Necessária)Permalink for this section

ws.send(JSON.stringify({ type: 'subscribe', channels: ['low_hold'], filters: { sports: ['football'], sportsbooks: ['fanduel', 'betmgm'], leagues: ['nfl'] } }));

Fluxo de MensagensPermalink for this section

Ao conectar, você recebe mensagens nesta ordem:

  1. connected — Mensagem de boas-vindas com seu tier, recursos e canais ativos
  2. subscribed — Confirmação dos canais e filtros ativos
  3. opportunities_snapshot — Uma por canal de oportunidade inscrito (ev, arbitrage, middles, low_hold)
  4. initial — Snapshot de odds por sportsbook (somente se o canal odds estiver inscrito)
  5. snapshot:complete — Sinaliza que todos os dados iniciais foram enviados

Fragmentação de snapshot e tamanho de frame: Snapshots grandes são divididos em múltiplos frames para evitar backpressure. Cada frame de snapshot é limitado a 256KB serializados; snapshots de odds são divididos adicionalmente por sportsbook, e snapshots de oportunidades em até 300 itens por frame. Aguarde snapshot:complete antes de tratar os dados iniciais como totalmente carregados. Se a sua biblioteca cliente impõe um tamanho máximo de mensagem recebida, mantenha-o em 512KB ou mais — o padrão de 1MB do websockets do Python (max_size=2**20) é suficiente. Um cliente com limite abaixo de 256KB fecha com 1009 (message too big) no meio do snapshot e entrará em um loop de reconexão sem nunca receber dados.

Depois disso, você recebe atualizações incrementais para os canais inscritos:

  • odds:update — Odds alteradas para um sportsbook (requer canal odds)
  • odds:removed — Odds removidas por um sportsbook (requer canal odds)
  • ev:detected / ev:expired — Oportunidades +EV (requer canal ev)
  • arb:detected / arb:expired — Oportunidades de arbitragem (requer canal arbitrage)
  • middles:detected / middles:expired — Oportunidades de middle (requer canal middles)
  • low_hold:detected / low_hold:expired — Oportunidades de low-hold (requer canal low_hold)
  • heartbeat — Keepalive do servidor a cada 30 segundos

ReconexãoPermalink for this section

Diferente do SSE, o WebSocket não reconecta automaticamente. Implemente backoff exponencial:

let reconnectDelay = 1000; let lastGlobalSeq = 0; ws.onmessage = (event) => { const msg = JSON.parse(event.data); if (msg.type === 'connected') lastGlobalSeq = msg.global_seq; if (msg.global_seq) lastGlobalSeq = msg.global_seq; }; ws.onclose = (event) => { if (event.code === 1000) return; // intentional close if (event.code === 4001 && event.reason.includes('displaced')) return; // newer session owns the slot setTimeout(() => { reconnectDelay = Math.min(reconnectDelay * 2, 30000); // Pass from_seq to attempt a resume (best-effort, ~5-min replay window). // If the server can't resume it falls back to a full snapshot and says so. connect({ from_seq: lastGlobalSeq }); }, reconnectDelay); };

Redefina reconnectDelay para 1000 em uma conexão bem-sucedida.

O servidor mantém um buffer de replay com aproximadamente os últimos 5 minutos de eventos. Passe from_seq=N ao reconectar para reproduzir os eventos perdidos em vez de receber um snapshot completo. O resume é de melhor esforço: em interrupções mais longas ou reconexões durante manutenção do servidor, o servidor reestabelece sua base com um snapshot completo, sinalizado como mode: "full_resync" em snapshot:complete — e um resume que encontra uma lacuna define gap_detected: true. Seu cliente precisa lidar com os dois casos — veja o Streaming Reliability Contract e Reconexão com Replay para mais detalhes.

Códigos de FechamentoPermalink for this section

CódigoSignificadoAção
1000Fechamento normalNenhuma ação necessária
1006Fechamento anormal (lado do cliente)Queda de rede — sempre reconectar
4001Falha de autenticação ou deslocamento por uma sessão mais nova na mesma chave — leia o motivo do fechamentoChave inválida → corrija. "displaced by newer session" → não reconecte automaticamente (single-connection)
4003As permissões mudaram durante o stream (downgrade, chave revogada, add-on removido)Reconecte para reautorizar com as permissões atuais

O código 1006 é reservado pela RFC 6455 e nunca é enviado pelo servidor. Sua biblioteca WebSocket o gera localmente quando a conexão TCP é interrompida sem um handshake de fechamento (falha de rede, kill de processo). Sempre reconecte ao vê-lo — o exemplo abaixo já faz isso corretamente: pula a reconexão apenas no 1000 e em um deslocamento 4001, nunca no 1006.

Referência Completa da APIPermalink for this section

Para documentação completa incluindo todos os esquemas de mensagens, tratamento de erros e exemplos em múltiplas linguagens, consulte a Referência da API WebSocket.

Last updated on