Skip to main content

Esports Odds API Tutorial: Build a Betting App

Esports Odds API Tutorial: Build a Betting App

This is an esports odds API tutorial for developers. It builds the data side of an esports betting app or odds product on Odds-API.io: list matches, pull series and map odds from several bookmakers, stream live scores and status, and settle from the final map results. Coverage, plans and the per-title pages are on the esports odds API product page; this post is the code. Every request and response below was run against the live API on 5 November 2026.

Esports odds API: titles, markets, live and pre-match

Esports is one sport in the API, with the slug esports, and all 15 titles sit under it: CS2, Dota 2, League of Legends, Valorant, Rainbow Six Siege, Rocket League, Mobile Legends, Call of Duty, Overwatch, King of Glory and more. On 5 November 2026 one /events call returned 337 upcoming and 17 live esports matches across 10 titles, led by Counter-Strike and Dota 2.

Each esports event also carries an esports object with the game and the parent league, plus the region and the series format when they are known. A best of three reads {"type": "bestOf", "games": 3}. Group matches by esports.game.slug to build a per-title menu.

Markets

GET /markets?sport=esports lists 70 market names and flags each one as priced pre-match, live or both. These are the ones most products start with:

  • ML: the series winner
  • Spread: the map or game handicap, with the line in hdp
  • Totals: series totals, with the line in hdp
  • 1st Map ML, 2nd Map ML and so on: the winner of a single map
  • 1st Map Handicap, 1st Map Rounds Handicap and 1st Map Total Rounds: map and round lines for CS2 and Valorant
  • 1st Map Total Kills and 1st Map First Blood: kill markets for MOBA titles such as LoL and Dota 2
  • Correct Score: the exact series score

Coverage differs by book. In the 5 November capture GG.bet priced pistol rounds and first to 5, 10, 15 and 20 kills, which Bet365 did not carry. Market names are case-insensitive, but they must be the full name. Our guide to esports betting markets explains what each one means.

Live and pre-match

Pre-match and live odds share the event id and the market names. An event moves from pending to live to settled, or to cancelled, and /events takes status=live or status=pending to split the two. On the WebSocket, status=live or status=prematch does the same for the stream. In the 5 November capture Bet365 priced a live CS2 match with in-play map markets such as 2nd Map ML and 2nd Map Rounds Handicap.

A request and its response

Odds are fetched per event. This is Dplus KIA v Bilibili Gaming at the League of Legends Worlds, a best of five, from two books and three markets:

curl --compressed -G "https://api.odds-api.io/v3/odds" \
  --data-urlencode "apiKey=YOUR_KEY" \
  --data-urlencode "eventId=8056202770" \
  --data-urlencode "bookmakers=Bet365,GG.bet" \
  --data-urlencode "markets=ML,Spread,1st Map ML"
{
  "id": 8056202770,
  "home": "Dplus KIA",
  "away": "Bilibili Gaming",
  "date": "2026-11-05T22:00:00Z",
  "status": "pending",
  "league": {"name": "League of Legends - Worlds", "slug": "league-of-legends-worlds"},
  "bookmakers": {
    "Bet365": [
      {"name": "ML", "updatedAt": "2026-11-05T17:03:44.046Z",
       "odds": [{"home": "2.37", "away": "1.53"}]},
      {"name": "Spread", "updatedAt": "2026-11-01T05:10:07.282Z",
       "odds": [{"hdp": 1.5, "home": "1.61", "away": "2.20"}]},
      {"name": "1st Map ML", "updatedAt": "2026-11-01T05:10:07.282Z",
       "odds": [{"home": "2.10", "away": "1.66"}]}
    ],
    "GG.bet": [
      {"name": "ML", "updatedAt": "2026-11-05T18:47:17.079Z",
       "odds": [{"home": "2.17", "away": "1.66"}]},
      {"name": "Spread", "updatedAt": "2026-11-05T18:08:59.21Z",
       "odds": [{"hdp": 1.5, "home": "1.56", "away": "2.38"}]},
      {"name": "1st Map ML", "updatedAt": "2026-11-05T18:47:17.079Z",
       "odds": [{"home": "2.02", "away": "1.76"}]}
    ]
  }
}

The response is shortened. GG.bet's Spread ladder ran from -2.5 to +2.5, and the urls object, which holds each book's event link, is left out. Odds are decimal strings, so parse them before you compare, and each market carries its own updatedAt.

1. Pick your titles

Choose the titles your users follow, then filter or group events by esports.game.slug. Every title uses the same sport slug and the same response shapes, so you can launch with a few and add more without changing your integration. The 15 biggest esports for betting covers the tournaments behind each one.

2. List upcoming matches

Every esports event lives under the esports sport slug. List upcoming matches with status=pending, or pass a league slug for one competition:

curl --compressed "https://api.odds-api.io/v3/events?sport=esports&status=pending&apiKey=YOUR_KEY"

