Exchange prices over REST
One GET returns the whole board. /livemap for the full snapshot, /events for a cheap index, /events/{id} for a single fixture.
Live and prematch exchange markets over REST and an optional WebSocket push stream. Plain JSON, one credential, and freshness stated on every single response — never implied.
No Betfair account, no app key, no exchange session. Your API key is your login.
// GET /betfair/v1/livemap?feed=live&sports=soccer
{ "meta": { "feed": "live", "seq": 18422,
"stale": false, "age_ms": 1180,
"event_count": 312 },
"liveMap": {
"34627115": {
"team1": "Arsenal", "team2": "Chelsea",
"sport": "soccer",
"tournament": "Premier League",
"openTimestamp": "2026-08-15T19:00:00Z",
"markets": [
{ "marketId": "1.248817",
"name": "Match Odds",
"runner": "Arsenal", "runnerId": 1,
"backOdds": 1.94, "backSize": 1200,
"layOdds": 1.96, "laySize": 900 },
/* … one entry per runner … */
] } } }
// inbound after subscribe — changed events only
{ "type": "update", "feed": "live",
"meta": { "seq": 18423, "age_ms": 140, "stale": false },
"changed": { /* eventId -> event */ },
"removed": [] }
MODULE 01 — What ships with the key
Both feeds and every market carried on them ship with every plan. Plans differ in request rate and push access — never in what the data contains.
One GET returns the whole board. /livemap for the full snapshot, /events for a cheap index, /events/{id} for a single fixture.
Every runner carries backOdds and layOdds plus the backSize and laySize sitting behind them — the spread and the liquidity, not just a mid price.
Two independent feeds on one key, selected with ?feed=live or ?feed=prematch. Omit it and you get live — only an unrecognised value is an error.
Snapshot on subscribe, then only what changed. Included in Pro + WS and Scale, or added to a plain Pro plan. Details ↓
Every response carries meta.age_ms and meta.stale. Live is flagged after 15 s, prematch after 120 s. You never have to guess whether a price is current.
Narrow the board to the sports you trade with ?sports=, and take the snapshot gzipped — the server compresses once per sequence and shares it across callers.
10 requests/second on Pro, 30 on Scale, with no monthly call ceiling to plan around. One concurrent WebSocket connection per key, on every plan.
BTC, ETH, USDT, TRX and more through NOWPayments. No cards, no auto-renew, no payment details sitting in someone's database.
MODULE 02 — Wire up in four lines
# pip install requests
import requests
H = {"x-portal-apikey": "YOUR_KEY"}
r = requests.get("https://betfair-odds.com/betfair/v1/livemap",
params={"feed": "live", "sports": "soccer"},
headers=H).json()
for ev_id, ev in r["liveMap"].items():
for m in ev["markets"]:
print(ev["team1"], "v", ev["team2"],
m["runner"], m["backOdds"], "/", m["layOdds"])
// node 18+, no dependencies
const url = "https://betfair-odds.com/betfair/v1/livemap?feed=live";
const res = await fetch(url, {
headers: { "x-portal-apikey": process.env.BETFAIR_ODDS_KEY }
});
const { meta, liveMap } = await res.json();
console.log(meta.seq, meta.age_ms, meta.stale);
for (const [id, ev] of Object.entries(liveMap))
console.log(ev.team1, "v", ev.team2, ev.markets.length);
> x-portal-apikey: KEY
> x-api-key: KEY
> ?key=KEY
Any of the three works on every REST call. The same key authenticates the WebSocket stream.
?feed= takes live or prematch and defaults to live. Lapsed key? /me still answers and tells you why.
Add-on
Stop polling. Authenticate, subscribe to the feeds and sports you care about, and take a snapshot followed by every change as it lands. Large snapshots arrive chunked at 512 KB carrying seq and final, so a client with a modest message limit never gets dropped mid-board — and a snapshot small enough to fit in one frame carries neither, so absence of seq means complete.
Included in Pro + WS and Scale. Sold separately only for adding push to a plain Pro plan — you are never billed for it twice. One concurrent connection per key.
+$99/ month// within 10s of connecting
→ { "action": "authenticate", "apiKey": "KEY" }
← { "type": "authenticated", "tier": "scale" }
→ { "action": "subscribe",
"feeds": ["live", "prematch"],
"sports": ["soccer"] }
← { "type": "subscribed", "feed": "live" }
← { "type": "snapshot", "feed": "live", "liveMap": {…} }
← { "type": "update", "changed": {…}, "removed": [] }
← { "type": "ping", "buffered_max_bytes": 0 }
→ { "action": "pong" }
feeds takes live, prematch or both on one socket. unsubscribe drops a feed without tearing the connection down.
Optional sports filter narrows the push to what you trade, so a soccer-only client never pays to receive the rest of the board.
Every ping carries buffered_max_bytes — the highest send backlog since the last ping. Climbing across pings means your read loop is falling behind.
MODULE 03 — Terms
Every paid plan carries both feeds, every market on them, back and lay with sizes, and the dashboard with its usage graph. You are buying throughput and push access — never a thinner feed.
Forever, not just the first invoice. Renewals, upgrades, plan changes, all of it. Payouts in crypto, bank or PayPal once your unpaid balance reaches $50. If you run a tool, a community or a screen, your users are already asking where the prices come from.
MODULE 04 — Query log
meta.age_ms and meta.stale, live is flagged stale after 15 s and prematch after 120 s, and GET /betfair/v1/health reports both feeds without a key.One key, one connection, and exchange prices with the liquidity behind them. Create an account in seconds — no Betfair login, no card on file.