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?Permalink for this section

Con REST ves un cambio en tu siguiente sondeo. El streaming te envía las filas modificadas en cuanto ocurren.

EnfoqueEntrega de actualizacionesAncho de bandaCaso de uso
Sondeo RESTEn tu siguiente sondeoAlto (payload completo en cada sondeo)Navegación casual, dashboards
Streaming SSEPush al cambiarBajo (solo deltas)Apuestas en vivo, alertas, sistemas automatizados
WebSocketPush al cambiarBajo (solo deltas)Comunicaciones bidireccionales, actualizaciones dinámicas de filtros

Beneficios clavePermalink for this section

  • Sin intervalo de sondeo — Las actualizaciones se envían en cuanto nuestro pipeline detecta un cambio, en lugar de esperar a tu siguiente petición
  • 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 SSEPermalink for this section

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 (new or updated) |<-- 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.

RequisitosPermalink for this section

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á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 eventoPermalink for this section

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, timestamp). Fusionar por id con el estado del snapshot. Una fila con un id que aún no tienes en tu mapa llega completa — almacénala.
odds:removedCuotas eliminadas por una casa de apuestas
ev:detectedNueva oportunidad +EV, o una versión actualizada de una ya enviada (mismo id) — haz upsert por id
ev:expiredOportunidad +EV ya no disponible
arb:detectedNueva oportunidad de arbitraje, o una versión actualizada de una ya enviada (mismo id)
arb:expiredOportunidad de arbitraje ya no disponible
middles:detectedNueva oportunidad de middle, o una versión actualizada de una ya enviada (mismo id). Consulta Resumen de Middles para estadísticas agregadas
middles:expiredOportunidad de middle ya no disponible
low_hold:detectedNueva oportunidad de low-hold, o una versión actualizada de una ya enviada (mismo id)
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 WebSocketPermalink for this section

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 APIPermalink for this section

SDKs con soporte para streamingPermalink for this section

  • 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 ejemploPermalink for this section

Last updated on