Building a live draft companion on the Sleeper API
The Sleeper API is read-only and has no websocket. "Live" means polling
GET /draft/<id>/picks on a timer. That's fine — until you realize that
self-hosting means every outbound call leaves from your home IP, and Sleeper
blocks IPs that cross ~1000 calls/minute.
One poller to rule them all
The fix is structural: exactly one poller per draft on the server, fanned out to every client watching that draft over our own WebSocket. Ten people watching the same draft is still one Sleeper call every three seconds — not ten.
Sleeper ◄── 1 poll / 3s ── [shared poller] ── deltas ──► WS clients (N)
The poller diffs each poll against the previous snapshot and pushes only the new picks. Clients hold their ranking locally and recompute the board from the deltas — the decision layer runs in your browser, the sync runs once on the server.
Heartbeats are not optional
Cloudflare drops idle WebSocket connections after ~100 seconds, and a draft sits idle the entire time someone is on the clock. Without a ping/pong every ~30s, clients silently drop and miss picks. Ask me how I know.