Skip to main content
Sportsbook API Integration: A Practical Guide
Back to Blog

Sportsbook API Integration: A Practical Guide

James Whitfield

James Whitfield

•10 min read

To integrate a sports betting API into a sportsbook, you sync fixtures from the API into your own event catalogue, load a REST odds snapshot for each event, then keep those prices current over a WebSocket stream. You map the API's events, markets and lines onto your own IDs, store the latest prices in your backend, and serve your frontend from that store. Bets are graded from the match status and scores feed. The odds API is one input. Bet placement, player accounts, payments and risk sit in your platform, not in the feed.

This guide walks through that architecture with the Odds-API.io v3 API. It is written for operators, startups and agencies building a sportsbook, a betting app, a tipster service or an odds comparison product. The responses shown are real captures from 28 September 2026.

What does an odds API cover, and what does it not?

An odds API is a data feed. It tells you which matches exist, what bookmakers are pricing them at, and how they finished. It does not run a sportsbook. Knowing where that line sits saves weeks of vendor calls.

ComponentOdds-API.ioWho provides it
Sports, leagues, fixtures, participantsYes: /sports, /leagues, /events, /participantsFeed, mapped into your catalogue
Pre-match and live oddsYes: 365+ bookmakers over REST and WebSocketFeed; you choose books and markets
Reference price for your own linesYes: ON Sharp consensus feedFeed; your traders set margin and limits
Live scores and match statusYes: /events plus scores and status channelsFeed; your engine grades bets
Historical odds and closing linesYes: /historical endpointsFeed; used for audits and models
Bet placement and bet slipNoYour betting engine or platform provider
Player accounts and wallet (PAM)NoPAM provider
KYC, AML, responsible gambling toolsNoCompliance vendors
Payments and payoutsNoPayment provider
Risk, liability and tradingNoYour trading team or risk tooling
Betting frontendNoYour website and apps
Gambling licenceNoYou, in each market you serve

For a tipster, affiliate or comparison site, the top half of the table is often the whole product. For a licensed operator, the feed is the pricing and results layer inside a larger stack.

What does the integration architecture look like?

Most betting products end up with the same five stages. Each one maps to a small set of endpoints.

  • Fixture sync. Pull /sports and /leagues once a day, then /events per sport on a schedule. Store the API event id next to your own.
  • Odds ingestion. Load a snapshot with /odds or /odds/multi, then subscribe to the WebSocket for changes.
  • Mapping. Translate API events, market names and lines into your own catalogue.
  • Storage and serving. Keep the latest market set per event and bookmaker in your own store and serve your frontend from it. Never call the API from a browser or app, where the key is exposed.
  • Settlement. Grade from the status and scores channels, checked against /events and the /historical endpoints.

Fixture sync starts with /events. The status filter takes pending, live, settled or cancelled, alone or as a comma-separated list.

curl --compressed -G "https://api.odds-api.io/v3/events" \
  --data-urlencode "apiKey=YOUR_KEY" \
  --data-urlencode "sport=football" \
  --data-urlencode "league=england-premier-league" \
  --data-urlencode "status=pending"
[
  {
    "id": 72221292,
    "home": "Arsenal FC",
    "away": "Leeds United",
    "homeId": 42,
    "awayId": 34,
    "date": "2026-10-10T11:30:00Z",
    "sport": {"name": "Football", "slug": "football"},
    "league": {"name": "England - Premier League", "slug": "england-premier-league"},
    "status": "pending",
    "scores": {"home": 0, "away": 0}
  }
]

That is a real response captured on 28 September 2026, trimmed to one fixture. The id is the key you carry through every later call.

Next, load prices. /odds/multi takes up to 10 event ids and counts as one request. The bookmakers parameter takes up to 30 names, spelled exactly as /bookmakers returns them.

curl --compressed -G "https://api.odds-api.io/v3/odds/multi" \
  --data-urlencode "apiKey=YOUR_KEY" \
  --data-urlencode "eventIds=72221292,72221294" \
  --data-urlencode "bookmakers=ON Sharp,Bet365,Betfair Sportsbook,DraftKings"
