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. WebSocket
SharpAPI bietet zwei Streaming-Protokolle. Beide liefern dieselben Daten mit derselben Geschwindigkeit.
| SSE | WebSocket | |
|---|---|---|
| URL | https://api.sharpapi.io/api/v1/stream | wss://ws.sharpapi.io |
| Richtung | Server → Client | Bidirektional |
| Update-Filter | Erneute Verbindung erforderlich | subscribe-Nachricht senden |
| Wiederverbindung | Automatisch (Last-Event-ID) | Manuell (Backoff implementieren) |
| Am besten geeignet für | Einfache Konsumenten, Browser | Interaktive 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.
Schnellstart
1. Verbinden
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 verarbeiten
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 aufrechterhalten
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 25000);4. Kanäle und Filter aktualisieren (keine Wiederverbindung erforderlich)
ws.send(JSON.stringify({
type: 'subscribe',
channels: ['low_hold'],
filters: { sports: ['football'], sportsbooks: ['fanduel', 'betmgm'], leagues: ['nfl'] }
}));Nachrichtenfluss
Beim Verbindungsaufbau erhalten Sie Nachrichten in folgender Reihenfolge:
connected— Willkommensnachricht mit Ihrem Tarif, Funktionen und aktiven Kanälensubscribed— Bestätigung der aktiven Kanäle und Filteropportunities_snapshot— Eine pro abonniertem Opportunitäts-Kanal (ev, arbitrage, middles, low_hold)initial— Quoten-Snapshot pro Sportsbook (nur wenn derodds-Kanal abonniert ist)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 (erfordertodds-Kanal)odds:removed— Quoten wurden von einem Sportsbook entfernt (erfordertodds-Kanal)ev:detected/ev:expired— +EV-Opportunitäten (erfordertev-Kanal)arb:detected/arb:expired— Arbitrage-Opportunitäten (erfordertarbitrage-Kanal)middles:detected/middles:expired— Middle-Opportunitäten (erfordertmiddles-Kanal)low_hold:detected/low_hold:expired— Low-Hold-Opportunitäten (erfordertlow_hold-Kanal)heartbeat— Server-Keepalive alle 30 Sekunden
Wiederverbindung
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ßcodes
| Code | Bedeutung | Aktion |
|---|---|---|
1000 | Normales Schließen | Keine Aktion erforderlich |
1006 | Abnormales Schließen (clientseitig) | Netzwerkabbruch — immer wiederverbinden |
4001 | Authentifizierung fehlgeschlagen oder Verdrängung durch eine neuere Session mit demselben Key — Close-Reason lesen | Ungültiger Key → korrigieren. "displaced by newer session" → nicht automatisch neu verbinden (single-connection) |
4003 | Berechtigungen 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-Referenz
Die vollständige Dokumentation einschließlich aller Nachrichtenschemata, Fehlerbehandlung und mehrsprachiger Beispiele finden Sie in der WebSocket-API-Referenz.