Skip to main content
ライブオッズAPI

ライブオッズAPI:WebSocketでリアルタイム配信

ライブオッズAPIは、ブックメーカーの価格が変わった瞬間にその値をコードへ届けます。Odds-API.ioは、34競技・450以上のブックメーカーのプレマッチおよびインプレーオッズを1本のWebSocket接続で配信し、ライブスコア、試合ステータス、試合時計も同じストリームで提供します。

WebSocketストリーミングは、すべての有料プランに追加できるアドオンです。RESTはすべてのプランに含まれます。

ブックメーカー
450+
競技
34
レイテンシ
<150ms
リプレイ期間
24h

ポーリングではなくWebSocketを使う理由

ポーリングは一定間隔でオッズを問い合わせるため、返ってくるデータの大半は変化していません。WebSocketは接続を開いたまま、動いた価格だけを運びます。そのため変化をより早く受け取れ、転送量も大幅に減ります。

ポーリングではなくWebSocketを使う理由
項目RESTポーリングWebSocket
変化の受け取り方一定間隔で再度リクエスト価格が変わった時点でプッシュ
レイテンシポーリング間隔+リクエスト時間<150ms
接続ポーリングごとに新しいHTTPリクエスト開いたままの1本の接続
取り下げられたマーケットイベントを再取得しないと気づけない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分の1に削減でき、圧縮なしの接続はHTTP 426で拒否されます。ほとんどのライブラリは自動で対応しています:

圧縮は必須です
プラットフォームライブラリ圧縮
Node.jswsデフォルトで有効
ブラウザネイティブのWebSocketデフォルトで有効
Pythonwebsocketsデフォルトで有効
Pythonwebsocket-client非対応。websocketsを使用
PHPamphp/websocket-clientデフォルトで有効(zlibが必要)
PHPtextalk/websocket非対応。amphp/websocket-clientを使用
Gogorilla/websocketEnableCompression = trueを設定
.NETClientWebSocketDangerousDeflateOptionsを設定(.NET 6以降)

メッセージの種類

すべてのメッセージはtypeを持つJSONオブジェクトです。オッズのメッセージは/v3/oddsエンドポイントと同じ形式なので、1つのパーサーで両方を処理できます。

