|
| 1 | +--- |
| 2 | +description: "How to cover many sports, leagues, books, events, and opportunity types on a single SSE or WebSocket connection. Comma-separated filters and channel multiplexing replace the need for multiple parallel streams." |
| 3 | +--- |
| 4 | + |
| 5 | +import { Callout, Tabs } from 'nextra/components' |
| 6 | + |
| 7 | +# One Connection, Many Topics |
| 8 | + |
| 9 | +A single SharpAPI stream can cover everything you need — multiple sports, multiple leagues, multiple books, multiple events, and odds + every opportunity type — on one socket. You almost never need to open a stream per filter. |
| 10 | + |
| 11 | +<Callout type="info"> |
| 12 | +The per-key concurrent stream cap is **1 by default for every paid tier**, with newer-wins displacement: a second connection from the same key kicks the older one. This page shows the patterns that make a single connection sufficient. If you genuinely need parallel sockets (multiple processes / machines), see [Fleet & Multi-Process](#fleet--multi-process) below. |
| 13 | +</Callout> |
| 14 | + |
| 15 | +## Why one connection is enough |
| 16 | + |
| 17 | +Every identity filter on the stream endpoint accepts a **comma-separated list**, and the `channel` parameter (`all`) merges odds and opportunity events into the same event stream. The server pre-serializes each cycle once and applies your filters per-connection, so wide filters are cheap — there's no efficiency penalty for subscribing to "everything you care about" on one socket vs. splitting it. |
| 18 | + |
| 19 | +| Filter | Single value | Multiple values | |
| 20 | +|--------|--------------|-----------------| |
| 21 | +| `sport` | `sport=basketball` | `sport=basketball,football,ice_hockey` | |
| 22 | +| `league` | `league=nba` | `league=nba,nfl,mlb,nhl` | |
| 23 | +| `sportsbook` | `sportsbook=draftkings` | `sportsbook=draftkings,fanduel,pinnacle` | |
| 24 | +| `market` | `market=moneyline` | `market=moneyline,point_spread,total_points` | |
| 25 | +| `event` | `event=evt_abc` | `event=evt_abc,evt_def,evt_ghi` | |
| 26 | +| `channel` (SSE) | `channel=odds` | `channel=all` (odds + opportunities) | |
| 27 | +| `channels` (WS) | `channels=ev` | `channels=ev,odds,arbitrage,middles,low_hold` | |
| 28 | + |
| 29 | +## Patterns |
| 30 | + |
| 31 | +### Multi-sport: NBA + NFL + MLB on one socket |
| 32 | + |
| 33 | +Instead of opening three streams, pass all three leagues to one: |
| 34 | + |
| 35 | +<Tabs items={['JavaScript', 'Python']}> |
| 36 | + <Tabs.Tab> |
| 37 | +```javascript |
| 38 | +const es = new EventSource( |
| 39 | + 'https://api.sharpapi.io/api/v1/stream' + |
| 40 | + '?channel=all' + |
| 41 | + '&league=nba,nfl,mlb' + |
| 42 | + '&api_key=YOUR_KEY' |
| 43 | +); |
| 44 | + |
| 45 | +es.addEventListener('odds:update', (e) => { |
| 46 | + const { odds, book } = JSON.parse(e.data); |
| 47 | + // odds[].league tells you which league this update is for |
| 48 | + for (const o of odds) routeByLeague(o); |
| 49 | +}); |
| 50 | +``` |
| 51 | + </Tabs.Tab> |
| 52 | + <Tabs.Tab> |
| 53 | +```python |
| 54 | +import sseclient, requests, json |
| 55 | + |
| 56 | +response = requests.get( |
| 57 | + 'https://api.sharpapi.io/api/v1/stream', |
| 58 | + params={'channel': 'all', 'league': 'nba,nfl,mlb'}, |
| 59 | + headers={'X-API-Key': 'YOUR_KEY'}, |
| 60 | + stream=True, |
| 61 | +) |
| 62 | +for event in sseclient.SSEClient(response).events(): |
| 63 | + if event.event == 'odds:update': |
| 64 | + data = json.loads(event.data) |
| 65 | + for o in data['odds']: |
| 66 | + route_by_league(o) |
| 67 | +``` |
| 68 | + </Tabs.Tab> |
| 69 | +</Tabs> |
| 70 | + |
| 71 | +### Odds + every opportunity type on one socket |
| 72 | + |
| 73 | +Set `channel=all` (SSE) or include every opportunity type in `channels=` (WS): |
| 74 | + |
| 75 | +<Tabs items={['SSE', 'WebSocket']}> |
| 76 | + <Tabs.Tab> |
| 77 | +```javascript |
| 78 | +const es = new EventSource( |
| 79 | + 'https://api.sharpapi.io/api/v1/stream?channel=all&api_key=YOUR_KEY' |
| 80 | +); |
| 81 | + |
| 82 | +es.addEventListener('odds:update', handleOddsDelta); |
| 83 | +es.addEventListener('ev:detected', handleEV); |
| 84 | +es.addEventListener('arb:detected', handleArb); |
| 85 | +es.addEventListener('middles:detected', handleMiddle); |
| 86 | +es.addEventListener('low_hold:detected', handleLowHold); |
| 87 | +``` |
| 88 | + </Tabs.Tab> |
| 89 | + <Tabs.Tab> |
| 90 | +```javascript |
| 91 | +const ws = new WebSocket( |
| 92 | + 'wss://ws.sharpapi.io' + |
| 93 | + '?api_key=YOUR_KEY' + |
| 94 | + '&channels=odds,ev,arbitrage,middles,low_hold' |
| 95 | +); |
| 96 | +``` |
| 97 | + </Tabs.Tab> |
| 98 | +</Tabs> |
| 99 | + |
| 100 | +The server tags each message with its event type — your handler dispatches by event name, exactly as if you'd opened five separate streams. |
| 101 | + |
| 102 | +### Tracking N specific events |
| 103 | + |
| 104 | +If you want tight updates on a fixed list of events (e.g. ten games tonight), pass them all to `event=`: |
| 105 | + |
| 106 | +```javascript |
| 107 | +const eventIds = [ |
| 108 | + 'nba_lal_bos_2026-04-30', 'nba_phx_dal_2026-04-30', |
| 109 | + 'nfl_kc_buf_2026-05-01', /* ... */ |
| 110 | +].join(','); |
| 111 | + |
| 112 | +const es = new EventSource( |
| 113 | + `https://api.sharpapi.io/api/v1/stream?channel=all&event=${eventIds}&api_key=YOUR_KEY` |
| 114 | +); |
| 115 | +``` |
| 116 | + |
| 117 | +This replaces the pattern of opening one `/stream/events/{eventId}` socket per event. |
| 118 | + |
| 119 | +### Per-book or per-market splits |
| 120 | + |
| 121 | +Same idea for books and markets: |
| 122 | + |
| 123 | +```javascript |
| 124 | +// Track Pinnacle sharp moves + DK/FD live prices, moneyline + spreads only: |
| 125 | +const es = new EventSource( |
| 126 | + 'https://api.sharpapi.io/api/v1/stream' + |
| 127 | + '?channel=odds' + |
| 128 | + '&sportsbook=pinnacle,draftkings,fanduel' + |
| 129 | + '&market=moneyline,point_spread' + |
| 130 | + '&api_key=YOUR_KEY' |
| 131 | +); |
| 132 | +``` |
| 133 | + |
| 134 | +### Dynamic re-subscription (WebSocket) |
| 135 | + |
| 136 | +The bidirectional control of WebSocket lets you change filters mid-connection without dropping the socket. Use this when your set of "interesting" topics changes over the day (e.g. user opens / closes views in a dashboard): |
| 137 | + |
| 138 | +```javascript |
| 139 | +const ws = new WebSocket('wss://ws.sharpapi.io?api_key=YOUR_KEY'); |
| 140 | + |
| 141 | +// Initial subscription |
| 142 | +ws.onopen = () => ws.send(JSON.stringify({ |
| 143 | + type: 'subscribe', |
| 144 | + channels: ['odds', 'ev'], |
| 145 | + filters: { sport: ['basketball'], league: ['nba'] }, |
| 146 | +})); |
| 147 | + |
| 148 | +// Later — user adds NFL to their dashboard |
| 149 | +function addNFL() { |
| 150 | + ws.send(JSON.stringify({ |
| 151 | + type: 'subscribe', |
| 152 | + filters: { sport: ['basketball', 'football'], league: ['nba', 'nfl'] }, |
| 153 | + })); |
| 154 | +} |
| 155 | +``` |
| 156 | + |
| 157 | +See the [WebSocket API Reference](/en/api-reference/websocket) for the full subscribe-message schema. |
| 158 | + |
| 159 | +## Fleet & multi-process |
| 160 | + |
| 161 | +The one case where a single connection genuinely doesn't work: **separate processes or machines that can't share a socket.** Examples: |
| 162 | + |
| 163 | +- Ten trading bots running on ten boxes, each needing its own live feed. |
| 164 | +- Worker pool where N workers each consume a stream independently. |
| 165 | +- Backend service + browser dashboard, both authenticated as the same user. |
| 166 | + |
| 167 | +The default 1-stream cap means a second connection on the same key will displace the first (close code `4001 displaced by newer session` on WS, writer teardown on SSE). The intended escape hatch is a **per-key `maxStreams` override** in Unkey metadata, sized to the fleet. This is set up on Enterprise plans — contact [hello@sharpapi.io](mailto:hello@sharpapi.io) to provision a higher cap for a specific key. |
| 168 | + |
| 169 | +For everything else — even very wide subscriptions — the patterns above let one socket cover what looks like ten separate concerns. |
| 170 | + |
| 171 | +## Server-side cost |
| 172 | + |
| 173 | +There is no efficiency reason to split. The server's hot path: |
| 174 | + |
| 175 | +1. Computes the per-cycle book diff **once**. |
| 176 | +2. Pre-serializes the unfiltered changed/removed payloads **once**. |
| 177 | +3. For each connected client, applies their filter and serializes the matching subset (or sends the pre-serialized bytes directly when the client has no content filters). |
| 178 | + |
| 179 | +A client with `league=nba,nfl,mlb,nhl` costs almost the same as four clients each with one league — minus four sockets, four TLS handshakes, four sets of HTTP headers, and four `connected`/`snapshot` cycles. The single-connection pattern is also better for **delta consistency**: you see all updates in the order the server produced them, instead of interleaving across sockets with independent backpressure. |
| 180 | + |
| 181 | +## Migration checklist |
| 182 | + |
| 183 | +If you're moving from a multi-stream architecture: |
| 184 | + |
| 185 | +1. **Combine identity filters** — collect every `sport`, `league`, `sportsbook`, `market`, and `event` value across your old streams and join them with commas on one URL. |
| 186 | +2. **Use `channel=all`** (SSE) or every needed `channels=` value (WS) instead of one stream per event type. |
| 187 | +3. **Dispatch in your handler** — read `odds[].league` / `odds[].sportsbook` / `event.event` to route messages internally exactly as before. |
| 188 | +4. **Drop your reconnect-N-times logic** — one socket means one reconnect path. SSE auto-reconnects; for WS, see the [reconnection pattern](/en/streaming/websocket#reconnection). |
| 189 | +5. **Keep `min_ev` / `min_profit` thresholds** — these still narrow the opportunity stream server-side and reduce bandwidth. |
| 190 | + |
| 191 | +## See also |
| 192 | + |
| 193 | +- [Streaming Overview](/en/streaming/overview) — protocol comparison and quick start |
| 194 | +- [WebSocket Streaming Guide](/en/streaming/websocket) — subscribe messages, reconnection |
| 195 | +- [SSE API Reference](/en/api-reference/stream) — full filter parameter list |
| 196 | +- [WebSocket API Reference](/en/api-reference/websocket) — subscribe schema, close codes |
0 commit comments