pinnacle odds api / xml feed / migration
The Pinnacle XML odds feed is retired. Here is its JSON replacement.
GET/kit/v1/markets?sport_id=1
{ events, last }200 OK
Pinnacle's public feeds ended in July 2025, and the XML odds feed went with them. The same prices now come from the Pinnacle odds API as JSON: one authenticated GET returns the board, and the shape mirrors the legacy third-party formats closely enough that existing clients swap the base URL and key and keep working. The free key covers everything on this page: 20 requests a minute and 100 a day, figures checked 2026-11-07.
Create a free keyYour XML habits, one to one
The fixture block you used to parse is now an event object: teams are home and away, the start time is starts, and the league is league_name. The moneyline your parser hunted for sits at periods.num_0.money_line with home, draw and away as decimal prices, and spreads, totals and team totals sit beside it keyed by their line.
What the XML feed called a refresh is now a cursor: every response carries last, and sending it back as since returns only the events that changed. A poller that used to re-download the whole document now reads a delta.
# From XML habits to the JSON
# board. The key and base URL
# come from your account and
# the docs.
export ODDS_API_KEY="your key"
export ODDS_BASE_URL="https://..."
U="$ODDS_BASE_URL/kit/v1/markets"
H="x-portal-apikey: $ODDS_API_KEY"
# The board as JSON: events and
# the last cursor.
curl -s -H "$H" "$U?sport_id=1" \
| jq '{last, n: (.events | length)}'
# One line per match: the fields
# your XML parser used to read.
curl -s -H "$H" "$U?sport_id=1" \
| jq -r '.events[]
| [.home, .away, .starts,
.periods.num_0.money_line.home,
.periods.num_0.money_line.draw,
.periods.num_0.money_line.away]
| @tsv'
Select the code to copy it.
MAPelement to field
decimal prices throughoutJSON
From XML elements to JSON fields
Whatever the exact tags your old parser read, the concepts translate directly. The fixture element becomes an entry in the events array; the sport grouping becomes the sport_id parameter on the request; the moneyline, handicap and totals elements become money_line, spreads and totals under the period; and the document timestamp becomes the last cursor you send back as since. Prices are decimal throughout, so no fractional or American conversion survives the move unless you add it yourself.
Two things have no XML counterpart worth porting. Live prices move too fast for a document model, so the same board endpoint serves them with a freshness the XML feed never had, and drop alerts arrive as a push stream rather than a diff you computed by hand; the Python page shows that reader.
What people ask when the XML feed dies
Short answers before you rewrite the parser.
- Is there still a Pinnacle XML odds feed?
No. Pinnacle's public feeds ended in July 2025. The current path is the Pinnacle odds API over JSON, served by an independent feed with a free key on signup.
- Can I keep my XML parser?
No part of the JSON API speaks XML. The pragmatic move is a thin adapter: one function that fetches the JSON and emits the rows your parser already accepts.
- Does the JSON feed cover the same sports?
Yes, the board covers the sports Pinnacle prices, live and prematch. The sport IDs your integration used are stable integers in the new API too.
- What does migration cost?
Nothing while you test: the free key allows 20 requests a minute and 100 a day, enough to port and verify a parser before any plan decision.
NEXTbash xml-to-json.sh
One evening to leave the XML behind
Create the free key, point the script at your sport and compare the rows against the document you remember. When the polling loop needs more than 100 calls a day, the pricing page shows the plan that lifts the cap.