Compare odds across bookmakers

Read one game's prices once and find the best price for each side.

What it does

The program asks for one event's winner market from every bookmaker in your plan. Each outcome in the response lists every bookmaker's price, sorted best first, so the best price for each side is the first one. The program prints that price, the key of the bookmaker offering it and when we saw it.

One request does the work. The event odds route returns every bookmaker's quotes for the event, so there is no need to call once per bookmaker. markets=winner keeps the response to the market you compare, which keeps the objects you use low.

The program

const url = "https://api.sockodds.com/v1/events/ev_000ckk0nc1tg/odds?markets=winner";
const response = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
const { data: event } = await response.json();

// The full match winner market, with each side's prices sorted best first.
const market = event.markets.find((m) => m.key === "winner" && m.period === "full");
const outcomes = new Map((market?.outcomes ?? []).map((outcome) => [outcome.side, outcome]));

console.log(`${event.name}, winner (full match)`);
for (const participant of event.participants) {
  const outcome = outcomes.get(participant.role);
  if (!outcome) {
    console.log(`${participant.identity.name}: no current price`);
    continue;
  }
  const best = outcome.prices[0];
  console.log(
    `${outcome.name}: best ${best.decimal_odds.toFixed(2)} at ${best.bookmaker}, ` +
      `seen ${best.observed_at}, ${outcome.prices.length} bookmakers quoting`,
  );
}
import json, os, urllib.request

url = "https://api.sockodds.com/v1/events/ev_000ckk0nc1tg/odds?markets=winner"
request = urllib.request.Request(
    url, headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]}
)
with urllib.request.urlopen(request) as response:
    event = json.load(response)["data"]

# The full match winner market, with each side's prices sorted best first.
market = next((m for m in event["markets"] if m["key"] == "winner" and m["period"] == "full"), None)
outcomes = {outcome["side"]: outcome for outcome in (market["outcomes"] if market else [])}

print(f"{event['name']}, winner (full match)")
for participant in event["participants"]:
    outcome = outcomes.get(participant["role"])
    if outcome is None:
        print(f"{participant['identity']['name']}: no current price")
        continue
    best = outcome["prices"][0]
    print(
        f"{outcome['name']}: best {best['decimal_odds']:.2f} at {best['bookmaker']}, "
        f"seen {best['observed_at']}, {len(outcome['prices'])} bookmakers quoting"
    )

Output from a local copy of the API with invented test prices

Australia v New Zealand, winner (full match)
New Zealand: best 2.23 at playup, seen 2026-10-15T07:58:00.000Z, 4 bookmakers quoting
Australia: best 1.81 at playup, seen 2026-10-15T07:58:00.000Z, 4 bookmakers quoting

How it works

  • Sides, not names. Each outcome carries a side key (home and away here; competitor_1 and competitor_2 in tennis and UFC). Every bookmaker's quote on a side sits in that one outcome's prices, even when bookmakers spell a team differently. The program prints sides in the order of the event's participants.
  • Best first. prices is sorted by decimal_odds, highest first, so prices[0] is the best price and prices.length is how many bookmakers quote the side. A price names its bookmaker by key; /v1/bookmakers gives each key's display name.
  • One period. markets=winner returns the winner market for every period a bookmaker quotes, such as halves or quarters. The program keeps full only, so a half price never competes with a full match price.
  • Current prices only. A quote older than ten minutes is withheld from current responses, so every price the program sees was observed in the last ten minutes. It prints the price's observed_at so you can show your users when it was seen.
  • No price is not zero. If no bookmaker quotes a side right now, the program says so instead of printing a number.

Make it yours

  • Compare a different market: change markets=winner to a key from the market catalogue. Each handicap or total line is its own market, so read the best price per outcome the same way.
  • Compare across a league: call /v1/odds with league= and loop over data, following links.next for more events.
  • Limit the bookmakers: add bookmakers= with up to 20 keys from the bookmaker list.

The prices above come from a local copy of the API with invented test prices. A best price is the best quote we saw, not an offer: bookmakers change prices often and may limit or refuse a bet.

Need help?FAQ · Email support

Search across the SockOdds documentation and catalogue.