Getting started
Quickstart
From nothing to a live price comparison in three calls, using the demo key.
1. List sports
See which sports and leagues are listed right now. Free: 0 tokens.
curl -s "https://api.b337.ai/odds/v1/sports" \
-H "X-API-Key: demo"import requests
resp = requests.get(
"https://api.b337.ai/odds/v1/sports",
headers={"X-API-Key": "demo"},
timeout=10,
)
resp.raise_for_status()
print(resp.json())// Node 18+, saved as an .mjs file
const res = await fetch("https://api.b337.ai/odds/v1/sports", {
headers: { "X-API-Key": "demo" },
});
console.log(await res.json());2. Find an event
Ask for events in a league. Each one carries an event_id and the books that list it.
curl -s "https://api.b337.ai/odds/v1/events?league=epl&limit=1" \
-H "X-API-Key: demo"import requests
resp = requests.get(
"https://api.b337.ai/odds/v1/events",
params={"league": "epl", "limit": "1"},
headers={"X-API-Key": "demo"},
timeout=10,
)
resp.raise_for_status()
print(resp.json())// Node 18+, saved as an .mjs file
const res = await fetch("https://api.b337.ai/odds/v1/events?league=epl&limit=1", {
headers: { "X-API-Key": "demo" },
});
console.log(await res.json());Response
{
"count": 1,
"truncated": true,
"data": [{
"event_id": "soccer/gb/epl/arsenal_fc_v_leeds_united_fc/2026-10-10",
"sport": "soccer",
"league": "epl",
"country": "gb",
"name": "Arsenal FC v Leeds United FC",
"home": "Arsenal FC",
"away": "Leeds United FC",
"participants": ["Arsenal FC", "Leeds United FC"],
"competitors": [
{"id": "soccer:arsenal_fc", "name": "Arsenal FC", "side": "home",
"logo_url": "https://api.b337.ai/odds/v1/media/logo/2f6693605fb6dcfa.png"},
{"id": "soccer:leeds_united_fc", "name": "Leeds United FC", "side": "away",
"logo_url": "https://api.b337.ai/odds/v1/media/logo/c6d81a00ff2a0516.png"}],
"is_outright": false,
"start_time": "2026-10-10T11:30:00Z",
"status": "prematch",
"books": ["bet365", "betfair", "pinnacle", "sportsbet", "tab"]
}],
"generated_at": "2026-10-05T03:06:34Z"
}
// A finished event (GET /events?league=mlb&date=2026-09-26), trimmed:
{
"count": 18, "truncated": false, "next_cursor": null,
"since": "2026-09-26", "until": "2026-09-26",
"data": [{
"event_id": "baseball/us/mlb/philadelphia_phillies_v_tampa_bay_rays/2026-09-26",
"sport": "baseball", "league": "mlb", "country": "us",
"name": "Philadelphia Phillies v Tampa Bay Rays",
"start_time": "2026-09-26T23:15:00Z", "status": "finished",
"books": ["bet365", "betfair", "fanduel", "pinnacle", "sportsbet", "tab"],
"has_closing": true, "off_at": "2026-09-26T23:15:00Z", "off_source": "scheduled"
}]
}3. Get the odds
Send the event_id back to /odds. These samples chain steps 2 and 3, so they work whichever match is next.
# Take the next EPL match, then ask for its head-to-head prices
EVENT_ID=$(curl -s "https://api.b337.ai/odds/v1/events?league=epl&limit=1" \
-H "X-API-Key: demo" | jq -r '.data[0].event_id')
curl -sG "https://api.b337.ai/odds/v1/odds" \
--data-urlencode "event_id=$EVENT_ID" \
--data-urlencode "markets=head_to_head" \
--data-urlencode "books=sportsbet,tab" \
-H "X-API-Key: demo" --compressedimport requests
BASE = "https://api.b337.ai/odds/v1"
HEADERS = {"X-API-Key": "demo"}
events = requests.get(f"{BASE}/events", params={"league": "epl", "limit": 1},
headers=HEADERS, timeout=10).json()
event_id = events["data"][0]["event_id"]
odds = requests.get(f"{BASE}/odds", headers=HEADERS, timeout=10, params={
"event_id": event_id,
"markets": "head_to_head",
"books": "sportsbet,tab",
}).json()
for market in odds["markets"]:
for sel in market["selections"]:
best = max(sel["prices"].items(), key=lambda kv: kv[1]["odds"])
print(sel["label"], "best:", best[0], best[1]["odds"])// Node 18+, saved as an .mjs file
const BASE = "https://api.b337.ai/odds/v1";
const headers = { "X-API-Key": "demo" };
const events = await (await fetch(`${BASE}/events?league=epl&limit=1`, { headers })).json();
const eventId = events.data[0].event_id;
const url = new URL(`${BASE}/odds`);
url.searchParams.set("event_id", eventId);
url.searchParams.set("markets", "head_to_head");
url.searchParams.set("books", "sportsbet,tab");
const res = await fetch(url, { headers });
console.log(res.headers.get("X-Tokens-Remaining"));
const { event, markets } = await res.json();
console.log(event.name, markets[0].selections.map((s) => s.label));Every selection carries prices keyed by book. Compare them directly: the same selection_key means the same outcome at every book.
4. Stream changes
Polling /odds faster than once a second returns the same body. To follow an event live, subscribe on the WebSocket with your own key instead.
# pip install websockets
import asyncio, json, os
import websockets
URL = "wss://api.b337.ai/odds/v1/ws?api_key=" + os.environ["ODDS_API_KEY"]
EVENT_ID = "soccer/gb/epl/arsenal_fc_v_leeds_united_fc/2026-10-10"
async def main():
async with websockets.connect(URL) as ws:
print(json.loads(await ws.recv())) # hello
await ws.send(json.dumps({
"op": "subscribe",
"event_ids": [EVENT_ID],
"markets": ["head_to_head"],
}))
async for frame in ws:
msg = json.loads(frame)
if msg["type"] == "changes":
for c in msg["changes"]:
print(c["book"], c["selection_key"], c["prev_odds"], "->", c["odds"])
asyncio.run(main())// npm install ws, then run as an .mjs file
import WebSocket from "ws";
const ws = new WebSocket("wss://api.b337.ai/odds/v1/ws", {
headers: { "X-API-Key": process.env.ODDS_API_KEY },
});
ws.on("open", () => {
ws.send(JSON.stringify({
op: "subscribe",
event_ids: ["soccer/gb/epl/arsenal_fc_v_leeds_united_fc/2026-10-10"],
markets: ["head_to_head"],
}));
});
ws.on("message", (raw) => {
const msg = JSON.parse(raw);
if (msg.type === "changes") {
for (const c of msg.changes) console.log(c.book, c.selection_key, c.prev_odds, "->", c.odds);
}
});# websocat: https://github.com/vi/websocat
websocat "wss://api.b337.ai/odds/v1/ws?api_key=$ODDS_API_KEY"
# then type:
{"op": "subscribe", "event_ids": ["soccer/gb/epl/arsenal_fc_v_leeds_united_fc/2026-10-10"], "markets": ["head_to_head"]}