Quickstart
-
Get an API key. Keys start with
opa_and are issued to your team. During the private preview we send you one; later you will create them in the Dashboard. -
Install the Centrifugo client for your language.
pip install centrifuge-pythonnpm install centrifuge -
Connect, load the current catalogue, then follow deltas.
You never subscribe to a channel yourself. The server subscribes you to the channels your key is entitled to (odds:v2:snapshot and odds:v2:delta) the moment you connect, so publications arrive on the client-level publication handler. See Channels.
# pip install centrifuge-pythonimport asynciofrom centrifuge import Client, ClientEventHandler
WS_URL = "wss://api.oddsfather.com.au/connection/websocket" # set at build time from PUBLIC_WS_URLAPI_KEY = "opa_..."SNAPSHOT, DELTA = "odds:v2:snapshot", "odds:v2:delta"
races: dict[str, dict] = {} # race key -> latest race object
def apply(payload: dict) -> None: for race in payload.get("events", []): races[race["key"]] = race
class Events(ClientEventHandler): async def on_connected(self, ctx): print("connected", ctx.client)
async def on_disconnected(self, ctx): print("disconnected", ctx.code, ctx.reason)
async def on_subscribed(self, ctx): # server-side subscription granted print("granted", ctx.channel)
async def on_publication(self, ctx): # every snapshot and delta lands here apply(ctx.pub.data)
async def main(): client = Client( WS_URL, events=Events(), headers={"Authorization": f"Bearer {API_KEY}"}, data={"key": API_KEY}, # same key in the connect payload also works ) await client.connect()
# Be correct immediately: fetch the full catalogue as it stands now, # falling back to the last snapshot kept in channel history. try: apply((await client.rpc("snapshot", {})).data) except Exception: hist = await client.history(SNAPSHOT, limit=1) if hist.publications: apply(hist.publications[0].data) print(len(races), "races loaded")
await asyncio.Event().wait() # keep running; deltas keep arriving
asyncio.run(main())// npm install centrifuge ws (Node 22+ has a global WebSocket; ws is for older Node)import { Centrifuge } from 'centrifuge';
const WS_URL = 'wss://api.oddsfather.com.au/connection/websocket'; // set at build time from PUBLIC_WS_URLconst API_KEY = process.env.ODDS_API_KEY; // opa_...const races = new Map();const apply = (payload) => { for (const race of payload.events ?? []) races.set(race.key, race); };
const client = new Centrifuge(WS_URL, { headers: { Authorization: `Bearer ${API_KEY}` }, // sent as emulated headers data: { key: API_KEY },});
client.on('connected', async (ctx) => { console.log('connected', ctx.client); try { apply((await client.rpc('snapshot', {})).data); } catch { const hist = await client.history('odds:v2:snapshot', { limit: 1 }); if (hist.publications.length) apply(hist.publications[0].data); } console.log(races.size, 'races loaded');});client.on('subscribed', (ctx) => console.log('granted', ctx.channel));client.on('publication', (ctx) => apply(ctx.data)); // server-side subscriptionsclient.on('disconnected', (ctx) => console.log('disconnected', ctx.code, ctx.reason));
client.connect();<script type="module"> import { Centrifuge } from 'https://esm.sh/centrifuge@5';
// Never put an opa_ key in a web page. Ask YOUR backend for a short-lived // token (see Authentication), then pass it as the connect payload. async function getToken() { const r = await fetch('/your-backend/odds-token', { method: 'POST' }); return (await r.json()).token; // opt_... }
const client = new Centrifuge('wss://api.oddsfather.com.au/connection/websocket', { data: { key: await getToken() }, }); client.on('publication', (ctx) => { for (const race of ctx.data.events ?? []) console.log(race.key); }); client.connect();</script>Browsers cannot set headers on a websocket handshake, so the credential travels in the connect payload’s data.key.
Check it worked
Section titled “Check it worked”You should see granted odds:v2:snapshot, granted odds:v2:delta, a snapshot with several hundred races, then a steady stream of single-race deltas. If the connection closes straight away with code 4501, your key was refused; see Errors.
Odds compares prices only. It is not a bookmaker, takes no bets and does not tell you what to back. Prices come from licensed Australian bookmakers' public sites and can change at any moment; always check the price with the bookmaker before you bet.
18+ · Chances are you're about to lose. For free and confidential support call 1800 858 858 or visit gamblinghelponline.org.au