[
  {
    "id": 72221292,
    "home": "Arsenal FC",
    "away": "Leeds United",
    "date": "2026-10-10T11:30:00Z",
    "status": "pending",
    "bookmakers": {
      "ON Sharp": [
        {"name": "ML", "odds": [{"home": "1.375", "draw": "5.392", "away": "8.907"}]},
        {"name": "Totals", "odds": [{"hdp": 2.5, "over": "1.800", "under": "2.153"}]}
      ],
      "Bet365": [
        {"name": "ML", "odds": [{"home": "1.380", "draw": "4.500", "away": "7.500"}]},
        {"name": "Totals", "odds": [{"hdp": 2.5, "over": "1.725", "under": "2.075"}]}
      ],
      "Betfair Sportsbook": [
        {"name": "ML", "odds": [{"home": "1.33", "draw": "4.75", "away": "8.5"}]},
        {"name": "Totals", "odds": [{"hdp": 2.5, "over": "1.65", "under": "2.1"}]}
      ],
      "DraftKings": [
        {"name": "ML", "odds": [{"home": "1.37", "draw": "4.80", "away": "7.50"}]}
      ]
    }
  }
]

Captured 28 September 2026 at 07:28 UTC, trimmed to one of the two events and two markets. DraftKings had no Totals 2.5 line at the time, so it only shows ML.

Mapping is where most integration bugs live. Market names are ML, Spread and Totals, plus period and prop variants listed by /markets. Spread and Totals carry the line in hdp, so group by market and hdp before you store or compare anything. Quarter lines such as -1.25 are real lines. The odds comparison guide covers grouping in more detail.

Then open the stream. The WebSocket at wss://api.odds-api.io/v3/ws takes everything as query parameters. markets is required for the odds channel. channels is an allowlist of odds, scores and status, so if you list scores and status without odds, you receive no odds. It streams the bookmakers selected on your account.

const params = new URLSearchParams({
  apiKey: process.env.ODDS_API_KEY,
  markets: 'ML,Spread,Totals',
  leagues: 'england-premier-league',
  channels: 'odds,scores,status',
});
if (store.lastSeq) params.set('lastSeq', store.lastSeq);

const ws = new WebSocket(`wss://api.odds-api.io/v3/ws?${params}`);
ws.onmessage = ({ data }) => {
  const msg = JSON.parse(data);
  if (msg.type === 'resync_required') return rebuildFromRest();
  if (msg.type === 'created' || msg.type === 'updated') store.replaceMarkets(msg.id, msg.bookie, msg.markets);
  if (msg.type === 'deleted' || msg.type === 'no_markets') store.clear(msg.id, msg.bookie);
  if (msg.type === 'status') store.setStatus(msg.id, msg.status, msg.scores);
  if (msg.seq) store.lastSeq = msg.seq;
};

An odds update has this shape:

{
  "type": "updated",
  "seq": 482917,
  "id": "72221292",
  "bookie": "Bet365",
  "markets": [
    {"name": "ML", "odds": [{"home": "1.38", "draw": "4.50", "away": "7.50"}]},
    {"name": "Totals", "odds": [{"hdp": 2.5, "over": "1.72", "under": "2.07"}]}
  ]
}

Two rules keep your state correct. First, replace the stored market set on every update. Do not merge. Suspended markets are dropped from the payload, so their absence is the signal. Second, persist seq and reconnect with lastSeq to replay what you missed. On resync_required, rebuild from REST with includeSeq=true and reconnect from the X-OddsAPI-Seq response header. The WebSocket guide has the full message reference.

Should a sportsbook use REST or WebSocket?

Use both. REST gives you a complete snapshot and a recovery path. The WebSocket gives you changes as they happen without spending requests. The split comes down to request math.

Paid plans include 5,000 requests per hour, about 83 a minute. Add-on packages add 10K, 20K or 30K requests per hour. /odds/multi returns up to 10 events per request.

  • A pre-match board of 300 fixtures refreshed every 60 seconds: 30 requests a minute, 1,800 an hour. That fits a paid plan.
  • The same board every 20 seconds: 5,400 an hour. That is over the base limit, so you need an add-on or a longer interval.
  • 40 live matches polled every 5 seconds: 4 requests per poll, 2,880 an hour, before any pre-match traffic.

