{"id":129,"date":"2026-01-24T14:43:16","date_gmt":"2026-01-24T14:43:16","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=129"},"modified":"2026-10-08T13:35:36","modified_gmt":"2026-10-08T13:35:36","slug":"websocket-odds-api-real-time-betting-data","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/","title":{"rendered":"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes"},"content":{"rendered":"<p>A WebSocket odds API pushes each price change to you the moment the feed sees it, so you stop asking &#8220;has anything moved?&#8221; on a timer. OddsPapi streams odds over <code>wss:\/\/v5.oddspapi.io\/ws<\/code> on its B2B plan. The free plan gives you the pre-game REST API and historical odds, 250 requests a month, across every sport and bookmaker.<\/p>\n<p>The useful question is how much a poller loses. We measured it on 8 October 2026. Between 13:10 and 13:20 UTC we polled two soccer boards every 4 seconds and counted every price that changed. Then we re-sampled the same data at slower poll rates to see what each rate would have caught.<\/p>\n<h2>What a Poll Misses: 10 Minutes, Two Boards<\/h2>\n<p>We asked both boards for the same 10 bookmakers: <code>pinnacle<\/code>, <code>singbet<\/code>, <code>betfair-ex<\/code>, <code>1xbet<\/code>, <code>draftkings<\/code>, <code>fanduel<\/code>, <code>polymarket<\/code>, <code>stake<\/code>, <code>betano<\/code> and <code>kalshi<\/code>. Eight of them returned prices on the in-play board and nine on the pre-match board. A &#8220;move&#8221; is a price whose value and <code>changedAt<\/code> both changed between two polls. Two changes inside one 4-second gap count as one, so every number below is a floor.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Board<\/th>\n<th>Prices<\/th>\n<th>Moves in 10 min<\/th>\n<th>Seen at 16 s<\/th>\n<th>Seen at 60 s<\/th>\n<th>Seen at 120 s<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>In play: V-League, Viettel v Hong Linh Ha Tinh<\/td>\n<td>1,460<\/td>\n<td>1,438 (144 a minute)<\/td>\n<td>796 (55%)<\/td>\n<td>438 (30%)<\/td>\n<td>316 (22%)<\/td>\n<\/tr>\n<tr>\n<td>Pre-match: Veikkausliiga, HJK v VPS, 1 h 50 m out<\/td>\n<td>2,378<\/td>\n<td>675 (68 a minute)<\/td>\n<td>502 (74%)<\/td>\n<td>165 (24%)<\/td>\n<td>133 (20%)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>The books move in two different ways, and the split decides whether you need the stream.<\/p>\n<ul>\n<li><strong>Exchanges and prediction markets tick.<\/strong> In play, Betfair Exchange moved 1,141 times across 119 prices, so the same prices changed again and again. A 60-second poll saw 193 of those 1,141 moves. Pre-match, Polymarket moved 226 times across 73 prices.<\/li>\n<li><strong>Sportsbooks reprice in bursts.<\/strong> In play, 1xBet moved 102 prices, Betano 47, DraftKings 36 and Pinnacle 23. Each book stamped its whole batch with one <code>changedAt<\/code> second, and no price moved twice. A 120-second poll caught all of them, late.<\/li>\n<\/ul>\n<p>Pinnacle sits between the two before kick-off. It made 268 moves on the HJK board across 163 prices, in nine bursts, and a 60-second poll saw 50 of them.<\/p>\n<p>So the answer depends on what you trade. If you read a sportsbook&#8217;s line once before you bet, a poll every minute or two catches each reprice. If you trade against an exchange ladder, a 60-second poll showed you about one move in six on the in-play board.<\/p>\n<h2>What Polling Costs<\/h2>\n<p>A missed move is one cost. The bill for the polls is the other.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Poll plan for one fixture<\/th>\n<th>Requests an hour<\/th>\n<th>Data an hour<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Every 4 s, 10 books requested (1.0 MB a call on HJK v VPS)<\/td>\n<td>900<\/td>\n<td>about 0.9 GB<\/td>\n<\/tr>\n<tr>\n<td>Every 4 s, all books (24.5 MB a call, 254 books)<\/td>\n<td>900<\/td>\n<td>about 22 GB<\/td>\n<\/tr>\n<tr>\n<td>Every 60 s, all books<\/td>\n<td>60<\/td>\n<td>about 1.5 GB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>At one call every 4 seconds, the free plan&#8217;s 250 monthly requests last about 17 minutes on one fixture. Two more limits apply to REST. Back-to-back <code>\/v4\/odds<\/code> calls return HTTP 429 with a <code>retryMs<\/code> field of about 300 ms, so a poller has to pace itself. And a poll returns the full state of every price you ask for, whether it moved or not. On the in-play board, 1,460 prices came back on each call. The median call carried 2 changed prices, and 48 of 149 carried none.<\/p>\n<h2>REST Polling vs the OddsPapi WebSocket<\/h2>\n<p>Every row in the WebSocket column comes from the public reference at <a href=\"https:\/\/docs.oddspapi.io\/websocket\/overview\" target=\"_blank\" rel=\"noopener\">docs.oddspapi.io<\/a>, read on 8 October 2026.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Aspect<\/th>\n<th>REST polling (v4)<\/th>\n<th>WebSocket (v5, B2B plan)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>How you get a change<\/td>\n<td>You ask on a timer<\/td>\n<td>The server pushes it<\/td>\n<\/tr>\n<tr>\n<td>Delay<\/td>\n<td>Up to one poll interval, plus the call<\/td>\n<td>Batched on a fixed window of about 10 ms; only the newest value per price inside one window is sent<\/td>\n<\/tr>\n<tr>\n<td>What each message carries<\/td>\n<td>The full state of every price you asked for<\/td>\n<td>One row per changed price, keyed <code>fixtureId:bookmaker:outcomeId:playerId<\/code><\/td>\n<\/tr>\n<tr>\n<td>Filters<\/td>\n<td><code>fixtureId<\/code>, <code>bookmakers<\/code> per call<\/td>\n<td><code>channels<\/code>, <code>sportIds<\/code>, <code>tournamentIds<\/code>, <code>fixtureIds<\/code>, <code>bookmakers<\/code>, set once at login<\/td>\n<\/tr>\n<tr>\n<td>Channels<\/td>\n<td>One endpoint per data type<\/td>\n<td><code>odds<\/code>, <code>fixtures<\/code>, <code>scores<\/code>, <code>bookmakers<\/code>, <code>futures<\/code>, <code>currencies<\/code> and more on one socket<\/td>\n<\/tr>\n<tr>\n<td>Recovery after a drop<\/td>\n<td>Poll again<\/td>\n<td>Resume from a stored <code>serverEpoch<\/code> and per-channel <code>entryId<\/code>; a <code>snapshot_required<\/code> message tells you to re-fetch by REST<\/td>\n<\/tr>\n<tr>\n<td>Size on the wire<\/td>\n<td>JSON<\/td>\n<td>JSON, MessagePack, or zstd (the docs quote about 5-6x smaller on <code>odds<\/code>, 7-9x with trained dictionaries)<\/td>\n<\/tr>\n<tr>\n<td>Limits<\/td>\n<td>Request quota; short gap between <code>\/odds<\/code> calls<\/td>\n<td>Up to 5 connections per key group; the server closes a client that cannot keep up<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>The stream sends state, not a tick ledger. If two changes to one price land inside one 10 ms window, you get the second. For every tick and for opening and closing lines, the docs point you to the historical endpoints, which the free plan also includes.<\/p>\n<h2>How the Stream Works<\/h2>\n<p>The flow below is the one the docs recommend for production. Connect first, then take a REST snapshot, so nothing falls in the gap between the two.<\/p>\n<ol>\n<li>Open <code>wss:\/\/v5.oddspapi.io\/ws<\/code> and send a <code>login<\/code> message within 10 seconds. The login carries your key, your channels and your filters.<\/li>\n<li>After <code>login_ok<\/code>, apply each update to your store, one row per price key.<\/li>\n<li>Fetch a REST snapshot and merge it. Per key, keep the side with the newer <code>changedAt<\/code>.<\/li>\n<li>Store <code>serverEpoch<\/code> and the last <code>entryId<\/code> per channel.<\/li>\n<li>On reconnect, send both to resume. If the server answers <code>snapshot_required<\/code>, re-fetch the snapshot and merge again.<\/li>\n<\/ol>\n<p>A login message in the shape the docs define, with channels and filters:<\/p>\n<pre class=\"wp-block-code\"><code>{\n  \"type\": \"login\",\n  \"apiKey\": \"YOUR_API_KEY\",\n  \"receiveType\": \"zstd\",\n  \"channels\": [\"odds\", \"fixtures\", \"scores\"],\n  \"sportIds\": [10],\n  \"bookmakers\": [\"pinnacle\", \"betfair-ex\", \"polymarket\"]\n}<\/code><\/pre>\n<p>Every update arrives in one envelope, shown here in the documented shape. <code>ts<\/code> is the send time and <code>entryId<\/code> is your resume cursor:<\/p>\n<pre class=\"wp-block-code\"><code>{\n  \"channel\": \"odds\",\n  \"type\": \"UPDATE\",\n  \"payload\": { \"...\": \"...\" },\n  \"ts\": 1765497902846,\n  \"entryId\": \"1765497902846-3221\"\n}<\/code><\/pre>\n<p>An <code>odds<\/code> payload row carries <code>price<\/code>, <code>active<\/code>, <code>marketId<\/code>, <code>limit<\/code> (the largest stake the book accepts), <code>meta<\/code> (exchange ladders) and two clocks: <code>bookmakerChangedAt<\/code>, when the book says it moved, and <code>changedAt<\/code>, when OddsPapi saw it. The full client examples, in Node and Python, are on the <a href=\"https:\/\/docs.oddspapi.io\/quickstart\" target=\"_blank\" rel=\"noopener\">quickstart page<\/a>. We did not run them for this post, because the stream needs a B2B key.<\/p>\n<h2>Build the REST Half on a Free Key<\/h2>\n<p>The snapshot-and-merge half of that flow runs on the free plan, and you can measure your own board with it. The code below finds the next soccer kick-offs, reads one board in the same key shape the stream uses, and counts moves for 60 seconds. A full run costs about 15 to 35 of your 250 monthly requests, depending on how many thin boards it skips.<\/p>\n<h3>Step 1: Find a Board and Read It<\/h3>\n<pre class=\"wp-block-code\"><code>import os\nimport time\nimport requests\nfrom datetime import date, datetime, timedelta, timezone\n\nAPI_KEY = os.environ[\"ODDSPAPI_API_KEY\"]\nBASE_URL = \"https:\/\/api.oddspapi.io\/v4\"\n\n# Step 1: find soccer fixtures that carry odds (from\/to is required)\ntoday = date.today()\nfixtures = requests.get(f\"{BASE_URL}\/fixtures\", params={\n    \"apiKey\": API_KEY,\n    \"sportId\": 10,\n    \"from\": today.isoformat(),\n    \"to\": (today + timedelta(days=1)).isoformat(),\n    \"hasOdds\": \"true\",\n}).json()\nsoon = sorted((f for f in fixtures if f.get(\"statusName\") == \"Pre-Game\"\n               and f[\"startTime\"] &gt; datetime.now(timezone.utc).isoformat()),\n              key=lambda f: f[\"startTime\"])\nprint(f\"{len(fixtures)} fixtures with odds, {len(soon)} still to kick off\")\n\n# Step 2: one snapshot of one board, keyed the way the stream keys it\ndef snapshot(fixture_id, bookmakers):\n    params = {\"apiKey\": API_KEY, \"fixtureId\": fixture_id, \"bookmakers\": bookmakers}\n    r = requests.get(f\"{BASE_URL}\/odds\", params=params)\n    if r.status_code == 429:  # \/odds wants a short gap between calls\n        time.sleep(r.json()[\"error\"].get(\"retryMs\", 1000) \/ 1000)\n        r = requests.get(f\"{BASE_URL}\/odds\", params=params)\n    r.raise_for_status()\n    rows = {}\n    for book, data in r.json().get(\"bookmakerOdds\", {}).items():\n        for market_id, market in (data.get(\"markets\") or {}).items():\n            for outcome_id, outcome in (market.get(\"outcomes\") or {}).items():\n                for player_id, p in (outcome.get(\"players\") or {}).items():\n                    key = f\"{fixture_id}:{book}:{outcome_id}:{player_id}\"\n                    rows[key] = {\"price\": p[\"price\"], \"active\": p.get(\"active\"),\n                                 \"changedAt\": p.get(\"changedAt\")}\n    return rows\n\nBOOKS = \"pinnacle,betfair-ex,1xbet,polymarket\"\nfor fx in soon[:20]:  # the next kick-offs move fastest before the whistle\n    board = snapshot(fx[\"fixtureId\"], BOOKS)\n    if len(board) &gt; 500:  # skip thin boards\n        break\n    time.sleep(0.5)  # \/odds wants a short gap between calls\nprint(f\"{fx['participant1Name']} v {fx['participant2Name']}: {len(board)} prices\")<\/code><\/pre>\n<h3>Step 2: Merge a Snapshot by changedAt<\/h3>\n<p>This is the merge rule from the docs. It works the same whether the newer row comes from a REST poll or from the socket.<\/p>\n<pre class=\"wp-block-code\"><code>def merge(store, rows):\n    \"\"\"Keep whichever side has the newer changedAt, per key (ISO strings sort by time).\"\"\"\n    changed = 0\n    for key, row in rows.items():\n        old = store.get(key)\n        if old is None or (row[\"changedAt\"] or \"\") &gt; (old[\"changedAt\"] or \"\"):\n            store[key] = row\n            changed += 1\n    return changed<\/code><\/pre>\n<h3>Step 3: Count the Moves on Your Board<\/h3>\n<pre class=\"wp-block-code\"><code>import time\nfrom collections import Counter\n\ndef count_moves(fixture_id, bookmakers, seconds=300, every=5):\n    \"\"\"Poll one board and count price moves per book, using changedAt.\"\"\"\n    seen, moves = {}, Counter()\n    stop = time.time() + seconds\n    while time.time() &lt; stop:\n        for key, row in snapshot(fixture_id, bookmakers).items():\n            book = key.split(\":\")[1]\n            if key in seen and row[\"changedAt\"] != seen[key][\"changedAt\"] \\\n                    and row[\"price\"] != seen[key][\"price\"]:\n                moves[book] += 1\n            seen[key] = row\n        time.sleep(every)\n    return moves\n\nmoves = count_moves(fx[\"fixtureId\"], BOOKS, seconds=60)\nfor book in BOOKS.split(\",\"):\n    n = moves[book]\n    print(f\"{book:12} {n:4} moves in 60 s\")<\/code><\/pre>\n<p>Our run at 13:34 UTC on 8 October 2026, on a Turkish Cup fixture about 25 minutes before kick-off:<\/p>\n<pre class=\"wp-block-code\"><code>214 fixtures with odds, 174 still to kick off\nDenizli Idmanyurdu 1959 Spor Kulubu v Serikspor: 529 prices\npinnacle       53 moves in 60 s\nbetfair-ex      0 moves in 60 s\n1xbet           0 moves in 60 s\npolymarket      0 moves in 60 s<\/code><\/pre>\n<p>Pinnacle moved 53 prices in that minute while the other three books held still. A run on the same board one minute earlier counted zero moves on all four books, and an earlier one counted 329 Pinnacle moves. One minute tells you little, so run it on your own fixtures before you pick a poll rate. A merge of a fresh snapshot into the first one, about a minute later, updated 59 rows.<\/p>\n<p>A few notes before you point this at a live board. In-play REST odds come with the paid custom plan, not the free one. Pace your calls: the 429 body tells you how long to wait in <code>retryMs<\/code>. And dedupe skins before you count books: several slugs can quote byte-identical prices, as the <a href=\"https:\/\/oddspapi.io\/blog\/consensus-odds-fair-odds-calculator-python\/\">consensus odds guide<\/a> shows.<\/p>\n<h2>Who Needs the Stream<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>What you build<\/th>\n<th>What to use<\/th>\n<th>Why, from the run above<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Exchange or prediction-market trading<\/td>\n<td>WebSocket<\/td>\n<td>A 60-second poll saw 193 of 1,141 Betfair Exchange moves in play<\/td>\n<\/tr>\n<tr>\n<td>In-play models and alerts<\/td>\n<td>WebSocket<\/td>\n<td>144 moves a minute on one in-play board, 8 books<\/td>\n<\/tr>\n<tr>\n<td>Steam alerts on sharp books before kick-off<\/td>\n<td>WebSocket, or REST every few seconds on a short list<\/td>\n<td>Pinnacle made 268 moves in 10 minutes on one pre-match board<\/td>\n<\/tr>\n<tr>\n<td>Line shopping on sportsbooks before you bet<\/td>\n<td>REST<\/td>\n<td>Sportsbooks repriced in bursts; a 120-second poll caught every burst<\/td>\n<\/tr>\n<tr>\n<td>Backtests and closing-line value<\/td>\n<td>REST historical odds<\/td>\n<td>History holds every snapshot; the stream holds state<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>For the sportsbook-lag side of this, the <a href=\"https:\/\/oddspapi.io\/blog\/soft-book-lag-pinnacle\/\">soft book lag study<\/a> times how long 1xBet and bet365 take to follow a Pinnacle move. For an alert bot on top of REST polling, start with the <a href=\"https:\/\/oddspapi.io\/blog\/steam-move-detector-python\/\">steam move detector<\/a>. In-play polling itself is covered in the <a href=\"https:\/\/oddspapi.io\/blog\/live-betting-api-real-time-in-play-odds\/\">live betting API guide<\/a>, and backtests in <a href=\"https:\/\/oddspapi.io\/blog\/backtest-betting-model-free-historical-odds\/\">how to backtest with free historical odds<\/a>.<\/p>\n<h2>Building for a sportsbook or a trading desk?<\/h2>\n<p>The B2B plan adds the stream on top of the same data: per-channel cursors with resume and replay, opening and closing line values for CLV, and a per-outcome settlement result on <code>\/v4\/settlements<\/code> once a fixture finishes. The <a href=\"https:\/\/oddspapi.io\/blog\/odds-feed-provider-sportsbooks-trading-desks\/\">odds feed provider guide<\/a> walks through the desk setup, and the reference lives at <a href=\"https:\/\/docs.oddspapi.io\/\" target=\"_blank\" rel=\"noopener\">docs.oddspapi.io<\/a>. Pricing is usage-based, so a desk pays for the bookmakers, sports and request volume it reads.<\/p>\n<h2>Frequently Asked Questions<\/h2>\n<h3>Does OddsPapi have a WebSocket odds API?<\/h3>\n<p>Yes. The gateway is <code>wss:\/\/v5.oddspapi.io\/ws<\/code>, and it streams odds, fixtures, scores, bookmaker status, futures and currency rates. WebSocket access comes with the B2B plan.<\/p>\n<h3>What does the free plan include?<\/h3>\n<p>The pre-game REST API, every sport and bookmaker, historical odds, and 250 requests a month. That is enough to build and test the REST half of a streaming client.<\/p>\n<h3>How fast does the stream deliver a change?<\/h3>\n<p>The odds channel batches on a fixed window of about 10 ms. Inside one window, only the newest value per price is sent. Each row carries <code>changedAt<\/code>, so you can measure the delay on your side.<\/p>\n<h3>How often do odds change?<\/h3>\n<p>On 8 October 2026, an in-play V-League board moved 144 prices a minute across the 8 books that priced it, most of them on Betfair Exchange. A pre-match Veikkausliiga board 1 hour 50 minutes from kick-off moved 68 a minute, led by Pinnacle and Polymarket.<\/p>\n<h3>Can I get every tick from the stream?<\/h3>\n<p>The stream sends the latest state of each price. For every recorded snapshot, and for opening and closing lines, use the historical odds endpoint.<\/p>\n<h3>What happens if my connection drops?<\/h3>\n<p>Reconnect with your stored <code>serverEpoch<\/code> and the last <code>entryId<\/code> per channel, and the gateway replays what you missed. The docs example shows a 60-second replay window. After a longer gap, the server sends <code>snapshot_required<\/code> and you re-fetch a REST snapshot.<\/p>\n<p><script type=\"application\/ld+json\">\n{\"@context\":\"https:\/\/schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[\n{\"@type\":\"Question\",\"name\":\"Does OddsPapi have a WebSocket odds API?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes. The gateway is wss:\/\/v5.oddspapi.io\/ws, and it streams odds, fixtures, scores, bookmaker status, futures and currency rates. WebSocket access comes with the B2B plan.\"}},\n{\"@type\":\"Question\",\"name\":\"What does the free plan include?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The pre-game REST API, every sport and bookmaker, historical odds, and 250 requests a month. That is enough to build and test the REST half of a streaming client.\"}},\n{\"@type\":\"Question\",\"name\":\"How fast does the stream deliver a change?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The odds channel batches on a fixed window of about 10 ms. Inside one window, only the newest value per price is sent. Each row carries changedAt, so you can measure the delay on your side.\"}},\n{\"@type\":\"Question\",\"name\":\"How often do odds change?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"On 8 October 2026, an in-play V-League board moved 144 prices a minute across the 8 books that priced it, most of them on Betfair Exchange. A pre-match Veikkausliiga board 1 hour 50 minutes from kick-off moved 68 a minute, led by Pinnacle and Polymarket.\"}},\n{\"@type\":\"Question\",\"name\":\"Can I get every tick from the stream?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The stream sends the latest state of each price. For every recorded snapshot, and for opening and closing lines, use the historical odds endpoint.\"}},\n{\"@type\":\"Question\",\"name\":\"What happens if my connection drops?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Reconnect with your stored serverEpoch and the last entryId per channel, and the gateway replays what you missed. The docs example shows a 60-second replay window. After a longer gap, the server sends snapshot_required and you re-fetch a REST snapshot.\"}}\n]}\n<\/script><\/p>\n<h2>Start With the Snapshot<\/h2>\n<p>Every streaming client needs the REST snapshot and the merge rule, and both run on a free key across 350+ bookmakers with free historical odds. Build that half now, measure your own board, and you will know from the numbers whether your strategy needs the stream.<\/p>\n<p><strong><a href=\"https:\/\/oddspapi.io\/en\/sign-up\" target=\"_blank\" rel=\"noopener\">Get your free OddsPapi API key<\/a><\/strong><\/p>\n<p><!--\nFocus Keyphrase: websocket odds api\nSEO Title: WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes\nMeta Description: A WebSocket odds API pushes each price change as it lands. On one in-play V-League board, a 60-second poll saw 438 of the 1,438 moves 4-second polls caught.\nSlug: websocket-odds-api-real-time-betting-data\nRefreshed 8 Oct 2026. Poll study 13:10-13:20 UTC on id1000062674090436 (in play) and id1000004174299684 (pre-match), 10 books requested (8 priced in play, 9 pre-match), 4 s polls; snippet output from 13:34 UTC (13:27 run: 329 pinnacle moves; 13:33 run: 0). WebSocket facts read on docs.oddspapi.io the same day; plan facts read on oddspapi.io\/en\/sign-up and \/en\/pricing. Catalogue 365 slugs \/ 285 liveOdds \/ 117 cloneOf.\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A WebSocket odds API pushes each price change as it lands. On one in-play V-League board, a 60-second poll saw 438 of the 1,438 moves 4-second polls caught.<\/p>\n","protected":false},"author":2,"featured_media":140,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[26,9,11,10,71],"class_list":["post-129","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to-guides","tag-live-odds","tag-odds-api","tag-python","tag-sports-betting-api","tag-websocket"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes | OddsPapi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes | OddsPapi Blog\" \/>\n<meta property=\"og:description\" content=\"A WebSocket odds API pushes each price change as it lands. On one in-play V-League board, a 60-second poll saw 438 of the 1,438 moves 4-second polls caught.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\" \/>\n<meta property=\"og:site_name\" content=\"OddsPapi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-01-24T14:43:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-08T13:35:36+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1856\" \/>\n\t<meta property=\"og:image:height\" content=\"576\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Odds API Writer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/oddspapi.io\/logo-v2.webp\" \/>\n<meta name=\"twitter:creator\" content=\"@oddspapiapi\" \/>\n<meta name=\"twitter:site\" content=\"@oddspapiapi\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Odds API Writer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes\",\"datePublished\":\"2026-01-24T14:43:16+00:00\",\"dateModified\":\"2026-10-08T13:35:36+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\"},\"wordCount\":1686,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg\",\"keywords\":[\"Live Odds\",\"Odds API\",\"Python\",\"Sports Betting API\",\"WebSocket\"],\"articleSection\":[\"How To Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\",\"name\":\"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes | OddsPapi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg\",\"datePublished\":\"2026-01-24T14:43:16+00:00\",\"dateModified\":\"2026-10-08T13:35:36+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg\",\"width\":1856,\"height\":576},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\",\"url\":\"https:\/\/oddspapi.io\/blog\/\",\"name\":\"OddsPapi\",\"description\":\"Sports Odds API Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"alternateName\":\"Odds Papi\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/oddspapi.io\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\",\"name\":\"OddsPapi\",\"url\":\"https:\/\/oddspapi.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png\",\"width\":135,\"height\":135,\"caption\":\"OddsPapi\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/oddspapiapi\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\",\"name\":\"Odds API Writer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g\",\"caption\":\"Odds API Writer\"},\"url\":\"https:\/\/oddspapi.io\/blog\/author\/andy-lavelle\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes | OddsPapi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/","og_locale":"en_US","og_type":"article","og_title":"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes | OddsPapi Blog","og_description":"A WebSocket odds API pushes each price change as it lands. On one in-play V-League board, a 60-second poll saw 438 of the 1,438 moves 4-second polls caught.","og_url":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/","og_site_name":"OddsPapi Blog","article_published_time":"2026-01-24T14:43:16+00:00","article_modified_time":"2026-10-08T13:35:36+00:00","og_image":[{"width":1856,"height":576,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg","type":"image\/jpeg"}],"author":"Odds API Writer","twitter_card":"summary_large_image","twitter_image":"https:\/\/oddspapi.io\/logo-v2.webp","twitter_creator":"@oddspapiapi","twitter_site":"@oddspapiapi","twitter_misc":{"Written by":"Odds API Writer","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes","datePublished":"2026-01-24T14:43:16+00:00","dateModified":"2026-10-08T13:35:36+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/"},"wordCount":1686,"commentCount":2,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg","keywords":["Live Odds","Odds API","Python","Sports Betting API","WebSocket"],"articleSection":["How To Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/","url":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/","name":"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes | OddsPapi Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg","datePublished":"2026-01-24T14:43:16+00:00","dateModified":"2026-10-08T13:35:36+00:00","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/01\/websocket-odds-api.jpg","width":1856,"height":576},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"WebSocket Odds API: Catch 1,438 Price Moves in 10 Minutes"}]},{"@type":"WebSite","@id":"https:\/\/oddspapi.io\/blog\/#website","url":"https:\/\/oddspapi.io\/blog\/","name":"OddsPapi","description":"Sports Odds API Tutorials &amp; Guides","publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"alternateName":"Odds Papi","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/oddspapi.io\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/oddspapi.io\/blog\/#organization","name":"OddsPapi","url":"https:\/\/oddspapi.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png","width":135,"height":135,"caption":"OddsPapi"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/oddspapiapi"]},{"@type":"Person","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13","name":"Odds API Writer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g","caption":"Odds API Writer"},"url":"https:\/\/oddspapi.io\/blog\/author\/andy-lavelle\/"}]}},"_links":{"self":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/129","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/comments?post=129"}],"version-history":[{"count":7,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/129\/revisions"}],"predecessor-version":[{"id":4873,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/129\/revisions\/4873"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/140"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}