Skip to Content
StreamingVisão Geral

Visão Geral do Streaming

Atualizações de odds e oportunidades em tempo real via SSE ou WebSocket.

A SharpAPI oferece dois protocolos de streaming: SSE (Server-Sent Events) sobre HTTP e WebSocket para comunicação bidirecional. Ambos entregam os mesmos dados em tempo real — escolha conforme seu caso de uso.

Por que Streaming?Permalink for this section

Com REST você vê uma alteração no seu próximo poll. O streaming envia as linhas alteradas assim que elas acontecem.

AbordagemEntrega de atualizaçõesLargura de bandaCaso de uso
Polling RESTNo seu próximo pollAlta (payload completo a cada poll)Navegação casual, dashboards
Streaming SSEPush na alteraçãoBaixa (apenas deltas)Apostas ao vivo, alertas, sistemas automatizados
WebSocketPush na alteraçãoBaixa (apenas deltas)Comunicação bidirecional, atualizações dinâmicas de filtros

Principais BenefíciosPermalink for this section

  • Sem intervalo de polling — As atualizações são enviadas assim que o nosso pipeline detecta uma alteração, em vez de esperar pela sua próxima requisição
  • Menor largura de banda — Apenas dados alterados são enviados, não snapshots completos a cada poll
  • Dois protocolos — SSE para simplicidade, WebSocket para controle bidirecional
  • Reconexão automática — SSE reconecta nativamente; WebSocket com lógica simples de retry

Como o SSE FuncionaPermalink for this section

Server-Sent Events é um padrão W3C para streaming servidor-para-cliente sobre HTTP:

Client Server | | |--- GET /api/v1/stream ------->| | | |<-- event: connected ----------| Stream established |<-- event: snapshot -----------| Full current state |<-- event: odds:update --------| Delta update |<-- event: odds:update --------| Delta update |<-- event: ev:detected --------| Opportunity (new or updated) |<-- event: heartbeat ----------| Keep-alive (every 30s) | ... |

O SSE reconecta automaticamente em caso de desconexão. O servidor usa Last-Event-ID para reproduzir quaisquer eventos que você tenha perdido.

RequisitosPermalink for this section

PlanoAcesso ao Streaming
FreeNão disponível
Hobby + Add-on ($99/mês)1 stream (substituição newer-wins)
Pro + Add-on ($99/mês)1 stream (substituição newer-wins)
Sharp + Add-on ($99/mês)1 stream (substituição newer-wins)
EnterpriseIncluído (limites personalizados)

Substituição newer-wins: abrir um segundo stream a partir da mesma API key encerra o primeiro. Uma conexão bem gerenciada é suficiente para a maioria dos casos de uso — veja Padrões de Conexão Única para técnicas. Implantações de fleet que precisem de múltiplos streams simultâneos podem solicitar um limite maior através do time de vendas.

Início RápidoPermalink for this section

// Local odds map — snapshot fills it, deltas merge into it const oddsMap = new Map(); const eventSource = new EventSource( 'https://api.sharpapi.io/api/v1/stream?channel=all&league=nba&api_key=YOUR_KEY' ); eventSource.addEventListener('connected', (e) => { const { stream_id, resumed } = JSON.parse(e.data); // A full snapshot follows every connect except a resume (resumed === true, // `odds` channel only), which replays missed deltas into the state you hold. // A started resume can still fall back to a full snapshot; this short example // leaves that out — the Streaming Reliability Contract has the complete client. if (resumed !== true) oddsMap.clear(); console.log('Stream connected:', stream_id); }); eventSource.addEventListener('snapshot', (e) => { const { odds } = JSON.parse(e.data); if (!odds) return; // opportunity chunks carry `ev` / `arbitrage` / `middles` / `low_hold` instead for (const odd of odds) oddsMap.set(odd.id, odd); console.log('Snapshot chunk:', odds.length, 'odds'); }); eventSource.addEventListener('odds:update', (e) => { const { odds, book } = JSON.parse(e.data); // Delta only contains dynamic fields — merge into local state by ID for (const delta of odds) { const full = oddsMap.get(delta.id); if (full) Object.assign(full, delta); else oddsMap.set(delta.id, delta); } console.log(`${book}: ${odds.length} odds updated`); }); eventSource.addEventListener('ev:detected', (e) => { // {opportunities, count, type}: new opportunities, or updated versions of ones already sent (same `id`) const { opportunities: opps } = JSON.parse(e.data); opps.forEach(opp => console.log(`+EV: ${opp.selection} at ${opp.ev_percentage}% EV`)); }); eventSource.addEventListener('heartbeat', () => { console.log('Connection alive'); }); eventSource.onerror = () => { console.log('Connection lost, auto-reconnecting...'); };

Node.jsPermalink for this section

