Skip to main content
The /ws/odds channel is the raw firehose. It emits every price_change from every book we track, plus everything the /ws/live channel emits (score_change, status_change, game_final, new_game). This is the channel for arbitrage, alerting, and real-time market analysis.

Event types on this channel

The snapshot frame on /ws/odds contains the same game-state baseline as /ws/live — the currently-live games and their scores/clocks. It does not contain market snapshots. For initial market state, call the REST endpoint GET /v1/games/{id}/markets for each game you care about, then stream price_change deltas from there.

Real captured sample

This is the actual wire content from wss://books.polynode.dev/ws/odds during a live match where Estudiantes went up 2-1 (Copa Libertadores, 2H 64’):
Notice the pattern: Estudiantes scores, their 2-1 correct-score outcome is now more likely, and we see 888sport, BetRivers, and Betano all shortening the price simultaneously (from +275/+260 to +230-240). That’s the sort of signal this channel exists to surface.

Volume expectations

During a mid-afternoon window with 5-10 games live, sustained rate is typically 100-500 price_change events per second, with bursts over 1,000/s immediately after goals or scoring plays. Plan your client-side processing accordingly.
  • Memory: each event is ~400 bytes JSON, so 500/s = ~200 KB/s ≈ 1.6 Mbit/s per subscriber.
  • CPU: JSON parsing + any per-event logic. On a modern laptop, ~5,000 events/sec is easy; beyond that you want to batch or pre-filter.
  • Storage: 500 events/sec × 86,400 seconds/day × 400 bytes ≈ 17 GB/day if you log everything. Filter aggressively or sample.

Filtering

Server-side filtering is not yet implemented in v1. Filter client-side on whichever of these fields you care about:
  • pn_league_code (e.g., only NBA → keep nba)
  • pn_market_type (e.g., only moneyline → keep moneyline)
  • book (e.g., only Polymarket vs DraftKings → keep ["Polymarket", "DraftKings"])
  • pn_slug or game_id (e.g., only a single game)
A proper subscription-filter layer is on the roadmap for v1.1.

Example: arbitrage detector

Ordering guarantees

  • Within a single poll cycle, score_change events are emitted before price_change events for the same game. Clients can rely on seeing the new score before seeing the price movements that respond to it.
  • Across poll cycles, events are delivered in emission order per connection.
  • If your consumer drops (channel full, 256 events queued), events are silently dropped, not buffered indefinitely. Detect gaps by tracking the ts field.

See also

  • Event Reference — full schema for price_change and all other event types
  • Live Channel — if you only need game state without the odds firehose