{"id":44,"date":"2025-11-19T22:18:16","date_gmt":"2025-11-19T22:18:16","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=44"},"modified":"2026-10-30T15:23:48","modified_gmt":"2026-10-30T15:23:48","slug":"bet365-historical-odds-guide-the-data-apis-and-strategy","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/","title":{"rendered":"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves"},"content":{"rendered":"<p>Bet365 runs no public API, so every developer who wants its price history ends up at an aggregator. That part is settled. The part nobody writes about is what the history actually looks like once you pull it, and it does not look the way a backtest expects.<\/p>\n<p>On one fixture, Manchester United v Manchester City on 13 September 2026, the <code>\/historical-odds<\/code> endpoint returned <strong>266 bet365 markets and 230,515 timestamped price snapshots<\/strong> in a single 23.4 MB response. Dedupe those snapshots on the price and <strong>29,569 of them are real moves<\/strong>. Filter down to pre-match prices that were live and tradeable, and <strong>147,362 snapshots collapse to 3,473<\/strong>. That is a 42x gap between how often the feed wrote a row and how often bet365 changed its mind.<\/p>\n<p>Pinnacle, on the same fixture, collapses 1.7x. If you count rows instead of moves, you will rank bet365 as the busiest book on the board when it is one of the stillest. This guide shows how to pull Bet365 historical odds, how to dedupe them before you model anything, and how far back the window runs. Every number below came from a live call made while writing it. The <a href=\"https:\/\/oddspapi.io\/en\/docs\/get-historical-odds\">historical odds documentation<\/a> has the full parameter list.<\/p>\n<h2>Does Bet365 Have a Public API?<\/h2>\n<p><strong>No. Bet365 publishes no developer documentation, no self-serve key and no public endpoint.<\/strong> Searching for &#8220;Bet365 API documentation&#8221; or &#8220;Bet365 API key&#8221; turns up nothing official, because nothing official exists for third-party developers.<\/p>\n<p>Bet365 keeps its feeds inside three channels: affiliate partners on revenue-share agreements, negotiated enterprise licensing, and its own apps. None of the three has a sign-up page.<\/p>\n<p>That leaves three routes to the data, and two of them are dead ends.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th>Standing<\/th>\n<th>Reliability<\/th>\n<th>Verdict<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Web scraping<\/strong><\/td>\n<td>Violates Bet365 terms of service<\/td>\n<td>Breaks on every markup change<\/td>\n<td>Avoid<\/td>\n<\/tr>\n<tr>\n<td><strong>Official partnership<\/strong><\/td>\n<td>Legitimate<\/td>\n<td>Negotiated, enterprise only<\/td>\n<td>Closed to most developers<\/td>\n<\/tr>\n<tr>\n<td><strong>Odds aggregator API<\/strong><\/td>\n<td>Legitimate<\/td>\n<td>Documented REST and WebSocket<\/td>\n<td>The working route<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>An aggregator collects bet365 prices alongside the rest of the market and hands them back under one schema. OddsPapi carries <strong>300+ bookmaker slugs<\/strong> as of 16 September 2026, of which 285 carry live in-play odds and 117 are flagged as clones of another feed. Bet365 is one of them, and its history is free on the free plan. For the live side of the same feed, see <a href=\"https:\/\/oddspapi.io\/blog\/live-betting-api-real-time-in-play-odds\/\">real-time odds for in-play markets<\/a>; for why direct access stays shut, see <a href=\"https:\/\/oddspapi.io\/blog\/bet365-api-odds-access\/\">the book everyone wants and nobody can query<\/a>, and for bet365 alongside the rest of the board, <a href=\"https:\/\/oddspapi.io\/blog\/sportsbook-api-350-bookmakers\/\">the sportsbook API guide<\/a>.<\/p>\n<h2>Pull Live Bet365 Prices First<\/h2>\n<p>Start on the live endpoint. It confirms your key works and shows you the market structure you will meet again in the history.<\/p>\n<pre class=\"wp-block-code\"><code>import os, requests, time\nfrom datetime import date, timedelta\n\nAPI_KEY = os.environ[\"ODDSPAPI_API_KEY\"]\nBASE_URL = \"https:\/\/api.oddspapi.io\/v4\"\n\ndef call(path, **params):\n    \"\"\"One call with a 429 retry. A 429 body is valid JSON, so check the status first.\"\"\"\n    params[\"apiKey\"] = API_KEY\n    for _ in range(5):\n        r = requests.get(f\"{BASE_URL}\/{path}\", params=params, timeout=120)\n        if r.status_code == 429:\n            time.sleep(float(r.json()[\"error\"].get(\"retryMs\", 3000)) \/ 1000 + 1)\n            continue\n        return r\n    return r\n\ntoday = date.today()\nfixtures = call(\n    \"fixtures\",\n    sportId=10,          # soccer\n    tournamentId=17,     # Premier League\n    **{\"from\": today.isoformat(), \"to\": (today + timedelta(days=8)).isoformat()},\n).json()\n\nfixture = fixtures[0]\ntime.sleep(1.0)\n\nodds = call(\"odds\", fixtureId=fixture[\"fixtureId\"], bookmakers=\"bet365\").json()\nbook = odds[\"bookmakerOdds\"][\"bet365\"]\nmarket = book[\"markets\"][\"101\"]          # Full Time Result\n\nprint(f\"{fixture['participant1Name']} v {fixture['participant2Name']}\")\nprint(f\"bet365 markets on this fixture: {len(book['markets'])}\")\n\nNAMES = {\"101\": \"home\", \"102\": \"draw\", \"103\": \"away\"}\nimplied = 0.0\nfor outcome_id, outcome in market[\"outcomes\"].items():\n    price = outcome[\"players\"][\"0\"]\n    if not price[\"active\"]:\n        continue\n    print(f\"  {NAMES[outcome_id]:5} {price['price']:&gt;6}  changed {price['changedAt']}\")\n    implied += 1 \/ price[\"price\"]\n\nprint(f\"  overround {(implied - 1) * 100:.2f}%\")\n<\/code><\/pre>\n<p>Run against Brentford v Chelsea on 16 September 2026, three days before kick-off:<\/p>\n<pre class=\"wp-block-code\"><code>Brentford FC v Chelsea FC\nbet365 markets on this fixture: 225\n  draw     3.7  changed 2026-09-16T10:46:57.799Z\n  home    2.87  changed 2026-09-16T10:46:57.799Z\n  away     2.3  changed 2026-09-16T10:46:57.799Z\n  overround 5.35%\n<\/code><\/pre>\n<p>Two things in that output catch people out. The outcome dictionary came back in the order draw, home, away, so position tells you nothing and you must key on the outcome ID. And <code>tournamentId<\/code> is undocumented on <code>\/fixtures<\/code>, works, cuts the payload roughly twentyfold, and lifts the ten-day range cap that <code>sportId<\/code> alone imposes.<\/p>\n<h3>Ten Bet365 Slugs, Three Different Prices<\/h3>\n<p>The catalogue holds ten bet365 feeds: <code>bet365<\/code>, <code>bet365-nj<\/code>, <code>bet365.bet.ar<\/code>, <code>bet365.bet.br<\/code>, <code>bet365.de<\/code>, <code>bet365.es<\/code>, <code>bet365.fr<\/code>, <code>bet365.gr<\/code>, <code>bet365.it<\/code> and <code>bet365.nl<\/code>. All ten priced 225 markets on Brentford v Chelsea. They did not all quote the same number.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Slugs<\/th>\n<th>Home \/ Draw \/ Away<\/th>\n<th>Overround<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>bet365<\/code>, <code>bet365-nj<\/code>, <code>bet365.de<\/code>, <code>bet365.it<\/code><\/td>\n<td>2.87 \/ 3.70 \/ 2.30<\/td>\n<td>5.35%<\/td>\n<\/tr>\n<tr>\n<td><code>bet365.bet.ar<\/code>, <code>bet365.bet.br<\/code>, <code>bet365.es<\/code>, <code>bet365.gr<\/code>, <code>bet365.nl<\/code><\/td>\n<td>2.82 \/ 3.70 \/ 2.25<\/td>\n<td>6.93%<\/td>\n<\/tr>\n<tr>\n<td><code>bet365.fr<\/code><\/td>\n<td>2.79 \/ 3.60 \/ 2.24<\/td>\n<td>8.26%<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Ten feeds, three prices. The French skin charges 2.9 points more margin than the flagship on the same match. Only <code>bet365-nj<\/code> carries <code>cloneOf: \"bet365\"<\/code>, so the <code>.de<\/code> and <code>.it<\/code> feeds quote byte-identical prices while the catalogue calls them independent. Dedupe on the price tuple before you count books or build a consensus, and never lean on <code>cloneOf<\/code> to do it for you. The method is in <a href=\"https:\/\/oddspapi.io\/blog\/consensus-odds-fair-odds-calculator-python\/\">calculating fair odds from 300+ bookmakers<\/a>.<\/p>\n<h2>Snapshots Are Not Line Moves<\/h2>\n<p>Now the history. The endpoint swaps two things on you: the top-level key becomes <code>bookmakers<\/code> instead of <code>bookmakerOdds<\/code>, and <code>players[\"0\"]<\/code> becomes a list of timestamped snapshots instead of a single price object.<\/p>\n<p>Test it on a fixture that has finished. A live <code>\/odds<\/code> call outside the market-open window reports an empty board and reads as missing coverage when the real cause is the clock.<\/p>\n<pre class=\"wp-block-code\"><code>import os, requests\nfrom datetime import datetime, timezone\n\nAPI_KEY = os.environ[\"ODDSPAPI_API_KEY\"]\nBASE_URL = \"https:\/\/api.oddspapi.io\/v4\"\n\nFIXTURE = \"id1000001772221266\"   # Manchester United v Manchester City, 13 Sep 2026\nKICKOFF = datetime(2026, 9, 13, 15, 30, tzinfo=timezone.utc)\n\nr = requests.get(\n    f\"{BASE_URL}\/historical-odds\",\n    params={\"apiKey\": API_KEY, \"fixtureId\": FIXTURE, \"bookmakers\": \"bet365\"},\n    timeout=300,\n)\nr.raise_for_status()\n\nmarket = r.json()[\"bookmakers\"][\"bet365\"][\"markets\"][\"101\"]\n\ndef stamp(s):\n    return datetime.fromisoformat(s.replace(\"Z\", \"+00:00\"))\n\nNAMES = {\"101\": \"home\", \"102\": \"draw\", \"103\": \"away\"}\nfor outcome_id, outcome in market[\"outcomes\"].items():\n    snapshots = outcome[\"players\"][\"0\"]\n    pre_match = [s for s in snapshots\n                 if stamp(s[\"createdAt\"]) &lt; KICKOFF and s[\"active\"]]\n\n    # The feed writes a row whether or not the price moved. Collapse the repeats\n    # before you count a line move, or you measure the poller, not the book.\n    moves, previous = [], None\n    for s in pre_match:\n        if s[\"price\"] != previous:\n            moves.append(s)\n            previous = s[\"price\"]\n\n    print(\n        f\"{NAMES[outcome_id]:5} {len(snapshots):&gt;5} snapshots  \"\n        f\"{len(pre_match):&gt;4} pre-match  {len(moves):&gt;3} price moves  \"\n        f\"open {moves[0]['price']} -&gt; close {moves[-1]['price']}\"\n    )\n    print(f\"      opened {moves[0]['createdAt']}\")\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>home    666 snapshots   476 pre-match    9 price moves  open 3.1 -&gt; close 3.0\n      opened 2026-08-30T20:49:13.579Z\ndraw    629 snapshots   471 pre-match    5 price moves  open 3.6 -&gt; close 3.75\n      opened 2026-08-30T20:49:13.579Z\naway    676 snapshots   507 pre-match   42 price moves  open 2.05 -&gt; close 2.2\n      opened 2026-08-30T20:49:13.579Z\n<\/code><\/pre>\n<p>Read the draw leg. bet365 wrote 471 pre-match rows on it and changed the price five times in fourteen days. The away leg carried the action: 42 moves, walking City from 2.05 out to 2.20.<\/p>\n<p>Scale that up and the shape holds across the whole book. Here is the same collapse run over all 266 markets and all 1,058 price legs, next to Pinnacle and DraftKings on the identical fixture.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Book<\/th>\n<th>Markets<\/th>\n<th>Snapshots<\/th>\n<th>Price moves<\/th>\n<th>Ratio<\/th>\n<th>Pre-match active ratio<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>bet365<\/code><\/td>\n<td>266<\/td>\n<td>230,515<\/td>\n<td>29,569<\/td>\n<td>7.8x<\/td>\n<td><strong>42.4x<\/strong><\/td>\n<\/tr>\n<tr>\n<td><code>pinnacle<\/code><\/td>\n<td>171<\/td>\n<td>91,704<\/td>\n<td>61,161<\/td>\n<td>1.5x<\/td>\n<td>1.7x<\/td>\n<\/tr>\n<tr>\n<td><code>draftkings<\/code><\/td>\n<td>175<\/td>\n<td>92,757<\/td>\n<td>35,687<\/td>\n<td>2.6x<\/td>\n<td>2.6x<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>bet365 shipped the widest menu and the stillest prices. Pinnacle shipped fewer markets and changed almost every one of them on almost every row. Any model that treats snapshot volume as a proxy for line movement inverts that ranking. Collapse first, then count.<\/p>\n<h2>Opening and Closing Lines Across Three Books<\/h2>\n<p><code>\/historical-odds<\/code> accepts a maximum of three bookmakers per call, and one unreadable slug fails the whole batch. Pull one slug at a time and merge in your own code.<\/p>\n<pre class=\"wp-block-code\"><code>import os, requests, time\nfrom datetime import datetime, timezone\n\nAPI_KEY = os.environ[\"ODDSPAPI_API_KEY\"]\nBASE_URL = \"https:\/\/api.oddspapi.io\/v4\"\n\nFIXTURE = \"id1000001772221266\"\nKICKOFF = datetime(2026, 9, 13, 15, 30, tzinfo=timezone.utc)\n\ndef stamp(s):\n    return datetime.fromisoformat(s.replace(\"Z\", \"+00:00\"))\n\ndef opening_and_closing(slug):\n    r = requests.get(\n        f\"{BASE_URL}\/historical-odds\",\n        params={\"apiKey\": API_KEY, \"fixtureId\": FIXTURE, \"bookmakers\": slug},\n        timeout=300,\n    )\n    if r.status_code != 200:\n        return None\n    market = r.json()[\"bookmakers\"][slug][\"markets\"][\"101\"]\n    row = {}\n    for outcome_id, outcome in market[\"outcomes\"].items():\n        # The close is the last ACTIVE snapshot BEFORE kick-off. Snapshots keep\n        # running past kick-off, so filter on both conditions.\n        pre = [s for s in outcome[\"players\"][\"0\"]\n               if stamp(s[\"createdAt\"]) &lt; KICKOFF and s[\"active\"]]\n        row[outcome_id] = (pre[0], pre[-1])\n    return row\n\nfor slug in [\"bet365\", \"pinnacle\", \"draftkings\"]:\n    row = opening_and_closing(slug)\n    opens = [row[o][0][\"price\"] for o in (\"101\", \"102\", \"103\")]\n    closes = [row[o][1][\"price\"] for o in (\"101\", \"102\", \"103\")]\n    open_margin = (sum(1 \/ p for p in opens) - 1) * 100\n    close_margin = (sum(1 \/ p for p in closes) - 1) * 100\n    print(f\"{slug:11} opened {row['101'][0]['createdAt'][:16]}  \"\n          f\"{open_margin:5.2f}%  -&gt;  {close_margin:5.2f}%  \"\n          f\"closing limit {row['101'][1]['limit']}\")\n    time.sleep(4.6)   # \/historical-odds wants 4.6 s between calls\n<\/code><\/pre>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Book<\/th>\n<th>Opened<\/th>\n<th>Opening 1X2<\/th>\n<th>Margin<\/th>\n<th>Closing 1X2<\/th>\n<th>Margin<\/th>\n<th>Closing limit<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>bet365<\/code><\/td>\n<td>30 Aug 20:49Z<\/td>\n<td>3.10 \/ 3.60 \/ 2.05<\/td>\n<td>8.82%<\/td>\n<td>3.00 \/ 3.75 \/ 2.20<\/td>\n<td>5.45%<\/td>\n<td>null<\/td>\n<\/tr>\n<tr>\n<td><code>pinnacle<\/code><\/td>\n<td>30 Aug 20:05Z<\/td>\n<td>3.11 \/ 3.65 \/ 2.14<\/td>\n<td>6.28%<\/td>\n<td>3.07 \/ 3.69 \/ 2.32<\/td>\n<td><strong>2.78%<\/strong><\/td>\n<td>10,000<\/td>\n<\/tr>\n<tr>\n<td><code>draftkings<\/code><\/td>\n<td>10 Sep 14:06Z<\/td>\n<td>3.05 \/ 3.80 \/ 2.15<\/td>\n<td>5.61%<\/td>\n<td>3.00 \/ 3.75 \/ 2.25<\/td>\n<td>4.44%<\/td>\n<td>null<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>bet365 and Pinnacle opened the derby 44 minutes apart, both on 30 August, fourteen days out. DraftKings waited until 10 September. bet365 shaved 3.4 points of margin between open and close and still finished 2.7 points wider than Pinnacle.<\/p>\n<p>The <code>limit<\/code> column matters more than it looks. Pinnacle populates it and bet365 leaves it null, so null-check before any arithmetic. Pinnacle&#8217;s home leg opened with a limit of 250 and closed at 10,000: the same price carries a different meaning at each end of that range. A historical price without its limit tells you half the story. For the wider lag picture, <a href=\"https:\/\/oddspapi.io\/blog\/soft-book-lag-pinnacle\/\">1xBet follows Pinnacle in 6 minutes and bet365 in 67<\/a>.<\/p>\n<h2>How Far Back the Window Runs<\/h2>\n<p>Check the window before you scope a backtest. The endpoint holds a rolling window rather than a permanent archive, and the answer is a single probe.<\/p>\n<pre class=\"wp-block-code\"><code>import os, requests, time\nfrom datetime import date, timedelta\n\nAPI_KEY = os.environ[\"ODDSPAPI_API_KEY\"]\nBASE_URL = \"https:\/\/api.oddspapi.io\/v4\"\n\ndef call(path, **params):\n    params[\"apiKey\"] = API_KEY\n    for _ in range(5):\n        r = requests.get(f\"{BASE_URL}\/{path}\", params=params, timeout=300)\n        if r.status_code == 429:\n            time.sleep(float(r.json()[\"error\"].get(\"retryMs\", 3000)) \/ 1000 + 1)\n            continue\n        return r\n    return r\n\ndef first_fixture(day):\n    r = call(\"fixtures\", sportId=10, tournamentId=17,\n             **{\"from\": day.isoformat(), \"to\": (day + timedelta(days=3)).isoformat()})\n    return r.json()[0] if r.status_code == 200 and r.json() else None\n\nprobe = date(2026, 9, 13)\nfor _ in range(12):\n    fixture = first_fixture(probe)\n    time.sleep(1.0)\n    if fixture is None:                    # international break, no fixtures\n        probe -= timedelta(days=30)\n        continue\n    r = call(\"historical-odds\", fixtureId=fixture[\"fixtureId\"], bookmakers=\"bet365\")\n    if r.status_code == 200:\n        markets = r.json()[\"bookmakers\"][\"bet365\"][\"markets\"]\n        snaps = sum(len(s) for m in markets.values()\n                    for o in m[\"outcomes\"].values() for s in o[\"players\"].values())\n        print(f\"{fixture['startTime'][:10]}  {len(markets):&gt;3} markets  {snaps:&gt;7,} snapshots\")\n    else:\n        print(f\"{fixture['startTime'][:10]}  {r.status_code} \"\n              f\"{r.json()['error']['code']}  &lt;- edge of the window\")\n        break\n    time.sleep(4.6)\n    probe -= timedelta(days=30)\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>2026-09-13  259 markets  379,820 snapshots\n2026-05-17   12 markets      125 snapshots\n2026-04-18   10 markets      226 snapshots\n2026-01-17  111 markets    6,050 snapshots\n2025-10-18  404 NOT_FOUND  &lt;- edge of the window\n<\/code><\/pre>\n<p>Run on 16 September 2026, Premier League history reached back to a 17 January 2026 fixture, roughly eight months. The same walk returns 404 <code>NOT_FOUND<\/code> on fixtures from 3 January 2026 and earlier. That boundary belongs to the fixture, not to bet365: repeat the call with <code>pinnacle<\/code> or <code>draftkings<\/code> on the same fixture IDs and you get the identical 404, so read it as the edge of the collection window.<\/p>\n<p>Depth inside the window varies by fixture as well. The derby held 266 bet365 markets and a May fixture held twelve. Probe the exact fixtures your model needs before you plan around a number. Once you have the window, <a href=\"https:\/\/oddspapi.io\/blog\/backtest-betting-model-free-historical-odds\/\">backtesting a model on free historical odds<\/a> and <a href=\"https:\/\/oddspapi.io\/blog\/historical-odds-csv-excel-backtesting\/\">exporting to CSV or Excel<\/a> pick up where this leaves off.<\/p>\n<h2>Two Bet365 Flags That Break Readers<\/h2>\n<p><strong>Every bet365 slug carries <code>liveOdds: false<\/code> in the catalogue.<\/strong> All ten of them. Read that as a pre-match capability flag rather than a promise about in-play rows, because the in-play prices keep arriving regardless. A reader that skips a book on <code>liveOdds<\/code> drops bet365 from your in-play data entirely.<\/p>\n<p><strong>A suspended bet365 price reads <code>\"price\": 1.0, \"active\": false<\/code>.<\/strong> On the derby, each 1X2 leg carried 63 suspended pre-match snapshots, and the final row of the fixture was a 1.0 at 17:22Z. Feed a 1.0 into an implied-probability calculation and you get 100% on a single leg. Filter on <code>active<\/code> at price level before any maths.<\/p>\n<p>The bookmakers filter answers with typed errors, which tells you which problem you have:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Call<\/th>\n<th>Response<\/th>\n<th>Meaning<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>bookmakers=bet365<\/code><\/td>\n<td>200 with the slug present<\/td>\n<td>Priced and readable<\/td>\n<\/tr>\n<tr>\n<td><code>bookmakers=bet365-uk<\/code><\/td>\n<td>400 <code>INVALID_PARAMETER<\/code><\/td>\n<td>No such slug; the body lists the valid ones<\/td>\n<\/tr>\n<tr>\n<td><code>bookmakers=vbet<\/code><\/td>\n<td>403 <code>RESTRICTED_ACCESS<\/code><\/td>\n<td>Real slug, gated on your plan<\/td>\n<\/tr>\n<tr>\n<td>200 with the slug absent<\/td>\n<td>200<\/td>\n<td>Readable, and this book has no price up<\/td>\n<\/tr>\n<tr>\n<td>Four slugs on <code>\/historical-odds<\/code><\/td>\n<td>400 <code>TOO_MANY_BOOKMAKERS<\/code><\/td>\n<td>Three per call is the cap<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>The Old Way and the Aggregator Way<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Task<\/th>\n<th>Scraping bet365 yourself<\/th>\n<th>OddsPapi<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Getting a price<\/td>\n<td>Headless browser, rotating proxies, ToS breach<\/td>\n<td>One GET with a query-string key<\/td>\n<\/tr>\n<tr>\n<td>Price history<\/td>\n<td>Whatever you captured, starting today<\/td>\n<td>Timestamped snapshots on the free plan<\/td>\n<\/tr>\n<tr>\n<td>Other books<\/td>\n<td>One scraper per book<\/td>\n<td>300+ slugs under one schema<\/td>\n<\/tr>\n<tr>\n<td>Asian handicaps and player props<\/td>\n<td>Reverse-engineer each layout<\/td>\n<td>Native market structures with stable IDs<\/td>\n<\/tr>\n<tr>\n<td>Real-time delivery<\/td>\n<td>Poll and hope<\/td>\n<td>WebSocket streaming<\/td>\n<\/tr>\n<tr>\n<td>Cost to start<\/td>\n<td>Your weekends<\/td>\n<td>Free plan, 250 requests a month<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>The four things worth the switch: <strong>300+ bookmakers<\/strong> under one schema, <strong>free historical odds<\/strong> rather than a sales call, <strong>WebSocket streaming<\/strong> for push delivery, and <strong>native market structures<\/strong> so an Asian handicap grades as an Asian handicap. Pricing across the providers sits in <a href=\"https:\/\/oddspapi.io\/blog\/odds-api-pricing-2026-comparison\/\">the 2026 odds API pricing comparison<\/a>, and the push side in <a href=\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\">the WebSocket odds API guide<\/a>.<\/p>\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"https:\/\/oddspapi.io\/\">Get a free API key and pull Bet365 history<\/a><\/div>\n<h2>Frequently Asked Questions<\/h2>\n<h3>Does Bet365 have a public API?<\/h3>\n<p>No. Bet365 publishes no developer documentation and no self-serve key. Its feeds go to affiliate partners, negotiated enterprise licences and its own apps. Developers read bet365 prices through an aggregator, which normalises them alongside 300+ other bookmakers under one schema.<\/p>\n<h3>Is Bet365 historical odds data free?<\/h3>\n<p>On OddsPapi, yes. The <code>\/historical-odds<\/code> endpoint sits on the free plan, so you can pull timestamped bet365 price snapshots and backtest against them before you pay anything. Most competitors put historical data behind a paid tier or a sales call.<\/p>\n<h3>How far back does Bet365 historical odds data go?<\/h3>\n<p>The endpoint serves a rolling window. Measured on 16 September 2026, Premier League history reached back to a 17 January 2026 fixture, about eight months. The edge belongs to the fixture rather than to bet365, since Pinnacle and DraftKings return the same boundary on the same fixture IDs. Walk your target dates with a probe call before you scope a backtest.<\/p>\n<h3>Why do I get so many snapshots per fixture?<\/h3>\n<p>The feed writes a row on every observation whether or not the price moved. One bet365 fixture returned 230,515 snapshots that dedupe to 29,569 actual price changes, and 42 to 1 on pre-match active prices alone. Collapse consecutive identical prices before you count line movement, or you will measure the polling rate instead of the book.<\/p>\n<h3>Which bet365 slug should I query?<\/h3>\n<p>The catalogue carries ten bet365 feeds. On a Premier League fixture in September 2026 they quoted three distinct 1X2 prices: <code>bet365<\/code>, <code>bet365-nj<\/code>, <code>bet365.de<\/code> and <code>bet365.it<\/code> at 5.35% overround, five regional skins at 6.93%, and <code>bet365.fr<\/code> at 8.26%. Query <code>bet365<\/code> for the flagship line, and dedupe on the price tuple if you pull several, because <code>cloneOf<\/code> flags only one of the three duplicate feeds.<\/p>\n<h3>Can I get live Bet365 odds as well as history?<\/h3>\n<p>Yes. The <code>\/odds<\/code> endpoint returns current bet365 prices under the same fixture and market IDs, so code written against one endpoint mostly carries to the other. Two differences: the live response nests under <code>bookmakerOdds<\/code> and the historical one under <code>bookmakers<\/code>, and <code>players[\"0\"]<\/code> holds a single price object live and a list of snapshots historically. WebSocket streaming covers push delivery.<\/p>\n<h3>How do I export Bet365 historical odds to CSV?<\/h3>\n<p>The API returns JSON, which preserves the nested market structure. Flatten the snapshot lists into rows and write them with pandas: <code>pd.DataFrame(rows).to_csv(\"bet365.csv\", index=False)<\/code>. Dedupe on the price before you export, or your CSV inherits the 42 to 1 snapshot inflation.<\/p>\n<h3>How many bookmakers can I pull per historical call?<\/h3>\n<p>Three. A fourth returns 400 <code>TOO_MANY_BOOKMAKERS<\/code>, and one unreadable slug fails the whole batch, so pull one slug per call and merge the results yourself. Sleep 4.6 seconds between historical calls to stay inside the rate limit.<\/p>\n<p><script type=\"application\/ld+json\">\n{\"@context\":\"https:\/\/schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[\n{\"@type\":\"Question\",\"name\":\"Does Bet365 have a public API?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"No. Bet365 publishes no developer documentation and no self-serve key. Its feeds go to affiliate partners, negotiated enterprise licences and its own apps. Developers read bet365 prices through an aggregator, which normalises them alongside 300+ other bookmakers under one schema.\"}},\n{\"@type\":\"Question\",\"name\":\"Is Bet365 historical odds data free?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"On OddsPapi, yes. The historical-odds endpoint sits on the free plan, so you can pull timestamped bet365 price snapshots and backtest against them before you pay anything.\"}},\n{\"@type\":\"Question\",\"name\":\"How far back does Bet365 historical odds data go?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The endpoint serves a rolling window. Measured on 16 September 2026, Premier League history reached back to a 17 January 2026 fixture, about eight months. Walk your target dates with a probe call before you scope a backtest.\"}},\n{\"@type\":\"Question\",\"name\":\"Why do I get so many snapshots per fixture?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The feed writes a row on every observation whether or not the price moved. One bet365 fixture returned 230,515 snapshots that dedupe to 29,569 actual price changes. Collapse consecutive identical prices before you count line movement.\"}},\n{\"@type\":\"Question\",\"name\":\"Which bet365 slug should I query?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The catalogue carries ten bet365 feeds. On a Premier League fixture in September 2026 they quoted three distinct 1X2 prices at 5.35%, 6.93% and 8.26% overround. Query bet365 for the flagship line, and dedupe on the price tuple if you pull several.\"}},\n{\"@type\":\"Question\",\"name\":\"Can I get live Bet365 odds as well as history?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes. The odds endpoint returns current bet365 prices under the same fixture and market IDs. The live response nests under bookmakerOdds and the historical one under bookmakers, and players zero holds a single price object live and a list of snapshots historically.\"}},\n{\"@type\":\"Question\",\"name\":\"How do I export Bet365 historical odds to CSV?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The API returns JSON. Flatten the snapshot lists into rows and write them with pandas to_csv. Dedupe on the price before you export, or your CSV inherits the snapshot inflation.\"}},\n{\"@type\":\"Question\",\"name\":\"How many bookmakers can I pull per historical call?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Three. A fourth returns 400 TOO_MANY_BOOKMAKERS, and one unreadable slug fails the whole batch, so pull one slug per call and merge the results yourself.\"}}]}\n<\/script><\/p>\n<p><!--\nFocus Keyphrase: bet365 historical odds\nSEO Title: Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves\nMeta Description: One Manchester derby returns 266 bet365 markets. Pre-match, 147,362 active snapshots dedupe to 3,473 price moves, against Pinnacle's 1.7x ratio.\nSlug: bet365-historical-odds-guide-the-data-apis-and-strategy\nAll figures measured live 16 Sep 2026. Catalogue 360 slugs \/ 285 liveOdds \/ 117 cloneOf.\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>One Manchester derby returns 266 bet365 markets. Pre-match, 147,362 active snapshots dedupe to 3,473 price moves, against Pinnacle&#8217;s 1.7x ratio.<\/p>\n","protected":false},"author":2,"featured_media":46,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,1],"tags":[3,4],"class_list":["post-44","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-historical-odds","category-uncategorized","tag-bet365","tag-historical-odds"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves | OddsPapi Blog<\/title>\n<meta name=\"description\" content=\"One Manchester derby returns 266 bet365 markets. Pre-match, 147,362 active snapshots dedupe to 3,473 price moves, against Pinnacle&#039;s 1.7x ratio.\" \/>\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\/bet365-historical-odds-guide-the-data-apis-and-strategy\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves | OddsPapi Blog\" \/>\n<meta property=\"og:description\" content=\"One Manchester derby returns 266 bet365 markets. Pre-match, 147,362 active snapshots dedupe to 3,473 price moves, against Pinnacle&#039;s 1.7x ratio.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/\" \/>\n<meta property=\"og:site_name\" content=\"OddsPapi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-11-19T22:18:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-30T15:23:48+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"430\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\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=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves\",\"datePublished\":\"2025-11-19T22:18:16+00:00\",\"dateModified\":\"2026-10-30T15:23:48+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/\"},\"wordCount\":1841,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png\",\"keywords\":[\"bet365\",\"historical odds\"],\"articleSection\":[\"Historical Odds\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/\",\"name\":\"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves | OddsPapi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png\",\"datePublished\":\"2025-11-19T22:18:16+00:00\",\"dateModified\":\"2026-10-30T15:23:48+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png\",\"width\":1024,\"height\":430},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves\"}]},{\"@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":"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves | OddsPapi Blog","description":"One Manchester derby returns 266 bet365 markets. Pre-match, 147,362 active snapshots dedupe to 3,473 price moves, against Pinnacle's 1.7x ratio.","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\/bet365-historical-odds-guide-the-data-apis-and-strategy\/","og_locale":"en_US","og_type":"article","og_title":"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves | OddsPapi Blog","og_description":"One Manchester derby returns 266 bet365 markets. Pre-match, 147,362 active snapshots dedupe to 3,473 price moves, against Pinnacle's 1.7x ratio.","og_url":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/","og_site_name":"OddsPapi Blog","article_published_time":"2025-11-19T22:18:16+00:00","article_modified_time":"2026-10-30T15:23:48+00:00","og_image":[{"width":1024,"height":430,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png","type":"image\/png"}],"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":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves","datePublished":"2025-11-19T22:18:16+00:00","dateModified":"2026-10-30T15:23:48+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/"},"wordCount":1841,"commentCount":1,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png","keywords":["bet365","historical odds"],"articleSection":["Historical Odds"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/","url":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/","name":"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves | OddsPapi Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png","datePublished":"2025-11-19T22:18:16+00:00","dateModified":"2026-10-30T15:23:48+00:00","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/bet365-historical-odds-guide.png","width":1024,"height":430},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/bet365-historical-odds-guide-the-data-apis-and-strategy\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"Bet365 Historical Odds: Dedupe 230,515 Snapshots to 29,569 Moves"}]},{"@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\/44","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=44"}],"version-history":[{"count":13,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/44\/revisions"}],"predecessor-version":[{"id":5695,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/44\/revisions\/5695"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/46"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=44"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=44"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=44"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}