Oddschecker API: Get Odds Comparison Data as JSON (Free Alternative)
Looking for an Oddschecker API? There Isn’t One.
If you have ever tried to build something on top of Oddschecker the odds-comparison grid, the alerts, the “best price” highlight you have hit the same wall every developer hits: there is no public Oddschecker API. The site is an affiliate business, not a data business. The comparison table renders in the browser, the markup is obfuscated, and scraping it gets your IP blocked fast. There is no JSON endpoint, no API key, no docs.
So how do you get the actual data the price for every outcome, across every bookmaker, in a format you can parse? You point at an aggregator that was built API-first. This guide shows you how to pull the exact same multi-bookmaker comparison Oddschecker shows you, as clean JSON, from OddsPapi covering 350+ bookmakers (Oddschecker tracks roughly 30 UK books), with free historical odds and a real-time WebSocket feed on top.
Why Scraping Oddschecker Is a Dead End
Oddschecker is great for a human eyeballing the grid. It is hostile to a program:
- No API. There is no documented endpoint, no key, no rate-limit contract. Anything you build is reverse-engineering a private frontend.
- Anti-bot by design. Cloudflare, rotating class names, and JS-rendered prices mean a naive
requests.get()returns nothing useful. You end up running a headless browser farm to read a table. - UK-centric, ~30 books. The comparison is mostly UK high-street books. No sharps (Pinnacle, SBOBet), no exchanges, no crypto books, no US sportsbooks in any depth.
- No history. You see the price now. You cannot pull where the line opened or how it moved which is exactly what you need to backtest or measure closing line value.
An aggregator API solves all four. You get one authenticated HTTP call, a documented JSON shape, hundreds of books including the sharp ones, and free price history.
| Scraping Oddschecker | OddsPapi API | |
|---|---|---|
| Access method | Reverse-engineer a private frontend | Documented REST + WebSocket |
| Bookmakers | ~30 (UK retail) | 350+ (incl. Pinnacle, SBOBet, exchanges, crypto) |
| Output format | HTML you have to parse | Clean JSON |
| Sharp books | No | Yes (Pinnacle, SBOBet, Singbet) |
| Historical odds | No | Yes, free tier |
| Real-time push | Polling a webpage | WebSocket feed |
| Blocked at scale? | Yes (Cloudflare, IP bans) | No (API key) |
What the Comparison Data Actually Looks Like
Here is a live example. We pulled the Full Time Result market for England vs Ghana at the 2026 World Cup across every bookmaker OddsPapi had on the fixture (18 books, 15 with an active 1X2 price). This is the same grid Oddschecker would show you only you get it as JSON.
| Outcome | Worst price | Best price | Best book | Pinnacle (sharp) | Payout swing |
|---|---|---|---|---|---|
| England | 1.19 | 1.339 | fourwinds | 1.232 | +12.5% |
| Draw | 4.80 | 7.10 | bet365 | 6.58 | +47.9% |
| Ghana | 8.50 | 15.00 | betparx | 13.61 | +76.5% |
Look at the Ghana line: 8.50 at the worst book, 15.00 at the best. Same bet, same outcome, a 76% difference in what you get paid. That spread is the entire reason odds comparison exists and it is invisible if you are locked to a single sportsbook or stuck scraping a grid you cannot reliably parse. Pinnacle’s no-vig fair line on this fixture: England 78.3%, Draw 14.7%, Ghana 7.1%, at a 3.71% margin.
Tutorial: Build Your Own Odds Comparison in Python
Four steps: authenticate, find the fixture, pull every book’s price, build the comparison table. All code below is tested against the live API.
Step 1: Authenticate
OddsPapi uses an API key as a query parameter not a header. Grab a free key and confirm it works:
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.oddspapi.io/v4"
# apiKey is a query param, NOT a header
r = requests.get(f"{BASE_URL}/sports", params={"apiKey": API_KEY})
print(r.status_code) # 200
Step 2: Find a Fixture
Soccer is sportId=10. Pull fixtures in a date window (max 10 days apart) and keep the ones with hasOdds=true only those return a price payload.
params = {
"apiKey": API_KEY,
"sportId": 10,
"from": "2026-06-23",
"to": "2026-06-24",
}
fixtures = requests.get(f"{BASE_URL}/fixtures", params=params).json()
for f in fixtures:
if f.get("hasOdds") and f["tournamentName"] == "World Cup":
print(f["fixtureId"], f["participant1Name"], "vs", f["participant2Name"])
# id1000001666457046 England vs Ghana
Step 3: Pull Every Bookmaker’s Price
The /odds endpoint returns one fixture across all available books. The response is deeply nested the top key is bookmakerOdds, then markets (by market ID), then outcomes, then players["0"] for the current price. For soccer, market 101 is Full Time Result with outcomes 101 (Home), 102 (Draw), 103 (Away).
FIXTURE_ID = "id1000001666457046" # England vs Ghana
data = requests.get(f"{BASE_URL}/odds",
params={"apiKey": API_KEY, "fixtureId": FIXTURE_ID}).json()
OUTCOMES = {"101": "Home", "102": "Draw", "103": "Away"}
grid = {label: {} for label in OUTCOMES.values()}
for slug, book in data["bookmakerOdds"].items():
market = book.get("markets", {}).get("101") # Full Time Result
if not market:
continue
for oid, outcome in market["outcomes"].items():
player = outcome.get("players", {}).get("0")
# always filter on active price
if player and player.get("active") and player.get("price"):
grid[OUTCOMES[oid]][slug] = player["price"]
print(f"{len(data['bookmakerOdds'])} books on this fixture")
Always filter on active. A book can leave a suspended price in the payload with active=false. If you do not filter, a stale price will win your “best odds” comparison and send you to a market that no longer exists.
Step 4: Build the Comparison Table
Now find the best price per outcome the part Oddschecker charges affiliates to send traffic for:
for outcome, prices in grid.items():
if not prices:
continue
best_book = max(prices, key=prices.get)
worst = min(prices.values())
best = prices[best_book]
swing = (best / worst - 1) * 100
print(f"{outcome:6} best {best:>6} ({best_book:12}) "
f"worst {worst:>6} swing +{swing:.1f}%")
# Home best 1.339 (fourwinds ) worst 1.19 swing +12.5%
# Draw best 7.1 (bet365 ) worst 4.8 swing +47.9%
# Away best 15.0 (betparx ) worst 8.5 swing +76.5%
That is the whole product. Twenty lines of Python and you have programmatic odds comparison across hundreds of books no headless browser, no Cloudflare fight, no parsing obfuscated HTML.
Bonus: The Sharp Benchmark Oddschecker Never Shows
Because OddsPapi carries Pinnacle and SBOBet sharp books that move the market first you can de-vig the sharp line and measure whether any soft book is actually offering value, not just a higher number:
pin = {OUTCOMES[o]: grid[OUTCOMES[o]].get("pinnacle")
for o in OUTCOMES}
if all(pin.values()):
overround = sum(1 / p for p in pin.values())
vig = (overround - 1) * 100
print(f"Pinnacle margin: {vig:.2f}%")
for outcome, price in pin.items():
fair_prob = (1 / price) / overround
print(f" {outcome:6} fair {fair_prob*100:5.1f}% "
f"fair odds {1/fair_prob:.3f}")
# Pinnacle margin: 3.71%
# Home fair 78.3% fair odds 1.277
# Draw fair 14.7% fair odds 6.821
# Away fair 7.1% fair odds 14.123
Oddschecker can tell you bet365 has 7.1 on the draw. It cannot tell you Pinnacle’s fair line is 6.82 so the 7.1 is a genuine +EV price, not just a marketing number. That is the difference between an affiliate grid and a real data feed.
Free Historical Odds (Another Thing Oddschecker Can’t Give You)
Need to see how a line opened and moved? The /historical-odds endpoint returns the full price history per outcome free on the developer tier. The shape differs from live odds: the top key is bookmakers (not bookmakerOdds) and players["0"] is a list of snapshots, max 3 books per call.
hist = requests.get(f"{BASE_URL}/historical-odds", params={
"apiKey": API_KEY,
"fixtureId": FIXTURE_ID,
"bookmakers": "pinnacle,bet365,sbobet", # max 3
}).json()
book = hist["bookmakers"]["pinnacle"]["markets"]["101"]
for snap in book["outcomes"]["101"]["players"]["0"]:
print(snap["createdAt"], snap["price"])
That is what powers closing-line-value analysis and model backtesting and it is the data Oddschecker’s affiliate model has no reason to ever expose.
When to Use Which
If you are a bettor who wants to eyeball today’s best price, Oddschecker (or a player-prop screener like jedibets for props with Discord alerts) is fine. If you are building anything an alert bot, a value scanner, a dashboard, a model you need a structured feed, not a webpage. That is where an API-first aggregator wins.
FAQ
Does Oddschecker have a public API?
No. Oddschecker does not offer a public, documented API or developer key. It is an affiliate odds-comparison site, and its data is only available through the website. To get comparison data programmatically you need an aggregator like OddsPapi that exposes a REST and WebSocket feed.
Can I legally scrape Oddschecker?
Scraping Oddschecker violates its terms of service, and the site uses anti-bot protection (Cloudflare, JS-rendered prices, rotating markup) that blocks automated access. A licensed odds API is the reliable, terms-compliant way to get the same data.
How many bookmakers does OddsPapi compare versus Oddschecker?
OddsPapi aggregates 350+ bookmakers including sharp books (Pinnacle, SBOBet), exchanges, and crypto sportsbooks. Oddschecker tracks roughly 30, mostly UK high-street books, with no sharp or exchange depth.
Is the odds comparison data free?
Yes. OddsPapi has a free developer tier that includes live odds across hundreds of books and free historical odds for backtesting. The API key is passed as a query parameter, not a header.
What format is the odds data returned in?
Clean JSON. Each price is pre-converted to decimal, American, and fractional formats, so you do not need to write your own odds converter.
Stop Scraping. Get the Feed.
You do not need a headless browser farm to read an odds table. Get your free OddsPapi API key and pull comparison data across 350+ bookmakers as JSON in your next request.