created
購読中のイベントをブックメーカーが追加した
updated
オッズが変化した。そのイベントとブックメーカーについて、購読中マーケットの現在の全データを含みます
deleted
イベントが削除された、またはそのブックメーカーが停止した
no_markets
イベントは掲載中だが、購読中のマーケットがどれも開いていない
score
ライブスコアが変化した(scoresチャンネル)
status
イベントがpending、live、settled、cancelledのいずれかに変わった。現在のスコア付き(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
pending、live、settled、cancelledを知らせるstatusメッセージと、clockスナップショット

フィルター

markets
oddsチャンネルでは必須。ML,Spread,Totalsのように最大40個のマーケット名
sport
football,basketballのように最大10競技
leagues
england-premier-leagueのように最大20個のリーグslug
eventIds
最大50個のイベントID
status
インプレーのみならlive、プレマッチのみならprematch

channelsは許可リストです。オッズとスコアを一緒に受け取るにはchannels=odds,scores,statusを送ります。channels=scores,statusだけを指定するとオッズは届かなくなります。

取りこぼしのない再接続

ネットワークは切れるものです。すべてのオッズメッセージにはseq番号が付いており、フィードはこれを使って、再接続後に最初からやり直すことなく差分を追いつかせます。

  1. 1

    最後のseqを保持する

    受信した中で最大のseqを保存します。1つの接続内でseqは増える一方ですが、全購読者で共有されているため、番号が飛んでも欠落ではありません。

  2. 2

    lastSeqを付けて再接続する

    接続URLにlastSeqを追加します。切断中に変化したすべてのイベントとブックメーカーの最新状態を受け取り、その後ライブストリームが続きます。

  3. 3

    ライブに戻ったことを知る

    replayComplete=trueを追加すると、追いつきが完了した時点でreplay_completeメッセージが1件届きます。リプレイ用データは最大24時間保持されます。

  4. 4

    指示があれば再構築する

    正常なリプレイができない場合はresync_requiredが届きます。includeSeq=trueを付けて/v3/oddsからスナップショットを取得し、X-OddsAPI-Seqヘッダーのseqで再接続してください。

各APIキーで同時に2本の接続を維持できます(例:プライマリとスタンバイ)。追加の接続はご相談に応じます。

ライブスコアと試合時計

インプレーオッズはスコアと並べてこそ意味を持ちます。同じ接続でscoresチャンネルとstatusチャンネルを購読するか、RESTで現在の状態を取得してください。

GET /v3/events/live
現在進行中のすべてのイベントを、スコアとclockオブジェクト付きで返します。競技で絞り込み可能
clock
minute、playedSeconds、period、running、statusDetailに加え、サッカーではアディショナルタイム、テニスではサーブ権
ピリオド別スコア
ハーフ、クォーター、セット、延長、PK戦を、全競技共通のキーで提供
時計の進行表示
runningがtrueの間はplayedSecondsからカウントアップし、スナップショットを受け取るたびにサーバーの値にリセットします

RESTによるフォールバック:/oddsと/odds/updated

すべてのプランにREST APIが含まれ、同じライブオッズを提供します。スナップショットの取得や、ソケットが使えない環境でのポーリングに利用できます。

GET /v3/odds
選択したブックメーカーの1イベント分の完全なスナップショット。/v3/odds/multiなら1リクエストで最大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競技。サッカー、バスケットボール、テニス、アメリカンフットボール、野球、アイスホッケー、クリケット、eスポーツなど
インプレー
インプレーでオッズを出すすべてのブックメーカーのライブオッズを、約1秒ごとに更新
マーケット
マネーライン、スプレッド、トータル、アジアンハンディキャップ、コーナー、カード、選手プロップなど(ブックメーカーにより異なります)
レイテンシ
WebSocketでプッシュされる更新は<150ms
形式
/v3/oddsと同じJSONを、WebSocketとRESTで提供

プランとWebSocketアクセス

すべての有料プランに、ライブオッズを含むREST APIが付いています。WebSocketストリーミングは、どのプランにも追加できるアドオンです。

  • アドオンの料金はプラン料金と同額で、合計はプラン料金の2倍です(月払い・年払いとも)
  • ストリームにはアカウントで選択したすべてのブックメーカーが含まれるため、利用できるブックメーカー数はプランで決まります
  • スコア、ステータス、試合時計も同じ接続で届きます
  • いつでも月単位で解約できます
プランを比較 →

ライブオッズAPIのよくある質問

ライブオッズAPIとは何ですか?

ライブオッズAPIは、進行中のイベントの現在のベッティングオッズをJSONでコードに提供し、ブックメーカーが価格を動かすたびに最新の状態に保ちます。Odds-API.ioは450以上のブックメーカーのライブおよびプレマッチオッズをWebSocketで配信し、同じオッズをRESTでも提供しています。

ライブオッズの速度はどのくらいですか?

WebSocketの更新は<150msのレイテンシで届き、インプレーオッズは約1秒ごとに更新されます。RESTのポーリングではさらにポーリング間隔の分だけ遅れるため、インプレー向けアプリにはWebSocketをおすすめしています。

WebSocket接続が切れた場合はどうなりますか?

最後に受信したseqをlastSeqパラメータに指定して再接続してください。サーバーは切断中に変化したすべての最新状態を再送し(最大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キーごとに2本の接続が使えます。ライブオッズや/v3/odds/updatedを含むREST APIは、すべてのプランに含まれます。

今すぐライブオッズを配信

プランを選んでWebSocketストリーミングを追加し、1本の接続を開くだけで、フィード上の450以上のブックメーカーのライブオッズを受け取れます。

いつでも解約可能 · RESTとWebSocketで同じJSON