Skip to main content
实时赔率API

实时赔率API:通过WebSocket实时推送赔率

实时赔率API会在博彩公司的赔率变动的那一刻把新价格推送给你的代码。Odds-API.io通过一个WebSocket连接,推送34种体育项目、450+博彩公司的赛前和滚球赔率,同一数据流中还包含实时比分、比赛状态和比赛时钟。

WebSocket数据流是所有付费方案均可添加的附加服务。所有方案都包含REST API。

博彩公司
450+
体育项目
34
延迟
<150ms
补发窗口
24h

为什么用WebSocket而不是轮询

轮询按固定间隔请求赔率,拿回的大多是没有变化的数据。WebSocket保持连接,只传输发生变动的价格,因此你能更早看到变化,传输的数据也少得多。

为什么用WebSocket而不是轮询
对比项REST轮询WebSocket
如何获取变化按固定间隔重复请求价格一变就立即推送
延迟轮询间隔加上请求耗时<150ms
连接每次轮询都发起新的HTTP请求一个持续保持的连接
已撤下的盘口需重新读取赛事才能发现明确的deleted和no_markets消息
适用场景脚本、数据回填和快速查询滚球投注应用、滚球模型和套利扫描器

几行代码即可连接

带上你的API密钥和所需盘口,打开wss://api.odds-api.io/v3/ws。第一条消息是确认过滤条件的欢迎消息,之后赔率会在变化时实时到达。

Node.js
// 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()
Python
# 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.jsws默认开启
浏览器原生WebSocket默认开启
Pythonwebsockets默认开启
Pythonwebsocket-client不支持,请改用websockets
PHPamphp/websocket-client默认开启(需要zlib)
PHPtextalk/websocket不支持,请改用amphp/websocket-client
Gogorilla/websocket设置EnableCompression = true
.NETClientWebSocket设置DangerousDeflateOptions(.NET 6+)

消息类型

每条消息都是带有type字段的JSON对象。赔率消息与/v3/odds端点的格式相同,一个解析器即可同时处理两者。

created
博彩公司新增了一场你订阅的赛事
updated
赔率发生变化。消息包含该赛事和该博彩公司下你所订阅盘口的完整当前数据
deleted
赛事已被移除,或该博彩公司已下线
no_markets
赛事仍在列表中,但你订阅的盘口均未开放
score
实时比分发生变化(scores频道)
status
赛事变为待开始、进行中、已结算或已取消,并附带当前比分(status频道)
clock
对每场比赛时钟正在运行的进行中赛事,约每30秒发送一次比赛时钟快照

整体替换,切勿合并

暂停的盘口不会被标记,而是直接从下一条updated消息中省略。每收到一条消息,就用它覆盖你为该赛事和该博彩公司保存的数据,被撤下的价格会自动从你的应用中消失。

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
status
live表示仅滚球,prematch表示仅赛前

channels是白名单。发送channels=odds,scores,status可同时接收赔率和比分;只发送channels=scores,status则不再接收赔率。

重连不丢数据

网络总会断开。每条赔率消息都带有seq序号,重连后数据流会据此为你补齐数据,无需从头开始。

  1. 1

    保存最新的seq

    保存你收到的最大seq。在同一连接上seq只增不减,但它由所有订阅者共享,因此出现跳号并不代表丢失数据。

  2. 2

    使用lastSeq重连

    在连接URL中加上lastSeq。你会先收到断线期间发生变化的每个赛事和博彩公司的最新状态,然后实时数据流继续推送。

  3. 3

    确认已追上实时数据

    加上replayComplete=true,补发完成时会收到一条replay_complete消息。补发数据最多保留24小时。

  4. 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