Live is where polling stops working. A 5-second poll can still show a price that is 5 seconds old, and it burns the budget fast. The WebSocket pushes each change once, so you stop spending requests re-fetching prices that have not moved. It is an add-on priced at 2x the REST plan. Keep REST for the first snapshot, for recovery after resync_required, and for cold paths like fixture sync. The live odds API guide covers in-play handling.

How do you price your own lines from an odds feed?

An operator that sets its own prices needs a reference for where the market is. ON Sharp is our real-time sharp consensus feed. It is a synthetic bookmaker that blends de-vigged prices from a panel of sharp, high-limit books and republishes them at a fixed margin: 2% on two-way markets and 2.5% on football three-way ML. It is a reference price, not a book anyone can bet at. It covers pre-match only, lines are removed at kickoff, and it is available on paid plans.

In the capture above, ON Sharp priced Arsenal v Leeds at 1.375, 5.392 and 8.907. The retail books sat between 1.33 and 1.38 on Arsenal and between 7.50 and 8.5 on Leeds. The gap between ON Sharp and the books shows how much room there is for your own margin and where the market is loose.

ON_SHARP_3WAY_MARGIN = 1.025

def fair_probs(odds, book_margin=ON_SHARP_3WAY_MARGIN):
    return {k: (1 / float(v)) / book_margin for k, v in odds.items()}

def price(probs, margin=0.06):
    return {k: round(1 / (p * (1 + margin)), 2) for k, p in probs.items()}

on_sharp = {"home": "1.375", "draw": "5.392", "away": "8.907"}
print(price(fair_probs(on_sharp)))
# {'home': 1.33, 'draw': 5.21, 'away': 8.61}

This spreads a 6% margin proportionally, the simplest method. Your 1.33 on Arsenal would match the shortest retail price in the capture, so check your output against the books in /odds before you publish. Many operators weight margin towards longshots, cap prices against the best retail price, or skew by liability. That logic lives in your trading layer. The feed supplies the inputs: the reference price, each book's price, and the price history in /odds/movements.

How do you settle and grade bets?

Settlement needs a final result you trust and a clear rule for when to act on it.

  • The status channel sends pending, live, settled and cancelled transitions. The settled message carries the final scores, including period scores such as the first half.
  • The scores channel sends live score changes for in-play displays.
  • Score and status messages carry no seq and are not replayed. After a reconnect, read the current state from /events or /events/{id}.
  • /historical/events lists settled events and /historical/odds returns the odds for a past event. /historical/closing-lines returns closing odds for up to 10 leagues per request on paid plans.

Grade on the settled status, not on the clock. Void rules for cancelled matches come from your own terms; the feed reports the state. Keep a manual review queue for anything that changes after you settle. The historical endpoints double as an audit trail and a data set for closing line value and model training.

How do you evaluate an odds API provider?

Whichever vendor you pick, check the same things. Ask for evidence, not a feature list.

  • Coverage. Count the bookmakers, sports and leagues that matter to your market, not the headline total. Pull fixtures for your top leagues and check which books price them.
  • Latency. Measure the time from a price change to your system, pre-match and live, on your own connection. Ask whether live data arrives over a push stream.
  • Markets. Check the markets you will actually offer, including lines, periods and props, and how lines are represented.
  • Recovery. Ask what happens after a disconnect: replay, sequence numbers, a documented resync path.
  • Uptime transparency. Ask for incident history and how outages are communicated. A marketing number is not a commitment, so read the terms.
  • Licensing and terms. Confirm the licence covers commercial use in your product and display to your users.
  • Pricing model. Compare per-request, per-bookmaker and flat pricing against your real request math, live included.
  • Settlement data. Check that final and period scores arrive with a clear status.

Odds-API.io covers 365+ bookmakers across 34 sports and 12,000+ leagues. The full list is on the sportsbooks page, and the Bet365 API guide shows what one major book looks like in the feed.

Getting started

  • Choose a plan that covers the bookmakers you need, and select them on your account. The WebSocket streams that selection.
  • Sync fixtures for two or three leagues with /events.
  • Load snapshots with /odds/multi and store them by event, bookmaker, market and hdp.
  • Open one WebSocket with odds, scores and status, and persist seq.
  • Grade a week of settled matches against /historical/events before you go live.

For enterprise volume, custom bookmaker sets or commercial terms, email hello@odds-api.io.

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

See what's built with Odds API →