Real-time streams require the Sharp plan.
wss://api.apinn.io/ws pushes odds movements like the SSE stream, with one difference that matters: the connection is two-way, so you can tell the server what you want. Without a subscription you receive the whole book — exactly what the SSE stream sends.
Pass your key in the X-API-Key header, or as ?key=YOUR_KEY in the URL (a browser cannot set a header on a WebSocket connection).
Node.js
Subscribing
Send{"op": "subscribe", ...} with any combination of the filters below. They are cumulative: a line must match all the filters you set.
Subscribing again replaces the previous subscription and returns a fresh snapshot.
{"op": "unsubscribe"} returns you to the full book, and {"op": "ping"} answers {"t": "pong"}.
Server messages
Every delta also carries a
seq, incremented once per broadcast. A client with no subscription receives every number in order, so a gap means a lost message; a client with a subscription will normally see gaps (the broadcasts that did not concern it).
Removals
removed and removed_specials list the series that no longer exist — a match that ended, a market that closed. Delete them from your book. A client that ignores them keeps serving odds on markets that are gone: measured over nine hours, that is around 2.5% of the book, and it grows.
Removed entries carry only what identifies the series, not prices: event_id, period, market, line for odds; event_id, special_id, period, contestant_name, handicap for specials. They are filtered by your subscription like everything else.
Rows use the same shape as /api/odds: event_id, period, market, line, the odds (odds1/odds0/odds2), the true odds (todds*), status, and timestamp — the time of the last price change, not the time we sent it.
Why subscribe
Filtering is not just convenience. Measured on the live book, football alone is around 40% of all movements, moneyline on full time around 49%, and a single match is a rounding error. A client that follows a handful of matches goes from tens of thousands of lines an hour to a few dozen.Odds lines carry
event_id, period and market, but not the sport or the league — those are resolved through the fixtures. An event that is not yet in the fixtures index will not match a sports or leagues filter; filter on events if you need it immediately.