FBSharp
2026-06-10

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.