Skip to content

Changelog

The API is live at /v1. This page records API changes and the policy that governs them.

The API serves sports, bookmaker and market catalogues, upcoming and in-play odds, player props, and recorded history. Start with the quickstart.

2026-10-21: /v1/markets?sport= lists one sport’s books

Section titled “2026-10-21: /v1/markets?sport= lists one sport’s books”

GET /v1/markets takes an optional sport (nfl, soccer, epl, tennis, …). With it, the list holds only that sport’s markets, each entry’s bookmakers names only the books that carry the market for that sport, and sports is that one sport. A soccer league key answers as soccer, a tennis tournament key as tennis; a key that is not a sport key returns 404 UNKNOWN_SPORT. Without sport nothing changes: bookmakers is every book that carries the market in any of its sports.

Also in this release: Pinnacle is no longer listed for soccer btts or corners_total; it does not offer them.

2026-10-14: league on soccer fixtures, tournament on tennis

Section titled “2026-10-14: league on soccer fixtures, tournament on tennis”

Soccer fixtures carry league: {key, title} (for example {"key": "epl", ...}, the key soccer?league= takes). Tennis fixtures now carry the same object as tournament, the key tennis?tournament= takes, instead of league. Other sports carry neither field: their sport key is already the league. Live, history, prop history and websocket bodies all follow this; event ids do not change.

2026-10-14: props carry one player name, a player_id and main

Section titled “2026-10-14: props carry one player name, a player_id and main”

Every row on /props, /props/live and a single event’s props now names its player one way across all books of the fixture: the spelling the big US books use, where before each book kept its own (A.J. Barner, AJ Barner and Aj Barner on one game). A team defense is one player per team (SEA D/ST). Each row also carries player_id, a fixed id such as nfl:jaxonsmithnjigba that is the same on every book; join on it rather than on the name. And each row carries main: true | false: exactly one row per bookmaker, player and stat is that book’s main line, and it comes first among that player’s lines for the stat. The main line is the one priced on both sides with the most even prices; a line priced on one side only (a milestone such as Over 249.5) is main only when the book prices no line on both sides.

2026-10-14: history series starts full, then sends changed outcomes

Section titled “2026-10-14: history series starts full, then sends changed outcomes”

For fixtures with archived change rows, each bookmaker and market’s first series point contains its full market. Later points contain only outcomes whose price, point or size changed, with their own values and recorded_at. Apply points in order to rebuild the market. A since-bounded request starts full at the first emitted time, seeded from earlier changes. Fixtures without change rows retain whole-market checkpoint points. Opening and closing routes are unchanged. See the history guide.

2026-10-14: main lines first, with an explicit flag

Section titled “2026-10-14: main lines first, with an explicit flag”

Every spread, total and team-total ladder now puts its main line first across upcoming and live odds, one fixture, the live feed, and opening and closing history. Series points keep recorded order and omit main flags while they represent partial timestamp batches. Each outcome on these main-first ladders adds main: true|false. The book’s featured two-sided line takes priority; otherwise the most balanced two-sided line by implied probability is main. Lines with an implied probability sum at or below 1.25 take priority over lines above 1.25. A one-sided line is main only if there is no two-sided line. Extreme exchange and prediction-market quotes remain as quoted, including single-line moneylines.

Historical team totals now return separate market objects for each team, named by each outcome’s description; quotes without a recorded team name remain available.

2026-10-07: every list splits into upcoming and live

Section titled “2026-10-07: every list splits into upcoming and live”

/v1/sports/{sport}/odds, /events and /props now return only fixtures that have not started, on every plan. A fixture leaves them at kickoff. Three new routes return only fixtures in play now (started and not finished): /v1/sports/{sport}/odds/live, /events/live and /props/live. They take the same selectors and formats, need in-play odds (Growth and up) and are refused on Free and Starter with the same PLAN_REQUIRED answer as a started fixture asked for by id. They do not need the live feed add-on. Aggregate soccer and tennis lists still carry every upcoming fixture, with no date cap. Single-event and historical routes are unchanged.

