One id across every book
Events, teams and players resolve to canonical ids. points-all-game-ou-over is the same odd on DraftKings, bet365 and Stake, so a consensus or a best-price is a lookup, not a matching project.
41 sportsbooks · 17 leagues · one key
A normalized real-time odds API across 40+ sportsbooks: main lines, player props and alternate ladders, with deep links back to the bet slip. REST for the board, WebSocket for every change.
Priced from 41 sportsbooks with one key. No per-book setup.
Full coverageUS, UK, EU, sharps, exchanges, crypto
NFL to CS2, prematch and live
median on live markets
trailing 90 days
What you get
Props screeners, EV tools, line-shopping apps and trading desks all need the same three things: one identity across books, the book's own line, and a stream that never silently drops a change.
Events, teams and players resolve to canonical ids. points-all-game-ou-over is the same odd on DraftKings, bet365 and Stake, so a consensus or a best-price is a lookup, not a matching project.
Player props as one row per player and stat. Each book keeps its own line in its own cell, with the alternate ladder beside it. Two books on different totals never get blended into a number that matches neither.
Every frame carries a cursor. Reconnect with resume_from and receive exactly what you missed, or a resync_required telling you to snapshot again. No gaps you find out about later.
Decimal and American on every cell. null when a book has no usable price, never 0. A tri-state status, and lastSeenAt so you can tell an unchanged price from a book that stopped talking.
Selection-level links where the book supports them, event-level elsewhere, on every REST cell and every stream frame. A “bet now” button that never needs a second lookup.
In-play markets flow on the same connection as the prematch board. Each event carries its phase and the freshest score, period and clock any book has sent.
Normalized, not blended
The same id on every book. The line is deliberately not part of it: it lives in each book's cell as overUnder or spread, because two books can sit on different lines for the same odd, and the ladder beneath a main line belongs to that book alone.
Two surfaces, one contract
Same key, same ids. Every snapshot is stamped with the stream cursor it reflects, so a client applies frames from exactly that point and never double-counts a move.
/v1/events one row per event: phase, score, the best moneyline and which books price it./v1/events/:id the flat odds map: every odd, every book's cell with its main line and ladder./v1/props the screener: one row per player and stat, sorted by the widest gap between books./v1/changes the change log over HTTP, for clients that would rather poll than hold a socket.# The NBA board: one summary row per event,
# stamped with the stream cursor it reflects
curl "https://api.oddshouse.io/v1/events?league=nba&limit=50" \
-H "x-api-key: $ODDSHOUSE_KEY"
# One event: the flat odds map keyed by oddID,
# every book inside each odd
curl "https://api.oddshouse.io/v1/events/evt_2f9c1e" \
-H "x-api-key: $ODDSHOUSE_KEY"
# The player-prop screener: one row per (player, stat),
# widest gap between books first
curl "https://api.oddshouse.io/v1/props?league=nba&shared=1&sort=gap" \
-H "x-api-key: $ODDSHOUSE_KEY"import WebSocket from "ws";
const URL = "wss://api.oddshouse.io/v1/updates";
const ws = new WebSocket(`${URL}?key=${process.env.ODDSHOUSE_KEY}`);
let epoch = null, cursor = null;
ws.on("open", () => ws.send(JSON.stringify({
action: "subscribe",
filters: {
leagues: ["nba"],
books: ["draftkings", "fanduel"],
market_groups: ["player_props"],
},
})));
ws.on("message", (raw) => {
const frame = JSON.parse(raw);
if (frame.action === "hello") epoch = frame.data.epoch;
if (frame.cursor) cursor = frame.cursor; // for resume_from on reconnect
if (frame.action === "line_update") {
const { book, player, market, prev, price, dir } = frame.data;
console.log(book, player ?? market.type, prev, "->", price, dir);
}
if (frame.action === "resync_required") snapshotAgain();
});hello opens with your plan, the schema version, the stream epoch and the current cursor.subscribe narrows to leagues, books, market groups or events; the reply names anything it dropped.line_update one frame per change with prev, dir, the deep link and both timestamps.resume_from on reconnect replays exactly what you missed, or says resync_required when it can't.Coverage
Regulated US books, the UK high street, European leaders, the sharps, both exchanges and the crypto books, on the leagues your users actually bet.
Full boards, prematch and live.
Regulated, sharp, exchange and crypto, by region.
Reliability
Latency and uptime are numbers we report, not adjectives. Every frame carries the book's timestamp and ours, and every book has its own line on the status page.
Every frame carries the book's timestamp and ours. These are the medians we see on live markets.
seconds · solid: median · faint: 95th percentile
A book going quiet is an incident we report, not a stale board you discover.
Pricing
Every plan is the same feed with different limits: leagues, books, props, ladder depth, requests and sockets. Change them whenever your product does.
Try the feed on one league: every book, main lines, no card.
7 days, no card
Start freeA first product on a few leagues.
billed monthly
Start with StarterThe whole board, full ladders, room to grow.
billed monthly
Start with ProCustom entitlements, private books, an edge near you.
annual agreement
Talk to usPrices in USD, excluding tax. Every plan includes every sportsbook and the live markets.
FAQ
Live markets are read from each book's own push feed where it has one and polled where it doesn't. A change is published as one frame the moment the book confirms it, stamped with ts_received and ts_broadcast so you can measure us rather than take our word for it. Prematch boards re-poll on a slower cadence, because prematch prices crawl.
Events, teams and players get one canonical id across every book, and every odd gets a five-part id: stat, entity, period, bet type, side. A book's own line stays with the book. Two books can price the same odd at different lines and you see both, each with its own ladder, instead of a blended number that matches neither.
Yes. Player props come as one row per player and stat with each book's own line and ladder. Starter includes 3 alternate lines per cell, Pro and above the full ladder. Milestone props (a book quoting 'over 24.5' as '25+') are marked so your under side does not go missing.
Both, with the same key. Snapshot the board over REST (every snapshot is stamped with the stream cursor it reflects), open the socket, and apply the frames whose cursor is after that stamp. If the socket drops, reconnect with resume_from and the epoch from the hello frame and you receive exactly what you missed, or a resync_required frame telling you to snapshot again.
Each key has a token bucket that refills at your plan's per-minute rate. Over the limit you get a 429 with the reset time; nothing is queued or silently dropped. /v1/account/usage shows the bucket, today's request count by endpoint and your open sockets, so you can watch it live.
7 days, no card. You pick one league, get every sportsbook on it with main lines, 60 requests a minute and one socket. Upgrade from the portal whenever you want and the key keeps working; nothing needs re-issuing.
One league, every sportsbook, 7 days, no card. The key you get today is the key you scale with.