import EventSource from 'eventsource'; const oddsMap = new Map(); const es = new EventSource( 'https://api.sharpapi.io/api/v1/stream?channel=odds&league=nba', { headers: { 'X-API-Key': 'YOUR_KEY' } } ); es.addEventListener('snapshot', (e) => { const { odds } = JSON.parse(e.data); for (const odd of odds) oddsMap.set(odd.id, odd); console.log(`Received ${odds.length} initial odds`); }); es.addEventListener('connected', (e) => { const { resumed } = JSON.parse(e.data); if (resumed !== true) oddsMap.clear(); }); es.addEventListener('odds:update', (e) => { const { odds, book } = JSON.parse(e.data); // Merge compact deltas into local state for (const delta of odds) { const full = oddsMap.get(delta.id); if (full) Object.assign(full, delta); else oddsMap.set(delta.id, delta); } console.log(`${book}: ${odds.length} odds updated`); });

PythonPermalink for this section

import sseclient import requests import json url = 'https://api.sharpapi.io/api/v1/stream' params = {'channel': 'all', 'league': 'nba'} headers = {'X-API-Key': 'YOUR_KEY'} odds_map: dict[str, dict] = {} response = requests.get(url, params=params, headers=headers, stream=True) client = sseclient.SSEClient(response) for event in client.events(): data = json.loads(event.data) if event.data else {} if event.event == 'connected': # A full snapshot follows every connect except a resume (resumed == True, # odds channel only), which replays deltas into your existing state. # A started resume can still fall back to a full snapshot; this short # example leaves that out — see the Streaming Reliability Contract. if data.get('resumed') is not True: odds_map.clear() print(f"Stream {data['stream_id']} connected") elif event.event == 'snapshot': for odd in data.get('odds', []): odds_map[odd['id']] = odd print(f"Snapshot: {data['count']} odds") elif event.event == 'odds:update': # Delta only has dynamic fields — merge by ID for delta in data.get('odds', []): existing = odds_map.get(delta['id']) if existing: existing.update(delta) else: odds_map[delta['id']] = delta print(f"{data['book']}: {data['count']} odds updated") elif event.event == 'ev:detected': # {opportunities, count, type}: new, or updated versions of ones already sent (same id) for opp in data['opportunities']: print(f"+EV: {opp['selection']} at {opp['ev_percentage']}%")

Tipos de EventosPermalink for this section

EventoDescrição
connectedStream estabelecido, retorna ID do stream, filtros ativos e canais
snapshot / opportunities_snapshotDump completo de dados de odds/oportunidades (todos os campos)
snapshot:completeTodos os dados iniciais foram enviados
odds:updateDelta compacto — apenas campos dinâmicos (id, odds_american, odds_decimal, odds_probability, line, is_live, timestamp). Faça merge por id no estado do snapshot. Uma linha com um id que ainda não está no seu mapa chega completa — armazene-a.
odds:removedOdds removidas por uma sportsbook
ev:detectedNova oportunidade +EV, ou uma versão atualizada de uma já enviada (mesmo id) — faça upsert por id
ev:expiredOportunidade +EV não está mais disponível
arb:detectedNova oportunidade de arbitragem, ou uma versão atualizada de uma já enviada (mesmo id)
arb:expiredOportunidade de arbitragem não está mais disponível
middles:detectedNova oportunidade de middle, ou uma versão atualizada de uma já enviada (mesmo id). Veja Resumo de Middles para estatísticas agregadas
middles:expiredOportunidade de middle não está mais disponível
low_hold:detectedNova oportunidade de low-hold, ou uma versão atualizada de uma já enviada (mesmo id)
low_hold:expiredOportunidade de low-hold não está mais disponível
heartbeatKeep-alive enviado a cada 30 segundos
errorErro recuperável (a conexão permanece aberta)

SSE vs WebSocketPermalink for this section

RecursoSSEWebSocket
ProtocoloHTTP (unidirecional)ws:// / wss:// (bidirecional)
EndpointGET /api/v1/streamwss://ws.sharpapi.io
Inscrições em canaisDefinidas uma vez via parâmetros de queryAtualizadas a qualquer momento via mensagem subscribe
Atualizações de filtrosReconectar com novos parâmetrosEnviar mensagem subscribe
ReconexãoAutomática (Last-Event-ID)Manual (com backoff)
Suporte do navegadorEventSource nativoWebSocket nativo
Melhor paraConsumidores simples, SSRFiltros dinâmicos, comunicação bidirecional

Ambos os protocolos entregam os mesmos tipos de eventos e payloads de dados.

Referência Completa da APIPermalink for this section

SDKs com Suporte a StreamingPermalink for this section

  • TypeScript SDK — Cliente SSE integrado com handlers de eventos type-safe
  • Python SDK — Padrões de streaming baseados em handlers e iteradores

Projetos de ExemploPermalink for this section

Last updated on