Skip to Content
StreamingDescripción general

Visión general del streaming

Actualizaciones en tiempo real de cuotas y oportunidades mediante SSE o WebSocket.

SharpAPI ofrece dos protocolos de streaming: SSE (Server-Sent Events) sobre HTTP y WebSocket para comunicación bidireccional. Ambos entregan los mismos datos en tiempo real: elige según tu caso de uso.

¿Por qué streaming?

El sondeo (polling) REST introduce un retraso de 30-60 segundos entre los cambios de cuotas y el momento en que tu aplicación los ve. El streaming SSE entrega actualizaciones con latencia sub-segundo directamente a tu aplicación a medida que ocurren.

EnfoqueLatenciaAncho de bandaCaso de uso
Sondeo REST30-60sAlto (payload completo en cada sondeo)Navegación casual, dashboards
Streaming SSE< 1sBajo (solo deltas)Apuestas en vivo, alertas, sistemas automatizados
WebSocket< 1sBajo (solo deltas)Comunicaciones bidireccionales, actualizaciones dinámicas de filtros

Beneficios clave

  • Latencia sub-segundo — Las actualizaciones de cuotas llegan tan pronto como se detectan
  • Menor ancho de banda — Solo se envían los datos modificados, no instantáneas completas en cada sondeo
  • Dos protocolos — SSE para simplicidad, WebSocket para control bidireccional
  • Reconexión automática — SSE reconecta de forma nativa; WebSocket con lógica de reintento simple

Cómo funciona SSE

Server-Sent Events es un estándar W3C para streaming servidor-a-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 found |<-- event: heartbeat ----------| Keep-alive (every 30s) | ... |

SSE reconecta automáticamente al desconectarse. El servidor utiliza Last-Event-ID para reproducir cualquier evento que te hayas perdido.

Requisitos

TierAcceso al streaming
FreeNo disponible
Hobby + Add-on ($99/mes)1 stream (desplazamiento newer-wins)
Pro + Add-on ($99/mes)1 stream (desplazamiento newer-wins)
Sharp + Add-on ($99/mes)1 stream (desplazamiento newer-wins)
EnterpriseIncluido (límites personalizados)

Desplazamiento newer-wins: abrir un segundo stream desde la misma API key cierra el primero. Una sola conexión bien gestionada es suficiente para la mayoría de los casos de uso — consulta Patrones de conexión única para más técnicas. Las implementaciones de flota que necesiten múltiples streams simultáneos pueden solicitar un límite mayor a través de ventas.

Inicio rápido

// 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, reconnected } = JSON.parse(e.data); if (reconnected) oddsMap.clear(); console.log('Stream connected:', stream_id); }); eventSource.addEventListener('snapshot', (e) => { const { odds } = JSON.parse(e.data); 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); } console.log(`${book}: ${odds.length} odds updated`); }); eventSource.addEventListener('ev:detected', (e) => { const 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.js

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('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); } console.log(`${book}: ${odds.length} odds updated`); });

Python

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': if data.get('reconnected'): 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) print(f"{data['book']}: {data['count']} odds updated") elif event.event == 'ev:detected': for opp in data: print(f"+EV: {opp['selection']} at {opp['ev_percentage']}%")

Tipos de evento

EventoDescripción
connectedStream establecido, devuelve el ID del stream, los filtros activos y los canales
snapshot / opportunities_snapshotVolcado completo de datos de cuotas/oportunidades (todos los campos)
snapshot:completeSe han enviado todos los datos iniciales
odds:updateDelta compacto — solo campos dinámicos (id, odds_american, odds_decimal, odds_probability, line, is_live, odds_changed_at). Fusionar por id con el estado del snapshot.
odds:removedCuotas eliminadas por una casa de apuestas
ev:detectedNueva oportunidad +EV detectada
ev:expiredOportunidad +EV ya no disponible
arb:detectedNueva oportunidad de arbitraje detectada
arb:expiredOportunidad de arbitraje ya no disponible
middles:detectedNueva oportunidad de middle detectada (consulta Resumen de Middles para estadísticas agregadas)
middles:expiredOportunidad de middle ya no disponible
low_hold:detectedNueva oportunidad de low-hold detectada
low_hold:expiredOportunidad de low-hold ya no disponible
heartbeatKeep-alive enviado cada 30 segundos
errorError recuperable (la conexión permanece abierta)

SSE frente a WebSocket

CaracterísticaSSEWebSocket
ProtocoloHTTP (unidireccional)ws:// / wss:// (bidireccional)
EndpointGET /api/v1/streamwss://ws.sharpapi.io
Suscripciones a canalesDefinidas una vez mediante parámetros de consultaActualizables en cualquier momento mediante mensaje subscribe
Actualizaciones de filtrosReconectar con nuevos parámetrosEnviar mensaje subscribe
ReconexiónAutomática (Last-Event-ID)Manual (con backoff)
Soporte del navegadorEventSource nativoWebSocket nativo
Mejor paraConsumidores simples, SSRFiltros dinámicos, comunicaciones bidireccionales

Ambos protocolos entregan los mismos tipos de eventos y payloads de datos.

Referencia completa de la API

SDKs con soporte para streaming

  • SDK de TypeScript — Cliente SSE integrado con manejadores de eventos con tipado seguro
  • SDK de Python — Patrones de streaming basados en handlers e iteradores

Proyectos de ejemplo

Last updated on