Every fixture object now has "is_live": true | false right after commence_time, on /events, /odds, a single event’s odds, /props, the historical routes and the live feed. It is true from kickoff until the game is over (each sport has its own cut-off after kickoff) and false before and after, so you no longer compare commence_time with your own clock. It is worked out when the response is served, so it flips at kickoff even while prices are unchanged. On the live feed a flip arrives as one compact status upsert (event_id, is_live) and moves no price.

2026-09-28: the live feed sends a price only when it moves

Section titled “2026-09-28: the live feed sends a price only when it moves”

A changes message on the live feed now sends an outcome upsert only when something on that outcome actually moved. When a book re-confirms a market and no price in it moved, you get one small unchanged upsert for the market (event_id, bookmaker, market, last_update) instead of every price again: set that market’s last_update and keep its prices. Measured on our soccer feed, 99.5% of re-confirmed prices had not moved, so this cuts a busy subscription’s traffic many times over while every price still carries proof of when the book last confirmed it; see only what moved.

2026-09-28: a live feed picture or changes message can arrive in parts

Section titled “2026-09-28: a live feed picture or changes message can arrive in parts”

A picture or changes message on the live feed now carries part and parts. Most of the time both are 1. A message too big for one WebSocket frame (a busy sport’s default picture can run several megabytes) now arrives as several consecutive frames of the same type instead of one oversized frame. Every part shares the same cursor and resume_token, in order, never split mid-fixture or mid-entry. Reassemble by concatenating data (or upserts/removals) across parts and treat the message complete once part == parts; see part and parts. Keep a resume_token only from a complete message: if the connection drops between parts, discard the partial message and resume with the token of the last complete one.

2026-09-28: outcomes are named after the fixture’s teams, away first

Section titled “2026-09-28: outcomes are named after the fixture’s teams, away first”

On h2h and spreads (every period), each team outcome’s name is now exactly the fixture’s own away_team or home_team, whatever spelling the book itself uses (a book’s PHI Eagles is served as Philadelphia Eagles). Outcomes come in a fixed order: the away team, then the home team, then Draw on a three-way moneyline, rung by rung on a ladder. A team_totals outcome’s description names the fixture’s team the same way, and totals are always Over then Under.

2026-09-28: the period is a field, not a key suffix

Section titled “2026-09-28: the period is a field, not a key suffix”

A market’s key is now its family alone (h2h, spreads, totals, team_totals, …), never a period suffix. A period market instead carries a separate period field (1h, 2h, 1q..4q, 1p..3p, f5, 1i, 1s..5s, reg); a full-game market has no period field at all. Select periods with the new periods request parameter: omit it for the full game only, name periods to get just those (periods=1h), add full to keep the full game beside others (periods=full,1h), or ask for everything (periods=all). The same rule now applies to player props (stat is the stem alone, period moves to period), to the history endpoints, and to /v1/markets (the family field is gone since it always equalled key; one row per key and period). Old suffixed keys such as totals_f5 now return 422 INVALID_MARKET.

Within /v1, only additive changes ship without notice: a new endpoint, a new optional parameter, a new response field, or a new value in a field already marked x-extensible-enum (a market key, an error code, a plan). Renaming or removing a field, changing its type, or changing what a parameter defaults to never ships inside /v1. That kind of change ships as /v2 instead, with /v1 kept running and both versions documented for a minimum 90-day notice period.

Adding an accepted request value, making a required parameter optional, adding a response header, or unlocking an operation for another plan is additive. Narrowing accepted request values, changing a field’s meaning, or changing behavior when a parameter is omitted requires a new API version. Response value lists may grow; clients should accept unfamiliar values. The published API reference defines the fields covered by this policy.

A new version is announced here and in the documentation at least 90 days before /v1 stops answering. Both versions remain documented and callable throughout that period. /v1 responses carry a Deprecation header (RFC 9745) from the announcement and a Sunset header (RFC 8594) giving its retirement date. Migration notes explain what changed and how to move. During the notice period, /v1 keeps its existing behavior and access rules.