18+ Only  |  Gambling can be addictive — please gamble responsibly  |  Gambling Help: 1800 858 858  |  GambleAware

Build a racing "next to go" dashboard in Python (free Australian racing API)

Australian racing runs almost non-stop — thoroughbreds, greyhounds and harness across dozens of meetings a day. The core view every racing product needs is "next to go": the upcoming races in jump-time order, with runners and live prices. In this tutorial we'll build exactly that in Python using the free PuntersEdge AU Odds API.

⚠️ 18+ only. This is racing *data* tooling, not tipping or a betting service. Racing moves fast — confirm prices live before acting. Not financial advice. Gambling Help: 1800 858 858.

Get a free key

Free key at puntersedge.online/api-platform (1,500 credits/month, no card), passed in X-API-Key.

import requests
from datetime import datetime, timezone

BASE = "https://api.puntersedge.online/v1"
HEADERS = {"X-API-Key": "YOUR_FREE_KEY"}

Step 1 — pull the next races

/v1/racing/next-to-go returns upcoming races (horse + greyhound) in start-time order, each with its runners and per-bookmaker win/place prices:

def next_to_go():
    r = requests.get(f"{BASE}/racing/next-to-go", headers=HEADERS, timeout=15)
    r.raise_for_status()
    return r.json()

for race in next_to_go()[:5]:
    jump = datetime.fromisoformat(race["start_time"].replace("Z", "+00:00"))
    mins = (jump - datetime.now(timezone.utc)).total_seconds() / 60
    print(f"{race['venue']} R{race['race_number']} ({race['category']}) "
          f"— jumps in {mins:.0f} min, {len(race['runners'])} runners")

Step 2 — best win price per runner

Each runner carries a bookmakers list; collapse it to the best available win price:

def best_win(runner):
    prices = [b["win_price"] for b in runner["bookmakers"] if b.get("win_price")]
    if not prices:
        return None, None
    best = max(prices)
    book = next(b["key"] for b in runner["bookmakers"] if b.get("win_price") == best)
    return best, book

race = next_to_go()[0]
print(f"{race['venue']} R{race['race_number']}")
for r in sorted(race["runners"], key=lambda x: (best_win(x)[0] or 9e9)):
    price, book = best_win(r)
    if price:
        print(f"  {r['number']:>2}. {r['name']:<22} {price:>5} @ {book}")

Sorting by price gives you the market order — favourites first — which is exactly how a next-to-go board reads.

Step 3 — a refreshing dashboard loop

Wrap it in a loop that reprints the next few jumps every 30 seconds:

import time

def board(n=4):
    for race in next_to_go()[:n]:
        jump = datetime.fromisoformat(race["start_time"].replace("Z", "+00:00"))
        mins = (jump - datetime.now(timezone.utc)).total_seconds() / 60
        fav = min(race["runners"], key=lambda x: best_win(x)[0] or 9e9)
        fp, fb = best_win(fav)
        print(f"{mins:4.0f}m  {race['venue']:<16} R{race['race_number']:<2} "
              f"{race['category']:<9} fav: {fav['name']} {fp} ({fb})")

while True:          # Ctrl-C to stop
    print("\n=== NEXT TO GO ===")
    board()
    time.sleep(30)

That's a live racing board: venue, race number, code, time to jump and the favourite's best price — refreshing on its own.

Wrap up

From here you can split horse vs greyhound (category), show place prices, add a per-runner price-comparison row, or push a notification when a runner's best price crosses a threshold.

(18+, gamble responsibly, Gambling Help 1800 858 858.)

Get a free API key →

This site contains wagering-related analysis and is intended for Australian users aged 18+. Gambling involves risk. Please gamble responsibly.