{"id":4163,"date":"2026-09-24T10:00:00","date_gmt":"2026-09-24T10:00:00","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=4163"},"modified":"2026-09-15T13:17:06","modified_gmt":"2026-09-15T13:17:06","slug":"nba-alternate-lines-api","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/","title":{"rendered":"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field"},"content":{"rendered":"<p>You want every NBA spread and total, not just the one the book shows on its front page. OddsPapi ships them as separate market IDs: <strong>401 rungs on the full-game total and 321 on the full-game handicap, 722 in all<\/strong>. The line itself lives in the <code>handicap<\/code> field of <code>\/v4\/markets<\/code>, never on the price.<\/p>\n<p>Sort those rungs by <code>marketId<\/code> and you get a ladder that is out of order. Measured on 15 September 2026 at 13:14 UTC, the market ID order and the line order agree on <strong>81 of the 401 total rungs and 80 of the 321 spread rungs<\/strong>. Everything below comes from live calls made that afternoon on the free tier.<\/p>\n<h2>Why the Market ID Is Not the Line<\/h2>\n<p>OddsPapi assigns market IDs in blocks, and it added those blocks at different times. On the NBA total ladder the half-point rungs from 100.5 to 259.5 sit in one block of five-digit IDs. Every whole-point rung, plus the half points above 259.5, sits in a six-digit block that starts at 112878. Walk the ladder by ID and it climbs 100.5, 101.5, 102.5 all the way to 259.5, then drops back to 100.0 and climbs again.<\/p>\n<p>The spread ladder splits three ways. IDs 11356 to 11556 hold the rungs from -25.0 to +25.0. The outer rungs sit above 113360. An ID walk jumps backwards twice: from +25.0 to -40.0, then from +40.0 to -80.0.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Line<\/th>\n<th>Market ID<\/th>\n<th>Outcome array as shipped<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>220.0<\/td>\n<td>113118<\/td>\n<td>Under, Over<\/td>\n<\/tr>\n<tr>\n<td>220.5<\/td>\n<td>11246<\/td>\n<td>Over, Under<\/td>\n<\/tr>\n<tr>\n<td>221.0<\/td>\n<td>113120<\/td>\n<td>Over, Under<\/td>\n<\/tr>\n<tr>\n<td>221.5<\/td>\n<td>11248<\/td>\n<td>Over, Under<\/td>\n<\/tr>\n<tr>\n<td>222.0<\/td>\n<td>113122<\/td>\n<td>Under, Over<\/td>\n<\/tr>\n<tr>\n<td>222.5<\/td>\n<td>11250<\/td>\n<td>Over, Under<\/td>\n<\/tr>\n<tr>\n<td>223.0<\/td>\n<td>113124<\/td>\n<td>Over, Under<\/td>\n<\/tr>\n<tr>\n<td>223.5<\/td>\n<td>11252<\/td>\n<td>Under, Over<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Two traps in one table. The IDs alternate between blocks as the line steps by half a point, and the outcome array order changes with it. Across all 401 total rungs, 198 list Over first and 203 list Under first. On the spread ladder, 163 rungs list participant 1 first and 158 list participant 2 first. Read the label, never the array position.<\/p>\n<p>One more: all 4,902 basketball market definitions reuse the market ID as one of their own outcome IDs. Market 11248 has outcome 11248. Key a dictionary on the bare integer and the total ladder collides with itself.<\/p>\n<h2>Old Way vs OddsPapi<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Task<\/th>\n<th>Scraping a sportsbook<\/th>\n<th>OddsPapi<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Alt spread and total rungs<\/td>\n<td>Render the alt-lines drawer, parse it per book<\/td>\n<td>722 rungs from one <code>\/v4\/markets<\/code> call<\/td>\n<\/tr>\n<tr>\n<td>Line value<\/td>\n<td>Regex the label text<\/td>\n<td><code>handicap<\/code>, typed as a float<\/td>\n<\/tr>\n<tr>\n<td>Cross-book comparison<\/td>\n<td>Rebuild each book&#8217;s ladder by hand<\/td>\n<td>One <code>\/v4\/odds<\/code> call, 220 books keyed by slug<\/td>\n<\/tr>\n<tr>\n<td>Historical rungs<\/td>\n<td>Gone once the fixture settles<\/td>\n<td><code>\/v4\/historical-odds<\/code>, free on every tier<\/td>\n<\/tr>\n<tr>\n<td>Live updates<\/td>\n<td>Re-poll the whole drawer<\/td>\n<td>WebSocket push on <code>wss:\/\/v5.oddspapi.io\/ws<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>Step 1: Authenticate and Pull the Ladder<\/h2>\n<p>The key travels as a query parameter. It is never a header.<\/p>\n<pre class=\"wp-block-code\"><code>import os, requests\n\nAPI_KEY = os.environ[\"ODDSPAPI_API_KEY\"]\nBASE = \"https:\/\/api.oddspapi.io\/v4\"\nBASKETBALL = 11\n\ndefs = requests.get(f\"{BASE}\/markets\", params={\"apiKey\": API_KEY}).json()\nhoops = [m for m in defs if m.get(\"sportId\") == BASKETBALL]\nprint(f\"basketball market definitions: {len(hoops)}\")\n\ntotals = [m for m in hoops if m[\"marketName\"] == \"Over Under (incl. overtime)\"]\nspreads = [m for m in hoops if m[\"marketName\"] == \"Handicap (incl. overtime)\"]\nprint(f\"total rungs: {len(totals)}  spread rungs: {len(spreads)}\")\n\nby_id = sorted(totals, key=lambda m: m[\"marketId\"])\nby_line = sorted(totals, key=lambda m: m[\"handicap\"])\nprint(\"sorted by marketId:\", [m[\"handicap\"] for m in by_id[:5]])\nprint(\"sorted by handicap:\", [m[\"handicap\"] for m in by_line[:5]])\n\nagree = sum(1 for a, b in zip(by_id, by_line) if a[\"marketId\"] == b[\"marketId\"])\nprint(f\"positions where the two orders agree: {agree} of {len(totals)}\")<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>basketball market definitions: 4902\ntotal rungs: 401  spread rungs: 321\nsorted by marketId: [100.5, 101.5, 102.5, 103.5, 104.5]\nsorted by handicap: [100.0, 100.5, 101.0, 101.5, 102.0]\npositions where the two orders agree: 81 of 401<\/code><\/pre>\n<p><code>\/v4\/markets<\/code> ignores a <code>sportId<\/code> parameter and returns all 33,115 definitions, so filter client-side. That quirk also bites on <a href=\"https:\/\/oddspapi.io\/blog\/euroleague-basketball-odds-api\/\">the EuroLeague board, where the match result ships under two market IDs<\/a>.<\/p>\n<h2>Step 2: Resolve the Ladder Once, Reuse It Everywhere<\/h2>\n<p>Build the ladder in line order and carry the outcome labels with it. Then nothing downstream needs to know about ID blocks.<\/p>\n<pre class=\"wp-block-code\"><code>import os, time, requests\n\nAPI_KEY = os.environ[\"ODDSPAPI_API_KEY\"]\nBASE = \"https:\/\/api.oddspapi.io\/v4\"\n\n\ndef get(path, **params):\n    \"\"\"One call, with the rate-limit retry the docs ask for.\"\"\"\n    for _ in range(4):\n        r = requests.get(f\"{BASE}{path}\", params={\"apiKey\": API_KEY, **params})\n        if r.status_code == 429:\n            time.sleep(r.json().get(\"retryMs\", 2000) \/ 1000)\n            continue\n        r.raise_for_status()\n        return r.json()\n    raise RuntimeError(f\"rate limited on {path}\")\n\n\ndef load_ladder(sport_id, market_name):\n    \"\"\"Return the rungs of one ladder, in line order, with their outcome labels.\"\"\"\n    rungs = [m for m in get(\"\/markets\")\n             if m.get(\"sportId\") == sport_id and m[\"marketName\"] == market_name]\n    rungs.sort(key=lambda m: m[\"handicap\"])\n    return [{\n        \"line\": m[\"handicap\"],\n        \"market_id\": m[\"marketId\"],\n        # Match on the NAME. The array order is not stable across rungs.\n        \"outcomes\": {o[\"outcomeName\"]: o[\"outcomeId\"] for o in m[\"outcomes\"]},\n    } for m in rungs]\n\n\ntotals = load_ladder(11, \"Over Under (incl. overtime)\")\nspreads = load_ladder(11, \"Handicap (incl. overtime)\")\nprint(f\"{len(totals)} total rungs, {len(spreads)} spread rungs\")\n\nfor r in totals:\n    if 220.0 &lt;= r[\"line\"] &lt;= 222.0:\n        print(f\"  line {r['line']:6.1f}  market {r['market_id']:&lt;7} outcomes {r['outcomes']}\")<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>401 total rungs, 321 spread rungs\n  line  220.0  market 113118  outcomes {'Under': 113119, 'Over': 113118}\n  line  220.5  market 11246   outcomes {'Over': 11246, 'Under': 11247}\n  line  221.0  market 113120  outcomes {'Over': 113120, 'Under': 113121}\n  line  221.5  market 11248   outcomes {'Over': 11248, 'Under': 11249}\n  line  222.0  market 113122  outcomes {'Under': 113123, 'Over': 113122}<\/code><\/pre>\n<p>The dictionary keys tell you the flip is real. Line 220.0 hands you Under first, 220.5 hands you Over first. Position-based parsing writes the Under price into the Over column on roughly half the ladder. <a href=\"https:\/\/oddspapi.io\/blog\/cs2-odds-api\/\">The CS2 map handicap does the same thing across its 15 market IDs<\/a>.<\/p>\n<h2>Step 3: Read a Live Board<\/h2>\n<p>Opening night, Detroit at Boston, fixture <code>id1100013273682354<\/code>. One unfiltered call returned <strong>220 bookmakers and 531 distinct market IDs<\/strong> across 9.2 MB of JSON.<\/p>\n<pre class=\"wp-block-code\"><code>FIXTURE = \"id1100013273682354\"   # Detroit v Boston, NBA opening night\n\nladder = {m[\"marketId\"]: m[\"handicap\"] for m in get(\"\/markets\")\n          if m.get(\"sportId\") == 11 and m[\"marketName\"] == \"Handicap (incl. overtime)\"}\n\nboard = get(\"\/odds\", fixtureId=FIXTURE)\nbooks = board[\"bookmakerOdds\"]\nprint(f\"{len(books)} bookmakers on the board\")\n\nposted, tradeable = {}, {}\nfor slug, book in books.items():\n    for market_id, market in book[\"markets\"].items():\n        line = ladder.get(int(market_id))\n        if line is None:\n            continue\n        legs = [o[\"players\"].get(\"0\") for o in market[\"outcomes\"].values()]\n        legs = [p for p in legs if p]\n        if len(legs) != 2:\n            continue\n        posted.setdefault(line, set()).add(slug)\n        live = (not book[\"suspended\"]\n                and market.get(\"marketActive\")\n                and all(p[\"active\"] and (p[\"price\"] or 0) &gt; 1.0 for p in legs))\n        if live:\n            tradeable.setdefault(line, set()).add(slug)\n\nprint(f\"spread rungs with a posted price: {len(posted)} of {len(ladder)}\")\ntop = sorted(posted, key=lambda l: -len(posted[l]))[:5]\nfor line in top:\n    print(f\"  {line:+6.1f}  {len(posted[line]):3d} books posted, \"\n          f\"{len(tradeable.get(line, [])):3d} tradeable\")<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>220 bookmakers on the board\nspread rungs with a posted price: 321 of 321\n    -1.5  190 books posted,  88 tradeable\n    -1.0   63 books posted,  36 tradeable\n    -2.5   59 books posted,   8 tradeable\n    -3.5   58 books posted,   8 tradeable\n    +1.5   44 books posted,   6 tradeable<\/code><\/pre>\n<p>The spread ladder was complete: all 321 rungs from -80.0 to +80.0 carried a price. The total ladder carried 189 rungs, spanning 100.5 to 259.5. Together that is <strong>510 of the 722 catalogue rungs on a single fixture<\/strong>, 35 days before tip-off.<\/p>\n<p>Three flags decide whether a rung is tradeable. <code>suspended<\/code> sits on the bookmaker, <code>marketActive<\/code> on the market, and <code>active<\/code> on the individual price. Drop any one of the three and the leaderboard changes, which is the same lesson <a href=\"https:\/\/oddspapi.io\/blog\/nba-opening-night-odds-api\/\">the opening-night board study<\/a> ran into on the moneyline.<\/p>\n<h3>Ladder Width Splits the Board in Two<\/h3>\n<p>The median bookmaker posts two spread rungs. A cohort of crypto and offshore sportsbooks posts the whole thing: <code>500.casino<\/code>, <code>bcgame<\/code>, <code>betfury<\/code> and <code>betify<\/code> each shipped all 321 spread rungs, and <code>gamdom<\/code> shipped 163 total rungs. US retail runs narrow at this range, with <code>draftkings<\/code>, <code>bet365<\/code> and <code>caesars<\/code> on three total rungs apiece.<\/p>\n<p><code>pinnacle<\/code>, <code>betfair-ex<\/code>, <code>polymarket<\/code> and <code>circasports<\/code> had no NBA price at all 35 days out. Test for it rather than guessing, because the API tells you which case you are in:<\/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=notarealbook<\/code><\/td>\n<td>400 <code>INVALID_PARAMETER<\/code><\/td>\n<td>The slug does not exist<\/td>\n<\/tr>\n<tr>\n<td><code>bookmakers=&lt;gated slug&gt;<\/code><\/td>\n<td>403<\/td>\n<td>Your key cannot read it<\/td>\n<\/tr>\n<tr>\n<td><code>bookmakers=pinnacle<\/code><\/td>\n<td>200, slug absent from the payload<\/td>\n<td>The book has not opened this fixture<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Sharp books open NBA sides late and deep. On a played fixture they carry the full ladder, which Step 5 shows.<\/p>\n<h2>Step 4: Resolve the Main Line Without Trusting mainLine<\/h2>\n<p>Every price carries a <code>mainLine<\/code> boolean. On the spread it behaves: 195 books set it and only 2 set it on more than one rung. On the total it does not. <strong>199 books set <code>mainLine<\/code> on a total rung and 97 of them set it on more than one<\/strong>. bet365 flagged 221.5, 222.5 and 223.5 on the same fixture.<\/p>\n<p>Resolve the main line yourself instead. Take each book&#8217;s most balanced rung inside a sane price band, then take the mode across books.<\/p>\n<pre class=\"wp-block-code\"><code>import collections, statistics\n\nladder = {m[\"marketId\"]: m[\"handicap\"] for m in get(\"\/markets\")\n          if m.get(\"sportId\") == 11 and m[\"marketName\"] == \"Handicap (incl. overtime)\"}\nbooks = get(\"\/odds\", fixtureId=FIXTURE)[\"bookmakerOdds\"]\n\nmains, quotes = {}, {}\nfor slug, book in books.items():\n    if \"+\" in slug or slug in {\"demo\", \"singbet-b\"}:\n        continue\n    best = None\n    for market_id, market in book[\"markets\"].items():\n        line = ladder.get(int(market_id))\n        if line is None or book[\"suspended\"] or not market.get(\"marketActive\"):\n            continue\n        legs = [o[\"players\"].get(\"0\") for o in market[\"outcomes\"].values()]\n        legs = [p for p in legs if p and p[\"active\"] and (p[\"price\"] or 0) &gt; 1.0]\n        if len(legs) != 2:\n            continue\n        prices = sorted(p[\"price\"] for p in legs)\n        if not (1.5 &lt;= prices[0] and prices[1] &lt;= 2.6):\n            continue\n        balance = prices[1] - prices[0]\n        if best is None or balance &lt; best[0]:\n            best = (balance, line, tuple(prices))\n    if best:\n        mains[slug], quotes[slug] = best[1], best[2]\n\nmode, count = collections.Counter(mains.values()).most_common(1)[0]\nprint(f\"{len(mains)} books resolved, main line {mode:+.1f} on {count} of them\")\n\non_mode = {s: q for s, q in quotes.items() if mains[s] == mode}\nmargin = {s: (sum(1 \/ p for p in q) - 1) * 100 for s, q in on_mode.items()}\nprint(f\"median margin on {mode:+.1f}: {statistics.median(margin.values()):.2f}%\")\n\ntuples = collections.Counter(on_mode.values())\nprint(f\"{len(on_mode)} quotes collapse to {len(tuples)} distinct prices \"\n      f\"({100 * (1 - len(tuples) \/ len(on_mode)):.1f}%)\")\nfor slug, m in sorted(margin.items(), key=lambda kv: kv[1])[:5]:\n    print(f\"  {slug:&lt;16} {m:5.2f}%  {on_mode[slug]}\")<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>91 books resolved, main line -1.5 on 88 of them\nmedian margin on -1.5: 5.83%\n88 quotes collapse to 18 distinct prices (79.5%)\n  duel              3.09%  (1.94, 1.94)\n  atg.se            4.71%  (1.91, 1.91)\n  ballybet          4.71%  (1.91, 1.91)\n  betcity.nl        4.71%  (1.91, 1.91)\n  betmgm.co.uk      4.71%  (1.91, 1.91)<\/code><\/pre>\n<p>The dedupe step matters more than the ranking. Those 88 quotes on -1.5 carry 18 distinct price pairs, a 79.5% collapse, and the largest cluster is 26 slugs quoting 1.91 both ways. The main total behaves the same: 142 tradeable books on 221.5, 27 distinct pairs, 81.0% collapse, 27 slugs sharing 1.90 \/ 1.92. <code>cloneOf<\/code> does not predict which slugs land in a cluster, so <a href=\"https:\/\/oddspapi.io\/blog\/consensus-odds-fair-odds-calculator-python\/\">dedupe on the price tuple before you build a consensus<\/a>.<\/p>\n<p>Margins on the main rungs ran 6.40% median on the 221.5 total and 5.83% on the -1.5 spread. <code>1xbet<\/code> was tightest on the total at 2.07%, <code>duel<\/code> tightest on the spread at 3.09%. Among the US names, <code>draftkings<\/code>, <code>caesars<\/code> and <code>hardrockbet<\/code> all sat at 4.77% on the total and <code>fanduel<\/code> at 4.76%, with <code>bet365<\/code> at 7.53%. That spread across the field is what makes <a href=\"https:\/\/oddspapi.io\/blog\/line-shopping-python-best-odds\/\">line shopping across the full catalogue<\/a> worth the extra call.<\/p>\n<h2>Step 5: Pull the Ladder From a Played Fixture<\/h2>\n<p>Historical odds are free on every tier, and <code>\/v4\/historical-odds<\/code> keeps every rung a book ever posted. Lakers at Thunder on 8 April 2026, fixture <code>id1100013262924355<\/code>:<\/p>\n<pre class=\"wp-block-code\"><code>PLAYED = \"id1100013262924355\"   # Lakers v Thunder, 8 April 2026\n\nladder = {m[\"marketId\"]: m[\"handicap\"] for m in get(\"\/markets\")\n          if m.get(\"sportId\") == 11 and m[\"marketName\"] == \"Over Under (incl. overtime)\"}\n\nfor slug in [\"pinnacle\", \"draftkings\"]:\n    book = get(\"\/historical-odds\", fixtureId=PLAYED, bookmakers=slug)[\"bookmakers\"][slug]\n    rungs, snapshots, changes = {}, 0, 0\n    for market_id, market in book[\"markets\"].items():\n        line = ladder.get(int(market_id))\n        if line is None:\n            continue\n        rungs[line] = market_id\n        for outcome in market[\"outcomes\"].values():\n            for series in outcome[\"players\"].values():\n                snapshots += len(series)\n                last = None\n                for snap in series:\n                    if snap[\"price\"] != last:\n                        changes += 1\n                    last = snap[\"price\"]\n    lines = sorted(rungs)\n    print(f\"{slug}: {len(lines)} total rungs, {lines[0]} to {lines[-1]}, \"\n          f\"{snapshots} snapshots, {changes} price changes\")\n    time.sleep(4.6)<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>pinnacle: 69 total rungs, 200.5 to 234.5, 8195 snapshots, 6329 price changes\ndraftkings: 55 total rungs, 192.5 to 246.5, 15519 snapshots, 14586 price changes<\/code><\/pre>\n<p>Pinnacle carried 447 market IDs on that one fixture. Its busiest total rung, 223.0, moved from 1.833 \/ 2.009 at the open to 2.04 \/ 1.806 on the last active quote, and the closing limits read 3,000 on the Over and 3,722 on the Under. DraftKings ran wider on the ladder and narrower on the limit. A backtest built off either book alone learns that book&#8217;s habits, which is the argument <a href=\"https:\/\/oddspapi.io\/blog\/how-many-bookmakers-backtest\/\">the 176-book study<\/a> makes at length.<\/p>\n<p>Sleep 4.6 seconds between historical calls. The endpoint rate-limits at a lower threshold than <code>\/odds<\/code>, and a 429 body is valid JSON, so a <code>bookmakerOdds<\/code> check on its own reads a rate limit as an empty board. <a href=\"https:\/\/oddspapi.io\/blog\/backtest-betting-model-free-historical-odds\/\">Store the pull locally before you model on it<\/a>.<\/p>\n<h2>The Same Trap in Four Other Ladders<\/h2>\n<p>The feed holds 179 ladders of 40 rungs or more. Market ID order matches line order on 173 of them. Six behave differently, and NBA owns two:<\/p>\n<pre class=\"wp-block-code\"><code>sports = {s[\"sportId\"]: s[\"sportName\"] for s in get(\"\/sports\")}\nladders = collections.defaultdict(list)\nfor m in get(\"\/markets\"):\n    if m.get(\"handicap\") is not None:\n        ladders[(m.get(\"sportId\"), m[\"marketName\"])].append(m)\n\nfor (sport_id, name), rungs in ladders.items():\n    if len(rungs) &lt; 40:\n        continue\n    by_id = sorted(rungs, key=lambda m: m[\"marketId\"])\n    by_line = sorted(rungs, key=lambda m: m[\"handicap\"])\n    agree = sum(1 for a, b in zip(by_id, by_line) if a[\"marketId\"] == b[\"marketId\"])\n    if agree &lt; len(rungs):\n        print(f\"{sports.get(sport_id, sport_id):&lt;18}{name:&lt;36}\"\n              f\"{len(rungs):4d} rungs, {agree:4d} in line order\")<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>Tennis            Participant 2 Total Games            121 rungs,    3 in line order\nTennis            Participant 1 Total Games            105 rungs,    3 in line order\nBasketball        Over Under (incl. overtime)          401 rungs,   81 in line order\nBasketball        Handicap (incl. overtime)            321 rungs,   80 in line order\nAmerican Football Total (incl. overtime)               201 rungs,   51 in line order\nAmerican Football Handicap (incl. overtime)            281 rungs,   90 in line order<\/code><\/pre>\n<p>Run that audit against any sport before you write a parser for it. Basketball&#8217;s quarter and half ladders come out clean at 141 of 141 and 201 of 201, so the split is specific to the full-game markets. The NFL versions behave the same way, which is worth knowing before you <a href=\"https:\/\/oddspapi.io\/blog\/nfl-alternate-lines-api\/\">walk the NFL alternate-line board<\/a>.<\/p>\n<p>Once the ladder is in line order, the rest follows. Adjacent rungs give you <a href=\"https:\/\/oddspapi.io\/blog\/middle-bets-middling-python\/\">middles<\/a>, the price gap between neighbours gives you <a href=\"https:\/\/oddspapi.io\/blog\/nfl-key-numbers-half-point-cost\/\">the cost of a half point<\/a>, and the same <code>handicap<\/code>-first rule resolves <a href=\"https:\/\/oddspapi.io\/blog\/nhl-regulation-overtime-markets\/\">the NHL twin markets<\/a> and <a href=\"https:\/\/oddspapi.io\/blog\/nba-odds-api-player-props-spreads\/\">NBA player props<\/a>.<\/p>\n<h2>Stop Scraping Alt-Line Drawers<\/h2>\n<p>The catalogue held 360 bookmaker slugs on 15 September 2026, 285 of them flagged for live odds. The free tier reads every one of them, plus the full historical archive, plus the WebSocket stream on <code>wss:\/\/v5.oddspapi.io\/ws<\/code> when polling stops being fast enough. <a href=\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\">Push beats polling on a board this wide<\/a>.<\/p>\n<p><a href=\"https:\/\/oddspapi.io\/en\/sign-up\">Get a free API key<\/a> and pull the 722 rungs yourself.<\/p>\n<h2>FAQ<\/h2>\n<h3>How many alternate lines does the NBA board carry?<\/h3>\n<p>The basketball catalogue defines 401 full-game total rungs from 100.0 to 300.0 and 321 full-game handicap rungs from -80.0 to +80.0, in half-point steps. On the Detroit at Boston opener measured 35 days out, 510 of those 722 rungs carried a price from at least one of 220 bookmakers.<\/p>\n<h3>Where does OddsPapi put the line value for an alternate spread?<\/h3>\n<p>In the <code>handicap<\/code> field of the market definition returned by <code>\/v4\/markets<\/code>. Each rung is its own market ID, and the price payload carries no line value, so you join the board to the catalogue on <code>marketId<\/code>.<\/p>\n<h3>Can I sort alternate lines by market ID?<\/h3>\n<p>Not on the NBA full-game ladders. Market ID order matches line order on 81 of the 401 total rungs and 80 of the 321 spread rungs, because the rungs sit in separate ID blocks added at different times. Sort on <code>handicap<\/code> instead. Basketball&#8217;s quarter and half ladders do come out in ID order.<\/p>\n<h3>Is the mainLine flag reliable for NBA totals?<\/h3>\n<p>Treat it as a hint. On the opener, 199 bookmakers set <code>mainLine<\/code> on a total rung and 97 of them set it on more than one; bet365 flagged three rungs at once. Resolve the main line as each book&#8217;s most balanced rung inside a 1.5 to 2.6 price band, then take the mode across books.<\/p>\n<h3>Which bookmakers post the deepest NBA alternate-line ladder?<\/h3>\n<p>Crypto and offshore sportsbooks. On the opener, <code>500.casino<\/code>, <code>bcgame<\/code>, <code>betfury<\/code> and <code>betify<\/code> each posted all 321 spread rungs, and <code>gamdom<\/code> posted 163 total rungs. The median bookmaker posted two rungs a ladder that far out, and the board widens as tip-off approaches.<\/p>\n<h3>Do historical odds keep the alternate lines?<\/h3>\n<p>Yes, and it is free on every tier. On Lakers at Thunder from 8 April 2026, <code>\/v4\/historical-odds<\/code> returned 69 total rungs from Pinnacle across 8,195 snapshots and 55 rungs from DraftKings across 15,519 snapshots, with the full price series on each.<\/p>\n<p><script type=\"application\/ld+json\">\n{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How many alternate lines does the NBA board carry?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"The basketball catalogue defines 401 full-game total rungs from 100.0 to 300.0 and 321 full-game handicap rungs from -80.0 to +80.0, in half-point steps. On the Detroit at Boston opener measured 35 days out, 510 of those 722 rungs carried a price from at least one of 220 bookmakers.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Where does OddsPapi put the line value for an alternate spread?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"In the handicap field of the market definition returned by \/v4\/markets. Each rung is its own market ID, and the price payload carries no line value, so you join the board to the catalogue on marketId.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Can I sort alternate lines by market ID?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Not on the NBA full-game ladders. Market ID order matches line order on 81 of the 401 total rungs and 80 of the 321 spread rungs, because the rungs sit in separate ID blocks added at different times. Sort on handicap instead. Basketball's quarter and half ladders do come out in ID order.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Is the mainLine flag reliable for NBA totals?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Treat it as a hint. On the opener, 199 bookmakers set mainLine on a total rung and 97 of them set it on more than one; bet365 flagged three rungs at once. Resolve the main line as each book's most balanced rung inside a 1.5 to 2.6 price band, then take the mode across books.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Which bookmakers post the deepest NBA alternate-line ladder?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Crypto and offshore sportsbooks. On the opener, 500.casino, bcgame, betfury and betify each posted all 321 spread rungs, and gamdom posted 163 total rungs. The median bookmaker posted two rungs a ladder that far out, and the board widens as tip-off approaches.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Do historical odds keep the alternate lines?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes, and it is free on every tier. On Lakers at Thunder from 8 April 2026, \/v4\/historical-odds returned 69 total rungs from Pinnacle across 8,195 snapshots and 55 rungs from DraftKings across 15,519 snapshots, with the full price series on each.\"\n      }\n    }\n  ]\n}\n<\/script><\/p>\n<p><!--\nFocus Keyphrase: NBA alternate lines API\nSEO Title: NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field\nMeta Description: NBA alternate lines run 722 rungs, totals from 100.0 to 300.0 and spreads from -80.0 to +80.0. On opening night 174 books priced the 221.5 total.\nSlug: nba-alternate-lines-api\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>NBA alternate lines run 722 rungs, totals from 100.0 to 300.0 and spreads from -80.0 to +80.0. On opening night 174 books priced the 221.5 total.<\/p>\n","protected":false},"author":2,"featured_media":4164,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[75,16,9,11,10],"class_list":["post-4163","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to-guides","tag-basketball","tag-nba","tag-odds-api","tag-python","tag-sports-betting-api"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field | 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\/nba-alternate-lines-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field | OddsPapi Blog\" \/>\n<meta property=\"og:description\" content=\"NBA alternate lines run 722 rungs, totals from 100.0 to 300.0 and spreads from -80.0 to +80.0. On opening night 174 books priced the 221.5 total.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/\" \/>\n<meta property=\"og:site_name\" content=\"OddsPapi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-24T10:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"675\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\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=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field\",\"datePublished\":\"2026-09-24T10:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/\"},\"wordCount\":1561,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp\",\"keywords\":[\"Basketball\",\"NBA\",\"Odds API\",\"Python\",\"Sports Betting API\"],\"articleSection\":[\"How To Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/\",\"name\":\"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field | OddsPapi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp\",\"datePublished\":\"2026-09-24T10:00:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp\",\"width\":1200,\"height\":675,\"caption\":\"NBA Alternate Lines API header\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field\"}]},{\"@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":"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field | 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\/nba-alternate-lines-api\/","og_locale":"en_US","og_type":"article","og_title":"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field | OddsPapi Blog","og_description":"NBA alternate lines run 722 rungs, totals from 100.0 to 300.0 and spreads from -80.0 to +80.0. On opening night 174 books priced the 221.5 total.","og_url":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/","og_site_name":"OddsPapi Blog","article_published_time":"2026-09-24T10:00:00+00:00","og_image":[{"width":1200,"height":675,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp","type":"image\/webp"}],"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":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field","datePublished":"2026-09-24T10:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/"},"wordCount":1561,"commentCount":0,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp","keywords":["Basketball","NBA","Odds API","Python","Sports Betting API"],"articleSection":["How To Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/","url":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/","name":"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field | OddsPapi Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp","datePublished":"2026-09-24T10:00:00+00:00","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/nba-alternate-lines-api.webp","width":1200,"height":675,"caption":"NBA Alternate Lines API header"},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/nba-alternate-lines-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"NBA Alternate Lines API: Sort 722 Rungs by the Handicap Field"}]},{"@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\/4163","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=4163"}],"version-history":[{"count":1,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/4163\/revisions"}],"predecessor-version":[{"id":4165,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/4163\/revisions\/4165"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/4164"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=4163"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=4163"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=4163"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}