# Upcoming League of Legends Worlds matches
curl --compressed "https://api.odds-api.io/v3/events?sport=esports&league=league-of-legends-worlds&status=pending&apiKey=YOUR_KEY"

Each event returns an id, the two competitors (home and away), the league, the status and the esports object. Store the id, because you use it to fetch odds and to match live updates. Without a league, the response covers the next 14 days; pass an explicit to parameter to widen the window. With a league, there is no default window, so the status filter keeps the list to upcoming matches.

3. Fetch odds across bookmakers

The request above fetches one event. To refresh a list of matches, /odds/multi takes up to 10 event ids and counts as one request. These are the next three Worlds matches:

curl --compressed -G "https://api.odds-api.io/v3/odds/multi" \
  --data-urlencode "apiKey=YOUR_KEY" \
  --data-urlencode "eventIds=8056202770,5148164750,6924634596" \
  --data-urlencode "bookmakers=Bet365,GG.bet" \
  --data-urlencode "markets=ML,Spread,Totals,1st Map ML,2nd Map ML"

For esports you will want more than the series winner. Map handicaps, total maps, correct score, per-map winners, first blood and round handicaps carry most of the interest. Bookmaker names must match /bookmakers exactly, and you can pass up to 30 in one call.

The same payload builds an odds comparison table: one row per market, one column per bookmaker. Our odds comparison API guide shows the pattern.

4. Stream live scores and status over WebSocket

The feature that makes a betting app feel live is real-time scores. Subscribe to the odds, scores and status channels on one WebSocket connection. The markets parameter is required whenever the odds channel is active. This is backend Node.js code with the ws package, so your key stays on the server:

const WebSocket = require("ws");

const params = new URLSearchParams({
  apiKey: process.env.ODDS_API_KEY,
  sport: "esports",
  markets: "ML",
  channels: "odds,scores,status",
});
const ws = new WebSocket(`wss://api.odds-api.io/v3/ws?${params}`);

ws.on("message", (raw) => {
  const msg = JSON.parse(raw);
  if (msg.type === "score") updateScoreboard(msg.id, msg.scores);
  if (msg.type === "status") handleStatusChange(msg.id, msg.status, msg.scores);
});

channels is an allowlist, so you only receive what you list. Keep odds in the list to keep receiving odds. Leave channels out and you get the default odds-only stream.

Scores update in roughly one second, and the status channel tells you the moment a match is added, goes live, settles (with the final score) or is cancelled. Those are the events you need to open and close markets in your UI. Live esports scores over WebSocket walks through every message type, and the WebSocket guide covers filters, reconnects and replay.

5. Handle map and period data correctly

Esports scores are reported per map inside the periods object:

  • map1, map2 and so on give the winner of each map: 1 for the side that won it, 0 for the other
  • map1_score, map2_score and so on give the score inside a map, rounds in CS2 and kills in League of Legends and Dota 2. They are added after a series settles, and only for maps with a verified score
  • scores.home and scores.away are maps won, so a 2-1 series reads home 2, away 1

Take map winners from the mapN keys and the series winner from scores.home and scores.away. Round and kill markets need the mapN_score keys, so check they are present before you grade them. Which keys appear depends on the title and on how the match went, so treat every key as optional.

6. Settle and store

A status message with status settled includes the scores, so it can trigger your result processing. Score and status messages carry no seq. After a reconnect with lastSeq, the server resends the current status of live events and of events settled or cancelled in the last 12 hours, but score changes are not replayed, so reconcile through REST as well: settled events stay queryable via /v3/events?sport=esports&status=settled, which suits reconciliation jobs and backfilling anything you missed while disconnected.

7. Backtest with historical data

Settled results are half the story. /historical/events returns past matches for a league and a date range of up to 31 days, and /historical/odds returns the odds those matches closed at. That is what you need to backtest a model or to check your settlement logic against past results.

# Past League of Legends Worlds matches for a date range (31 days at most)
curl --compressed "https://api.odds-api.io/v3/historical/events?sport=esports&league=league-of-legends-worlds&from=2026-10-05T00:00:00Z&to=2026-11-04T00:00:00Z&apiKey=YOUR_KEY"

# The odds for one of those matches
curl --compressed "https://api.odds-api.io/v3/historical/odds?eventId=EVENT_ID&bookmakers=Bet365,GG.bet&apiKey=YOUR_KEY"

Historical odds come back in the same event shape as /odds, so code that reads /odds responses works unchanged. Historical events have the core event fields but no esports object.

Ship it

That is the whole loop: list events, fetch odds, stream scores and status, settle. For coverage and plans, go to the esports odds API page, and see pricing for your currency.

Related reading: esports betting markets explained, live esports scores over WebSocket and the 15 biggest esports for betting.

Get live and pre-match odds from 450+ bookmakers

OddsNotifier and OddsHub run on the same real-time feed you get with the API.

See what's built with Odds API →