实时赔率API:通过WebSocket实时推送赔率
实时赔率API会在博彩公司的赔率变动的那一刻把新价格推送给你的代码。Odds-API.io通过一个WebSocket连接,推送34种体育项目、450+博彩公司的赛前和滚球赔率,同一数据流中还包含实时比分、比赛状态和比赛时钟。
WebSocket数据流是所有付费方案均可添加的附加服务。所有方案都包含REST API。
- 博彩公司
- 450+
- 体育项目
- 34
- 延迟
- <150ms
- 补发窗口
- 24h
为什么用WebSocket而不是轮询
轮询按固定间隔请求赔率,拿回的大多是没有变化的数据。WebSocket保持连接,只传输发生变动的价格,因此你能更早看到变化,传输的数据也少得多。
| 对比项 | REST轮询 | WebSocket |
|---|---|---|
| 如何获取变化 | 按固定间隔重复请求 | 价格一变就立即推送 |
| 延迟 | 轮询间隔加上请求耗时 | <150ms |
| 连接 | 每次轮询都发起新的HTTP请求 | 一个持续保持的连接 |
| 已撤下的盘口 | 需重新读取赛事才能发现 | 明确的deleted和no_markets消息 |
| 适用场景 | 脚本、数据回填和快速查询 | 滚球投注应用、滚球模型和套利扫描器 |
几行代码即可连接
带上你的API密钥和所需盘口,打开wss://api.odds-api.io/v3/ws。第一条消息是确认过滤条件的欢迎消息,之后赔率会在变化时实时到达。
// npm i ws
import WebSocket from 'ws'
const API = 'https://api.odds-api.io/v3'
const apiKey = process.env.ODDS_API_KEY
const markets = 'ML,Spread,Totals'
const eventIds = '63017989,63017990' // up to 10, for example from GET /v3/events/live
const bookmakers = 'Bet365,Unibet' // the bookmakers selected on your account
const board = new Map()
let lastSeq = 0
let needsRebuild = false
function connect() {
const params = new URLSearchParams({ apiKey, markets, eventIds, channels: 'odds,scores,status' })
if (lastSeq) params.set('lastSeq', String(lastSeq))
const ws = new WebSocket(`wss://api.odds-api.io/v3/ws?${params}`)
ws.on('message', (raw) => {
const msg = JSON.parse(raw)
if (msg.type === 'resync_required') {
needsRebuild = true
return ws.close()
}
if (msg.seq > lastSeq) lastSeq = msg.seq
const key = `${msg.id}:${msg.bookie}`
if (msg.type === 'created' || msg.type === 'updated') board.set(key, msg.markets)
if (msg.type === 'deleted' || msg.type === 'no_markets') board.delete(key)
if (msg.type === 'score') console.log(msg.id, msg.scores.home, msg.scores.away)
})
ws.on('error', (err) => console.error('WebSocket error:', err.message || err.code))
ws.on('close', async () => {
if (needsRebuild) lastSeq = await rebuildFromRest()
setTimeout(connect, 2000)
})
}
// Replay was not possible: reload the subscription from REST and resume from its seq
async function rebuildFromRest() {
for (let attempt = 1; ; attempt++) {
try {
const params = new URLSearchParams({ apiKey, eventIds, bookmakers, markets, includeSeq: 'true' })
const res = await fetch(`${API}/odds/multi?${params}`)
const seq = Number(res.headers.get('X-OddsAPI-Seq'))
if (!res.ok || !(seq > 0)) throw new Error(`snapshot not usable: ${res.status}`)
const events = await res.json()
board.clear()
for (const event of events) {
for (const [bookie, odds] of Object.entries(event.bookmakers)) board.set(`${event.id}:${bookie}`, odds)
}
needsRebuild = false
return seq
} catch (err) {
console.error(`${err.message}, retry ${attempt}`)
await new Promise((resolve) => setTimeout(resolve, Math.min(30000, 1000 * 2 ** attempt)))
}
}
}
connect()# pip install websockets
import asyncio
import json
import os
from urllib.parse import urlencode
import websockets
params = urlencode({
"apiKey": os.environ["ODDS_API_KEY"],
"markets": "ML,Spread,Totals",
"leagues": "england-premier-league",
"status": "live",
})
async def main():
async with websockets.connect(f"wss://api.odds-api.io/v3/ws?{params}") as ws:
async for message in ws:
msg = json.loads(message)
if msg["type"] == "updated":
for market in msg["markets"]:
print(msg["id"], msg["bookie"], market["name"], market["odds"][0])
asyncio.run(main())必须启用压缩
客户端在连接时必须声明支持permessage-deflate。它能将带宽降低3到5倍,未启用压缩的连接会被拒绝并返回HTTP 426。大多数库会自动处理:
| 平台 | 库 | 压缩 |
|---|---|---|
| Node.js | ws | 默认开启 |
| 浏览器 | 原生WebSocket | 默认开启 |
| Python | websockets | 默认开启 |
| Python | websocket-client | 不支持,请改用websockets |
| PHP | amphp/websocket-client | 默认开启(需要zlib) |
| PHP | textalk/websocket | 不支持,请改用amphp/websocket-client |
| Go | gorilla/websocket | 设置EnableCompression = true |
| .NET | ClientWebSocket | 设置DangerousDeflateOptions(.NET 6+) |
消息类型
每条消息都是带有type字段的JSON对象。赔率消息与/v3/odds端点的格式相同,一个解析器即可同时处理两者。
created- 博彩公司新增了一场你订阅的赛事
updated- 赔率发生变化。消息包含该赛事和该博彩公司下你所订阅盘口的完整当前数据
deleted- 赛事已被移除,或该博彩公司已下线
no_markets- 赛事仍在列表中,但你订阅的盘口均未开放
score- 实时比分发生变化(scores频道)
status- 赛事变为待开始、进行中、已结算或已取消,并附带当前比分(status频道)
clock- 对每场比赛时钟正在运行的进行中赛事,约每30秒发送一次比赛时钟快照
整体替换,切勿合并
暂停的盘口不会被标记,而是直接从下一条updated消息中省略。每收到一条消息,就用它覆盖你为该赛事和该博彩公司保存的数据,被撤下的价格会自动从你的应用中消失。
{
"type": "updated",
"seq": 482917,
"timestamp": 1723992773,
"id": "63017989",
"bookie": "Bet365",
"markets": [
{
"name": "ML",
"updatedAt": "2026-11-08T15:42:10Z",
"odds": [{ "home": "1.85", "draw": "3.25", "away": "2.10" }]
},
{
"name": "Totals",
"updatedAt": "2026-11-08T15:42:10Z",
"odds": [{ "hdp": 2.5, "over": "1.85", "under": "2.10" }]
}
]
}频道和过滤条件
用channels选择需要的数据流,再用查询参数缩小范围,只接收你要展示的赛事。leagues和eventIds不能同时使用。
频道
odds- 默认频道。推送你账户所选博彩公司的
created、updated、deleted和no_markets消息 scores- 每次比分变化时发送
score消息,另有clock快照 status- 待开始、进行中、已结算和已取消时发送
status消息,另有clock快照
过滤条件
markets- odds频道必填。最多40个盘口名称,例如
ML,Spread,Totals sport- 最多10种体育项目,例如
football,basketball leagues- 最多20个联赛标识,例如
england-premier-league eventIds- 最多50个赛事ID
statuslive表示仅滚球,prematch表示仅赛前
channels是白名单。发送channels=odds,scores,status可同时接收赔率和比分;只发送channels=scores,status则不再接收赔率。
重连不丢数据
网络总会断开。每条赔率消息都带有seq序号,重连后数据流会据此为你补齐数据,无需从头开始。
- 1
保存最新的seq
保存你收到的最大
seq。在同一连接上seq只增不减,但它由所有订阅者共享,因此出现跳号并不代表丢失数据。 - 2
使用lastSeq重连
在连接URL中加上
lastSeq。你会先收到断线期间发生变化的每个赛事和博彩公司的最新状态,然后实时数据流继续推送。 - 3
确认已追上实时数据
加上
replayComplete=true,补发完成时会收到一条replay_complete消息。补发数据最多保留24小时。 - 4
按提示重建
如果无法完整补发,你会收到
resync_required。请带上includeSeq=true从/v3/odds获取快照,再用X-OddsAPI-Seq响应头中的seq重新连接。
每个API密钥可同时保持两个连接,例如一个主连接和一个备用连接。如需更多连接,可向我们申请。
实时比分和比赛时钟
滚球赔率配合比分才更有意义。在同一连接上订阅scores和status频道,或通过REST读取当前状态。
- GET /v3/events/live
- 当前所有进行中的赛事,包含比分和
clock对象。可按体育项目过滤 - clock
minute、playedSeconds、period、running和statusDetail,足球另含伤停补时,网球另含发球方- 分段比分
- 上下半场、节、盘、加时和点球,所有体育项目使用同一套键名
- 走动的时钟
- 在
running为true时从playedSeconds向前计时,每次收到快照时重置为服务器的值
REST备选方案:/odds和/odds/updated
所有方案都包含REST API,提供同样的实时赔率。可用于获取快照,或在不适合使用WebSocket的场景下轮询。
- GET /v3/odds
- 一场赛事在你所选博彩公司的完整快照。
/v3/odds/multi单次请求最多支持10场赛事 - GET /v3/odds/updated
- 真正的增量数据:只返回自
since以来发生变化的盘口,该时间点最多为90秒前。下次调用时将响应头X-Next-Since作为since传入,即可无缝衔接 - includeSeq=true
- 以
X-OddsAPI-Seq返回当前数据流位置,让你从REST快照切换到WebSocket时不会漏掉任何更新
增量数据无法告诉你某个盘口已被撤下,因此轮询客户端应不时通过/v3/odds重新读取赛事。WebSocket则会直接发送deleted和no_markets消息。
实时赔率数据覆盖范围
- 博彩公司
- 450+,包括Bet365、DraftKings、FanDuel、BetMGM、Betfair Exchange,以及欧洲、亚洲、澳大利亚和拉美的本地博彩公司
- 体育项目
- 34种,包括足球、篮球、网球、美式足球、棒球、冰球、板球和电子竞技
- 滚球
- 所有提供滚球盘的博彩公司的实时赔率,约每秒刷新一次
- 盘口
- 独赢、让分、大小球、亚洲让球、角球、罚牌和球员数据盘等,视博彩公司而定
- 延迟
- 通过WebSocket推送的更新延迟<150ms
- 格式
- 与
/v3/odds相同的JSON,通过WebSocket和REST提供
方案与WebSocket访问
所有付费方案都包含提供实时赔率的REST API。WebSocket数据流是可添加到任何方案的附加服务。
- 附加服务的费用与方案价格相同,因此总价为方案价格的2倍,可按月或按年付费
- 数据流包含你账户所选的全部博彩公司,因此可用的博彩公司数量取决于你的方案
- 比分、状态和比赛时钟通过同一连接推送
- 可按月随时取消
实时赔率API常见问题
什么是实时赔率API?
实时赔率API以JSON格式向你的代码提供进行中赛事的当前投注赔率,并在博彩公司调整价格时保持数据同步。Odds-API.io通过WebSocket推送450+博彩公司的滚球和赛前赔率,同样的赔率也可通过REST获取。
实时赔率有多快?
WebSocket更新的延迟为<150ms,滚球赔率约每秒刷新一次。REST轮询还要额外加上你的轮询间隔,因此我们建议滚球应用使用WebSocket。
WebSocket连接断开怎么办?
重连时将lastSeq参数设为你收到的最后一个seq。对于最长24小时的中断,服务器会补发断线期间所有变化的最新状态,然后继续实时推送。如果无法完整补发,服务器会发送resync_required,你需要通过REST快照重建数据。
可以使用哪些WebSocket客户端?
任何支持permessage-deflate压缩的客户端均可,压缩为必需项。Node.js ws、浏览器WebSocket和Python websockets默认支持。Python websocket-client和PHP textalk/websocket不支持,连接会被拒绝并返回HTTP 426。在Go中,请在gorilla/websocket上设置EnableCompression。
数据流包含实时比分和比赛时钟吗?
包含。在连接URL中加上channels=odds,scores,status,即可在赔率之外收到比分变化、状态变化,以及约每30秒一次的比赛时钟快照。GET /v3/events/live通过REST返回同样的比分和时钟。
如何开通WebSocket访问?
选择一个付费方案并添加WebSocket数据流。其费用与方案价格相同,推送你账户所选的全部博彩公司,每个API密钥可保持两个连接。所有方案都包含REST API,包括实时赔率和/v3/odds/updated。
立即接入实时赔率
选择方案,添加WebSocket数据流,只需一个连接即可接收数据流中450+博彩公司的实时赔率。
随时取消 · REST和WebSocket使用相同的JSON