Skip to Content
StreamingWebSocket

WebSocket-Streaming

Echtzeit-Updates für Quoten und Opportunitäten via WebSocket unter wss://ws.sharpapi.io.

Die vollständige API-Dokumentation einschließlich aller Nachrichtentypen, Schemata und Schließcodes finden Sie in der WebSocket-API-Referenz.

SSE vs. WebSocketPermalink for this section

SharpAPI bietet zwei Streaming-Protokolle. Beide liefern dieselben Daten mit derselben Geschwindigkeit.

SSEWebSocket
URLhttps://api.sharpapi.io/api/v1/streamwss://ws.sharpapi.io
RichtungServer → ClientBidirektional
Update-FilterErneute Verbindung erforderlichsubscribe-Nachricht senden
WiederverbindungAutomatisch (Last-Event-ID)Manuell (Backoff implementieren)
Am besten geeignet fürEinfache Konsumenten, BrowserInteraktive Apps, Dashboards

Wählen Sie SSE, wenn Sie Filter einmal setzen und einfach Daten empfangen möchten. Wählen Sie WebSocket, wenn Sie Filter dynamisch ändern müssen oder ein bidirektionales Protokoll bevorzugen.

SchnellstartPermalink for this section

1. VerbindenPermalink for this section

Verwenden Sie den Parameter channels, um nur die Daten zu abonnieren, die Sie benötigen:

// 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' );

Verfügbare Kanäle: ev, arbitrage, middles, low_hold, odds. Lassen Sie channels weg, um alles zu empfangen, was Ihr Tarif unterstützt.

Verfügbare Filter: sport, sportsbook, league, market, event_id — alle kommagetrennt.

Schwellenwert-Filter: min_ev (Standard 2.0), min_profit (Standard 0.5, gilt nur für Arbitrage, nicht für Low-Hold) — filtern Sie Opportunitäten mit geringem Wert serverseitig heraus.

2. Nachrichten verarbeitenPermalink 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. Verbindung aufrechterhaltenPermalink for this section

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

4. Kanäle und Filter aktualisieren (keine Wiederverbindung erforderlich)Permalink for this section

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

NachrichtenflussPermalink for this section

Beim Verbindungsaufbau erhalten Sie Nachrichten in folgender Reihenfolge:

  1. connected — Willkommensnachricht mit Ihrem Tarif, Funktionen und aktiven Kanälen
  2. subscribed — Bestätigung der aktiven Kanäle und Filter
  3. opportunities_snapshot — Eine pro abonniertem Opportunitäts-Kanal (ev, arbitrage, middles, low_hold)
  4. initial — Quoten-Snapshot pro Sportsbook (nur wenn der odds-Kanal abonniert ist)
  5. snapshot:complete — Signalisiert, dass alle initialen Daten gesendet wurden

Snapshot-Chunking & Frame-Größe: Große Snapshots werden auf mehrere Frames aufgeteilt, um Backpressure zu vermeiden. Jeder Snapshot-Frame ist auf 256KB (serialisiert) begrenzt; Quoten-Snapshots werden zusätzlich pro Sportsbook aufgeteilt, Opportunitäts-Snapshots in bis zu 300 Elemente pro Frame. Warten Sie auf snapshot:complete, bevor Sie die initialen Daten als vollständig geladen betrachten. Falls Ihre Client-Bibliothek eine maximale Größe für eingehende Nachrichten erzwingt, belassen Sie diese bei mindestens 512KB — der 1MB-Standard von Python websockets (max_size=2**20) ist ausreichend. Ein Client mit einem Limit unter 256KB schließt mitten im Snapshot mit 1009 (message too big) und gerät in eine Reconnect-Schleife, ohne jemals Daten zu empfangen.

Danach erhalten Sie inkrementelle Updates für Ihre abonnierten Kanäle:

  • odds:update — Quoten haben sich für ein Sportsbook geändert (erfordert odds-Kanal)
  • odds:removed — Quoten wurden von einem Sportsbook entfernt (erfordert odds-Kanal)
  • ev:detected / ev:expired — +EV-Opportunitäten (erfordert ev-Kanal)
  • arb:detected / arb:expired — Arbitrage-Opportunitäten (erfordert arbitrage-Kanal)
  • middles:detected / middles:expired — Middle-Opportunitäten (erfordert middles-Kanal)
  • low_hold:detected / low_hold:expired — Low-Hold-Opportunitäten (erfordert low_hold-Kanal)
  • heartbeat — Server-Keepalive alle 30 Sekunden

WiederverbindungPermalink for this section

Anders als SSE führt WebSocket keine automatische Wiederverbindung durch. Implementieren Sie ein exponentielles Backoff:

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); };

Setzen Sie reconnectDelay bei erfolgreicher Verbindung auf 1000 zurück.

Der Server hält einen Replay-Puffer von rund den letzten 5 Minuten an Ereignissen. Übergeben Sie from_seq=N bei der Wiederverbindung, um verpasste Ereignisse abzuspielen, statt einen vollständigen Snapshot zu erhalten. Resume erfolgt nach bestem Bemühen (Best-Effort): Bei längeren Ausfällen oder einer Wiederverbindung während Serverwartungsarbeiten setzt der Server Sie mit einem vollständigen Snapshot zurück, gekennzeichnet durch mode: "full_resync" bei snapshot:complete — und ein Resume, der auf eine Lücke stößt, setzt gap_detected: true. Ihr Client muss beide Fälle behandeln — Details siehe Streaming Reliability Contract und Wiederverbindung mit Replay.

SchließcodesPermalink for this section

CodeBedeutungAktion
1000Normales SchließenKeine Aktion erforderlich
1006Abnormales Schließen (clientseitig)Netzwerkabbruch — immer wiederverbinden
4001Authentifizierung fehlgeschlagen oder Verdrängung durch eine neuere Session mit demselben Key — Close-Reason lesenUngültiger Key → korrigieren. "displaced by newer session" → nicht automatisch neu verbinden (single-connection)
4003Berechtigungen haben sich während des Streams geändert (Downgrade, widerrufener Key, entferntes Add-on)Neu verbinden, um mit den aktuellen Berechtigungen zu autorisieren

Code 1006 ist gemäß RFC 6455 reserviert und wird niemals vom Server gesendet. Ihre WebSocket-Bibliothek generiert ihn lokal, wenn die TCP-Verbindung ohne Schließ-Handshake abbricht (Netzwerkausfall, Prozessabbruch). Verbinden Sie sich immer erneut, wenn Sie ihn sehen — das untenstehende Beispiel macht dies bereits korrekt: Es überspringt die Wiederverbindung nur bei 1000 und bei einer 4001-Verdrängung, niemals bei 1006.

Vollständige API-ReferenzPermalink for this section

Die vollständige Dokumentation einschließlich aller Nachrichtenschemata, Fehlerbehandlung und mehrsprachiger Beispiele finden Sie in der WebSocket-API-Referenz.

Last updated on