{"id":3681,"date":"2026-08-31T10:00:00","date_gmt":"2026-08-31T10:00:00","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=3681"},"modified":"2026-09-07T14:01:56","modified_gmt":"2026-09-07T14:01:56","slug":"rugby-odds-api","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/","title":{"rendered":"Rugby Odds API: NRL, Super League and Union From 123 Books"},"content":{"rendered":"<p>Rugby hides two sports behind one sport ID. Point a parser at <code>sportId=26<\/code> and you get the NRL, Super League, Pro D2 and a New Zealand provincial competition in the same list, priced by different bookmakers, with the match winner filed under two different market IDs that are not the same bet.<\/p>\n<p>The result is a parser that runs clean and reports the wrong board. It drops a third of the match-winner quotes, computes a negative margin on Kalshi, and reads a rugby union fixture as three times deeper than it is. This guide walks the rugby board with live data pulled on 24 August 2026 across 40 fixtures with odds, then gives you code that survives it.<\/p>\n<h2>What the rugby board holds<\/h2>\n<p>A ten-day window on <code>sportId=26<\/code>, sampled on 24 August 2026, returned 40 fixtures with odds, 123 distinct bookmakers and 88,380 prices. Books per fixture ran from 3 to 107, with a median of 52. The first split is the one nobody warns you about: <code>categoryName<\/code> carries the code, not the country.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>categoryName<\/th>\n<th>Fixtures with odds<\/th>\n<th>Competitions priced<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Rugby League<\/td>\n<td>21<\/td>\n<td>NRL Premiership, Super League, NRL Women<\/td>\n<\/tr>\n<tr>\n<td>Rugby Union<\/td>\n<td>19<\/td>\n<td>Rugbys Greatest Rivalry, Pro D2, National Provincial Championship, Int. Friendly Games, Queensland Premier Rugby<\/td>\n<\/tr>\n<tr>\n<td>Rugby Union Sevens<\/td>\n<td>0<\/td>\n<td>None priced in this window<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Depth per competition varies by a factor of thirty-five:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Competition<\/th>\n<th>Code<\/th>\n<th>Fixtures<\/th>\n<th>Books per fixture<\/th>\n<th>Pinnacle live<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>NRL Premiership<\/td>\n<td>League<\/td>\n<td>8<\/td>\n<td><strong>102 to 107<\/strong><\/td>\n<td>0 of 8<\/td>\n<\/tr>\n<tr>\n<td>Rugbys Greatest Rivalry<\/td>\n<td>Union<\/td>\n<td>2<\/td>\n<td>83 to 96<\/td>\n<td>1 of 2<\/td>\n<\/tr>\n<tr>\n<td>Super League<\/td>\n<td>League<\/td>\n<td>7<\/td>\n<td>55<\/td>\n<td>0 of 7<\/td>\n<\/tr>\n<tr>\n<td>Pro D2<\/td>\n<td>Union<\/td>\n<td>8<\/td>\n<td>38 to 54<\/td>\n<td>0 of 8<\/td>\n<\/tr>\n<tr>\n<td>National Provincial Championship<\/td>\n<td>Union<\/td>\n<td>7<\/td>\n<td>42 to 51<\/td>\n<td>0 of 7<\/td>\n<\/tr>\n<tr>\n<td>Int. Friendly Games<\/td>\n<td>Union<\/td>\n<td>1<\/td>\n<td>47<\/td>\n<td>0 of 1<\/td>\n<\/tr>\n<tr>\n<td>NRL, Women<\/td>\n<td>League<\/td>\n<td>6<\/td>\n<td>3<\/td>\n<td>0 of 6<\/td>\n<\/tr>\n<tr>\n<td>Queensland Premier Rugby<\/td>\n<td>Union<\/td>\n<td>1<\/td>\n<td>3<\/td>\n<td>0 of 1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h3>Half the board is posted and suspended<\/h3>\n<p>Only 44.4% of the 88,380 prices came back <code>active: true<\/code>, which is close to the 41.9% we measured on <a href=\"https:\/\/oddspapi.io\/blog\/college-football-odds-api\/\">early-season college football<\/a>. That average hides the more useful finding, because the split runs along the code:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Competition<\/th>\n<th>Books<\/th>\n<th>Prices active<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>NRL Premiership<\/td>\n<td>102 to 107<\/td>\n<td><strong>86.7% to 92.8%<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Super League<\/td>\n<td>55<\/td>\n<td>70.5% to 71.6%<\/td>\n<\/tr>\n<tr>\n<td>Rugbys Greatest Rivalry<\/td>\n<td>96<\/td>\n<td>37.6%<\/td>\n<\/tr>\n<tr>\n<td>National Provincial Championship<\/td>\n<td>42 to 51<\/td>\n<td><strong>30.6% to 32.2%<\/strong><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p><strong>Rugby league boards are live. Rugby union boards are mostly posted and suspended.<\/strong> A National Provincial Championship fixture carries 42 to 51 books and roughly a third of its prices are actually taking bets, so a parser that skips the price-level <code>active<\/code> flag reads that board as three times deeper than it is. Filter on the flag before you count anything.<\/p>\n<h2>The sharp and the depth sit in different codes<\/h2>\n<p>Pinnacle was live on 1 of the 40 upcoming fixtures. Read that too fast and you conclude Pinnacle has left rugby union. It has not. Pinnacle opens rugby late, so a live census taken on an upcoming fixture is measuring the clock rather than the coverage.<\/p>\n<p>To measure coverage instead, query fixtures that have already been played: <code>\/historical-odds<\/code> retains a book&#8217;s full menu after the live prices are dropped, so it answers whether a book covers a market at all.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Competition<\/th>\n<th>Fixture date<\/th>\n<th>Pinnacle<\/th>\n<th>SBOBet<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>NRL Premiership<\/td>\n<td>23 Aug<\/td>\n<td><strong>never quoted<\/strong><\/td>\n<td>never quoted<\/td>\n<\/tr>\n<tr>\n<td>NRL Premiership<\/td>\n<td>23 Aug<\/td>\n<td><strong>never quoted<\/strong><\/td>\n<td>never quoted<\/td>\n<\/tr>\n<tr>\n<td>Super League<\/td>\n<td>23 Aug<\/td>\n<td><strong>never quoted<\/strong><\/td>\n<td>Total 4, Handicap 3<\/td>\n<\/tr>\n<tr>\n<td>Super League<\/td>\n<td>23 Aug<\/td>\n<td><strong>never quoted<\/strong><\/td>\n<td>Total 2, Handicap 2<\/td>\n<\/tr>\n<tr>\n<td>National Provincial Championship<\/td>\n<td>23 Aug<\/td>\n<td>Winner 1, Total 5, Handicap 8<\/td>\n<td>never quoted<\/td>\n<\/tr>\n<tr>\n<td>National Provincial Championship<\/td>\n<td>23 Aug<\/td>\n<td>Winner 1, Total 8, Handicap 9<\/td>\n<td>Total 7, Handicap 8<\/td>\n<\/tr>\n<tr>\n<td>Currie Cup<\/td>\n<td>23 Aug<\/td>\n<td>Winner 1, Total 7, Handicap 6<\/td>\n<td>Total 4, Handicap 3<\/td>\n<\/tr>\n<tr>\n<td>Currie Cup<\/td>\n<td>21 Aug<\/td>\n<td>Winner 1, Total 11, Handicap 6<\/td>\n<td>never quoted<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p><strong>Pinnacle prices rugby union and does not price rugby league.<\/strong> That is a real coverage gap rather than a timing artefact, because the fixtures above had already kicked off when they were queried and their full price history was still there. Pinnacle quoted the National Provincial Championship and the Currie Cup on every union fixture tested, and quoted nothing at all on the NRL or Super League.<\/p>\n<p>SBOBet does not follow it. SBOBet quoted totals and handicaps on both Super League fixtures tested, so it is present on rugby league in a way Pinnacle is not, while skipping some union fixtures Pinnacle priced. Two sharp books, two different maps.<\/p>\n<p>The NRL is where the depth is. 102 to 107 books on every fixture in the sample, up to 222 distinct market IDs on one board. It has no sharp benchmark on it at all.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>You want<\/th>\n<th>Go to<\/th>\n<th>What you give up<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>A sharp benchmark to de-vig against<\/td>\n<td>Rugby union: the National Provincial Championship or the Currie Cup<\/td>\n<td>Pinnacle opens late, so it is usually absent from an upcoming fixture, and its menu is one Winner market plus 5 to 11 total rungs and 6 to 9 handicap rungs<\/td>\n<\/tr>\n<tr>\n<td>Book count and alt ladders<\/td>\n<td>NRL Premiership (102 to 107 books)<\/td>\n<td>Neither Pinnacle nor SBOBet quoted either NRL fixture tested<\/td>\n<\/tr>\n<tr>\n<td>A prediction-market quote<\/td>\n<td>Kalshi or Polymarket<\/td>\n<td>Both are placeholder-priced on rugby, and their ladders hold single-digit to low-double-digit dollars<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\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 or a generic sports API<\/th>\n<th>OddsPapi<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Cover both codes<\/td>\n<td>Two scrapers, two schemas, two league taxonomies<\/td>\n<td>One <code>sportId=26<\/code> call, split on <code>categoryName<\/code><\/td>\n<\/tr>\n<tr>\n<td>Reach a sharp price<\/td>\n<td>Pinnacle has no public API<\/td>\n<td><code>bookmakers=pinnacle<\/code> on the free tier<\/td>\n<\/tr>\n<tr>\n<td>Cover the NRL board<\/td>\n<td>Up to 107 separate bookmaker sites, each rate-limiting you<\/td>\n<td>One <code>\/odds<\/code> call returns all 107<\/td>\n<\/tr>\n<tr>\n<td>Get price history<\/td>\n<td>Store it yourself from today, or buy it<\/td>\n<td><code>\/historical-odds<\/code>, free tier, back to the day each book opened<\/td>\n<\/tr>\n<tr>\n<td>Read prediction-market depth<\/td>\n<td>Kalshi and Polymarket, separate auth, separate formats<\/td>\n<td><code>exchangeMeta<\/code> back and lay ladders in the same payload<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>Step 1: Authenticate and split the two codes<\/h2>\n<p>The key is a query parameter. It is never a header.<\/p>\n<pre class=\"wp-block-code\"><code>import requests, time, collections\n\nAPI_KEY = \"YOUR_API_KEY\"\nBASE_URL = \"https:\/\/api.oddspapi.io\/v4\"\nRUGBY = 26\n\ndef api(path, **params):\n    \"\"\"One call, with the rate limit and the empty-window 404 handled.\"\"\"\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(r.json()[\"error\"].get(\"retryMs\", 1500) \/ 1000 + 0.3)\n            continue\n        if r.status_code == 404:\n            return []          # an empty fixture window 404s, it does not return []\n        r.raise_for_status()\n        return r.json()\n    raise RuntimeError(\"rate limited\")\n\nfixtures = api(\"fixtures\", sportId=RUGBY, **{\"from\": \"2026-08-24\", \"to\": \"2026-09-03\"})\npriced = [f for f in fixtures if f[\"hasOdds\"]]\n\nby_code = collections.Counter(f[\"categoryName\"] for f in priced)\nprint(len(priced), \"fixtures with odds\")\nfor code, n in by_code.most_common():\n    print(f\"  {code:20s} {n}\")\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>40 fixtures with odds\n  Rugby League         21\n  Rugby Union          19\n<\/code><\/pre>\n<p>Two rules come out of that call. An empty date window returns HTTP 404 with a <code>FIXTURE_NOT_FOUND<\/code> body, so <code>raise_for_status()<\/code> on its own kills a season loop. And <code>to<\/code> is a midnight-UTC instant, so set it to the day after the last day you want.<\/p>\n<h3>Find the competitions<\/h3>\n<pre class=\"wp-block-code\"><code>comp = collections.Counter((f[\"categoryName\"], f[\"tournamentName\"]) for f in priced)\nfor (code, name), n in comp.most_common():\n    print(f\"  {code:14s} {name:34s} {n}\")\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>  Rugby League   NRL Premiership                     8\n  Rugby Union    Pro D2                              8\n  Rugby Union    National Provincial Championship    7\n  Rugby League   Super League                        7\n  Rugby League   NRL, Women                          6\n  Rugby Union    Rugbys Greatest Rivalry             2\n  Rugby Union    Int. Friendly Games                 1\n  Rugby Union    Queensland Premier Rugby            1\n<\/code><\/pre>\n<p>Filter on the pair. Filtering on <code>tournamentName<\/code> alone mixes codes, and the same trap bites in soccer, where 35 competitions are called &#8220;Premier League&#8221;.<\/p>\n<h2>Step 2: The match winner ships under two market IDs<\/h2>\n<p>Rugby carries four market families and nothing else. No player props, no try-scorer market, no first-scorer market, zero player-keyed prices anywhere in the window.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Family<\/th>\n<th>Market ID<\/th>\n<th>Outcomes<\/th>\n<th>Books quoting it<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1X2<\/td>\n<td>263<\/td>\n<td>263 home, 264 draw, 265 away<\/td>\n<td><strong>116<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Total<\/td>\n<td>one ID per line<\/td>\n<td>Over, Under<\/td>\n<td>111<\/td>\n<\/tr>\n<tr>\n<td>Handicap<\/td>\n<td>one ID per line<\/td>\n<td>two-sided<\/td>\n<td>106<\/td>\n<\/tr>\n<tr>\n<td>Winner<\/td>\n<td>261<\/td>\n<td>261 home, 262 away<\/td>\n<td>90<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p><strong>Market 261 and market 263 are not duplicates. They are different bets.<\/strong> 261 is a two-way price with no draw. 263 is a three-way that prices the draw. Rugby league games can be drawn, and books price both, so both appear in the same payload for the same fixture. Of the 40 fixtures sampled on 24 August 2026, 33 carried both families, 6 carried only 1X2, and none carried only Winner.<\/p>\n<p>Which one a book uses is a property of the book. Five books quote 261 and never touch 263:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Book<\/th>\n<th>Winner (261)<\/th>\n<th>1X2 (263)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>pinnacle<\/td>\n<td>quotes it<\/td>\n<td>never<\/td>\n<\/tr>\n<tr>\n<td>hardrockbet<\/td>\n<td>quotes it<\/td>\n<td>never<\/td>\n<\/tr>\n<tr>\n<td>paddypower<\/td>\n<td>quotes it<\/td>\n<td>never<\/td>\n<\/tr>\n<tr>\n<td>tab.com.au<\/td>\n<td>quotes it<\/td>\n<td>never<\/td>\n<\/tr>\n<tr>\n<td>tabtouch<\/td>\n<td>quotes it<\/td>\n<td>never<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Key on 263 and you drop all five, Pinnacle included. Across the 40 fixtures that costs 1,161 of 3,245 match-winner quotes, or <strong>35.8%<\/strong>. Losing a third of your board is bad. Losing the only sharp on it changes every fair price you compute.<\/p>\n<pre class=\"wp-block-code\"><code>WINNER_2WAY = \"261\"     # 261 home, 262 away\nWINNER_3WAY = \"263\"     # 263 home, 264 draw, 265 away\n\ndef live_prices(market):\n    \"\"\"Active prices only. The active flag lives on the price, not the outcome.\"\"\"\n    out = {}\n    for outcome_id, outcome in market.get(\"outcomes\", {}).items():\n        price = outcome.get(\"players\", {}).get(\"0\")\n        if price and price.get(\"active\"):\n            out[outcome_id] = price[\"price\"]\n    return out\n\ndef read_board(books):\n    board = {}\n    for slug, book in books.items():\n        if book.get(\"suspended\"):\n            continue\n        markets = book.get(\"markets\", {})\n        two = live_prices(markets.get(WINNER_2WAY, {}))\n        three = live_prices(markets.get(WINNER_3WAY, {}))\n        entry = {}\n        if len(two) == 2:\n            entry[\"two_way\"] = {\"home\": two[\"261\"], \"away\": two[\"262\"]}\n        if len(three) == 3:\n            entry[\"three_way\"] = {\"home\": three[\"263\"], \"draw\": three[\"264\"],\n                                  \"away\": three[\"265\"]}\n        elif three:\n            entry[\"partial\"] = three          # see step 3\n        if entry:\n            board[slug] = entry\n    return board\n\n# swap in the fixture ID from your own \/fixtures call\nFIXTURE = \"id2600029465687684\"    # Brisbane Broncos v Melbourne Storm, NRL Premiership\n\nodds = api(\"odds\", fixtureId=FIXTURE)\nboard = read_board(odds[\"bookmakerOdds\"])\nfor slug in sorted(board):\n    print(f\"  {slug:20s} {board[slug]}\")\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>  # each row is\n  #   slug: {'two_way':   {'home': .., 'away': ..},\n  #          'three_way': {'home': .., 'draw': .., 'away': ..},\n  #          'partial':   {outcome_id: price}}\n  #\n  # Brisbane Broncos v Melbourne Storm, NRL Premiership, kickoff 27 Aug 09:50 UTC.\n  # The deepest board in the 24 August 2026 sample: 107 books, which produce\n  # 103 Winner and 1X2 quotes between them, and 40 independent prices.\n<\/code><\/pre>\n<h2>Step 3: Count outcomes before you compute a margin<\/h2>\n<p>Six books shipped market 263 with fewer than three priced outcomes. The market ID says three-way. The payload does not.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Book<\/th>\n<th>Outcomes shipped on 263<\/th>\n<th>Fixtures<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>pokerstars.uk<\/td>\n<td><strong>0 priced legs<\/strong><\/td>\n<td>17<\/td>\n<\/tr>\n<tr>\n<td>polymarket<\/td>\n<td>2 of 3<\/td>\n<td>16<\/td>\n<\/tr>\n<tr>\n<td>pointsbet.com.au<\/td>\n<td>2 of 3<\/td>\n<td>9<\/td>\n<\/tr>\n<tr>\n<td>betmgm.bet.br<\/td>\n<td>1 of 3<\/td>\n<td>9<\/td>\n<\/tr>\n<tr>\n<td>expekt.dk<\/td>\n<td>1 of 3<\/td>\n<td>9<\/td>\n<\/tr>\n<tr>\n<td>kalshi<\/td>\n<td>1 of 3 (the draw leg only)<\/td>\n<td>8<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Run a naive margin over those and the numbers lie in both directions. Kalshi&#8217;s single draw leg inverts to a fraction of 1.0 on its own, which reads as a large negative margin and trips every arbitrage alert you own. PointsBet&#8217;s two legs sum to a little over 1.0, which reads as the tightest three-way on the board when it is really a two-way missing its draw.<\/p>\n<p><strong>A single book never offers you a free arbitrage.<\/strong> If the inverse prices of a market sum to 1.0 or less, you are looking at an incomplete market, not an edge.<\/p>\n<pre class=\"wp-block-code\"><code>def margin(prices):\n    return (sum(1 \/ p for p in prices.values()) - 1) * 100\n\nrows = [(margin(e[\"two_way\"]), slug) for slug, e in board.items() if \"two_way\" in e]\nfor m, slug in sorted(rows):\n    print(f\"  {slug:20s} {m:5.2f}%\")\n<\/code><\/pre>\n<p>Run that across the sample rather than one fixture and you get a ranking. These are median margins per book on fixtures where it quoted the family, for books present on at least four fixtures. <strong>Market 261, the two-way Winner, 77 ranked books:<\/strong><\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Rank<\/th>\n<th>Book<\/th>\n<th>Median margin<\/th>\n<th>Fixtures<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1<\/td>\n<td>paddypower<\/td>\n<td><strong>4.51%<\/strong><\/td>\n<td>9<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>fanduel<\/td>\n<td>5.09%<\/td>\n<td>7<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>netbet<\/td>\n<td>5.26%<\/td>\n<td>7<\/td>\n<\/tr>\n<tr>\n<td>4 to 28<\/td>\n<td>the 25-slug Unibet family (atg.se, ballybet, betcity.nl, betparx, betplay, bingoal.be, casumo, expekt.se, fourwinds, jacks.nl, leovegas, paf, prolineplus, scooore.be, svenskaspel, tabtouch, unibet and its regionals)<\/td>\n<td>5.33%<\/td>\n<td>8 to 10<\/td>\n<\/tr>\n<tr>\n<td>29<\/td>\n<td>hardrockbet<\/td>\n<td>5.79%<\/td>\n<td>31<\/td>\n<\/tr>\n<tr>\n<td>47<\/td>\n<td>betmgm<\/td>\n<td>7.54%<\/td>\n<td>15<\/td>\n<\/tr>\n<tr>\n<td>77<\/td>\n<td><strong>kalshi<\/strong><\/td>\n<td><strong>70.07%<\/strong><\/td>\n<td>8<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p><strong>Market 263, the three-way 1X2, 107 ranked books:<\/strong><\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Rank<\/th>\n<th>Book<\/th>\n<th>Median margin<\/th>\n<th>Fixtures<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1<\/td>\n<td>betfair-ex<\/td>\n<td><strong>3.89%<\/strong><\/td>\n<td>9<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>netbet<\/td>\n<td>4.95%<\/td>\n<td>7<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>draftkings<\/td>\n<td>6.49%<\/td>\n<td>26<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>fanduel<\/td>\n<td>6.92%<\/td>\n<td>14<\/td>\n<\/tr>\n<tr>\n<td>5<\/td>\n<td>winbet.ro<\/td>\n<td>7.23%<\/td>\n<td>27<\/td>\n<\/tr>\n<tr>\n<td>38<\/td>\n<td>1xbet<\/td>\n<td>8.08%<\/td>\n<td>37<\/td>\n<\/tr>\n<tr>\n<td>53<\/td>\n<td>bet365<\/td>\n<td>8.50%<\/td>\n<td>33<\/td>\n<\/tr>\n<tr>\n<td>76<\/td>\n<td>betmgm<\/td>\n<td>9.76%<\/td>\n<td>15<\/td>\n<\/tr>\n<tr>\n<td>107<\/td>\n<td><strong>polymarket<\/strong><\/td>\n<td><strong>194.12%<\/strong><\/td>\n<td>9<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Pinnacle and SBOBet appear in neither table. Each was live on 1 of the 40 fixtures, which is not enough to rank a book on, and quoting a one-fixture margin as if it were a rate is how the previous version of this table went wrong.<\/p>\n<p>Compare like with like. A two-way margin and a three-way margin are not the same measurement, and the same book prices them differently: FanDuel&#8217;s median is 5.09% on the two-way and 6.92% on the three-way, BetMGM&#8217;s is 7.54% and 9.76%. Our <a href=\"https:\/\/oddspapi.io\/blog\/vig-calculator-python-sportsbook-margin\/\">vig calculator guide<\/a> covers the normalisation if you need to rank across both.<\/p>\n<h3>Both prediction markets are placeholder-priced, in opposite families<\/h3>\n<p>Kalshi splits the game across three contracts, so its <code>bookmakerOutcomeId<\/code> values show three separate Kalshi tickets on one game:<\/p>\n<pre class=\"wp-block-code\"><code>  market 261, outcome 261:  home price   ...-&lt;HOME&gt;:yes\n  market 261, outcome 262:  away price   ...-&lt;AWAY&gt;:yes\n  market 263, outcome 264:  draw price   ...-TIE:yes\n<\/code><\/pre>\n<p>To build a Kalshi three-way, merge its market 261 with outcome 264 of market 263. Its two-way margin on its own always understates what it is really charging on the game.<\/p>\n<p>Then screen it. Across the 8 fixtures where Kalshi priced the two-way, its median margin was <strong>70.07%<\/strong>, the worst of the 77 books ranked on market 261. Polymarket does the same thing in the other family: across the 9 fixtures where it priced the three-way, its median margin was <strong>194.12%<\/strong>, the worst of 107. Neither number is a price. Both are placeholders sitting behind a real-looking ladder, and nothing in the payload flags them. Screen a prediction-market quote on its margin and its ladder depth before you use it, the same screen we applied to <a href=\"https:\/\/oddspapi.io\/blog\/kalshi-polymarket-nfl-odds\/\">Kalshi and Polymarket on the NFL<\/a>.<\/p>\n<h2>Step 4: Deduplicate, then read the count<\/h2>\n<p>Several slugs ship identical prices while <code>\/bookmakers<\/code> reports <code>cloneOf: null<\/code> for all of them. On a 107-book board that is not a rounding detail. Brisbane Broncos against Melbourne Storm produced 103 Winner and 1X2 quotes that collapse to <strong>40 independent prices<\/strong>, a collapse of 61.2%. The largest groups:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Slugs<\/th>\n<th>Price<\/th>\n<th>Members<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>24<\/strong><\/td>\n<td>1.84 \/ 1.96<\/td>\n<td>atg.se, ballybet, betcity.nl, betparx, betplay, bingoal.be, casumo, expekt.se, fourwinds, jacks.nl, leovegas, leovegas.es, paf, pointsbet.com.au and 10 more<\/td>\n<\/tr>\n<tr>\n<td>14<\/td>\n<td>1.80 \/ 1.90<\/td>\n<td>betmgm, borgata, bwin, bwin.dk, bwin.es, napoleonsports.be, partypoker, sportingbet, sportsinteraction, superbet.bet.br, superbet.ro, superbet.rs, winbet.bg, winbet.ro<\/td>\n<\/tr>\n<tr>\n<td>7<\/td>\n<td>1.91 \/ 1.91 \/ 21.0<\/td>\n<td>bet365 and its six regional slugs<\/td>\n<\/tr>\n<tr>\n<td>6<\/td>\n<td>1.909 \/ 1.909<\/td>\n<td>betinia.dk, estrelabet, fezbet, goldenpalacesports.be, lottoland, winpot.mx<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>1.80 \/ 1.91<\/td>\n<td>betmgm.co.uk, betuk, coral, ladbrokes<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>1.90 \/ 2.00 \/ 21.0<\/td>\n<td>betway, betway.de, betway.es<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<pre class=\"wp-block-code\"><code>seen = {}\nfor slug, entry in board.items():\n    if \"two_way\" not in entry:\n        continue\n    key = (entry[\"two_way\"][\"home\"], entry[\"two_way\"][\"away\"])\n    seen.setdefault(key, []).append(slug)\n\nprint(len([s for s in board if \"two_way\" in board[s]]), \"slugs ->\", len(seen), \"independent\")\nfor slugs in seen.values():\n    if len(slugs) > 1:\n        print(\"  identical:\", slugs)\n\nbest_home = max((e[\"two_way\"][\"home\"], s) for s, e in board.items() if \"two_way\" in e)\nbest_away = max((e[\"two_way\"][\"away\"], s) for s, e in board.items() if \"two_way\" in e)\nprint(\"  best home\", best_home, \" best away\", best_away)\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>  # Brisbane Broncos v Melbourne Storm, 24 August 2026:\n  # 103 Winner and 1X2 quotes -> 40 independent prices (61.2% collapse)\n  # Across all 40 fixtures: 1,116 slug-quotes -> 387 independent (65.3%)\n<\/code><\/pre>\n<p>Two in every three bookmaker rows on the rugby board are a duplicate feed. Deduplicate per fixture rather than from a fixed clone list, because the groups shift and books sometimes land on the same price by coincidence at round numbers.<\/p>\n<p>Then check what is behind the best price. On an exchange, <code>exchangeMeta.back<\/code> is a list of price levels with the best first, and each level carries a <code>size<\/code>, a <code>cents<\/code> share price and a <code>limit<\/code>, where the limit is the stake that takes the level. On rugby those ladders are close to empty: across the sample the median top-rung stake capacity was <strong>$5.10 on Kalshi<\/strong> (highest $6.00, 24 quotes) and <strong>$25.02 on Polymarket<\/strong> (highest $99.71, 59 quotes). On boxing the equivalent Kalshi number reached $24,705. Score an exchange quote on ladder depth for the sport in front of you, never on the price and never on a rule you carried over from another sport. <a href=\"https:\/\/oddspapi.io\/blog\/line-shopping-python-best-odds\/\">Line shopping<\/a> is only real if the best price will take your stake.<\/p>\n<h2>Step 5: Resolve the handicap line yourself<\/h2>\n<p>Rugby ladders are enormous, and the books that walk them are not the sharp ones. On Brisbane against Melbourne, FanDuel walked 50 handicap rungs. On the union fixtures it priced, Pinnacle walked 6 to 9 handicap rungs and 5 to 11 total rungs, and SBOBet 2 to 8 and 2 to 7.<\/p>\n<p><strong>FanDuel flags every rung it quotes as <code>mainLine: true<\/code>.<\/strong> All 102 of its 102 prices on that fixture, across all 50 handicap rungs. The flag fails on rugby the same way it failed on <a href=\"https:\/\/oddspapi.io\/blog\/nfl-team-totals-api\/\">NFL team totals<\/a> and on college football, so treat it as unusable.<\/p>\n<p>The flag is not even consistent between books. Across the whole 24 August 2026 sample only 17.0% of the 88,380 prices carried it, 15,038 in total. On the same fixture where FanDuel flagged 100% of its prices, bet365 and its regional slugs flagged 4 of 104 (3.8%), 22bet 12 of 102 (11.8%) and 1xbet 4 of 64 (6.2%). One field, five different meanings.<\/p>\n<p>What works is asking each book which of its own rungs it priced closest to even money, then taking the mode across books:<\/p>\n<pre class=\"wp-block-code\"><code>catalog = api(\"markets\", sportId=RUGBY)\nlines = {str(m[\"marketId\"]): m[\"handicap\"]\n         for m in catalog if m[\"marketName\"] == \"Handicap\"}\n\ndef most_balanced(book, lines):\n    \"\"\"The rung this book priced closest to 50\/50 is the one it believes.\"\"\"\n    best = None\n    for market_id, market in book.get(\"markets\", {}).items():\n        if market_id not in lines:\n            continue\n        prices = list(live_prices(market).values())\n        if len(prices) != 2:\n            continue\n        skew = abs(1 \/ prices[0] - 1 \/ prices[1])\n        if best is None or skew < best[0]:\n            best = (skew, lines[market_id], prices)\n    return best\n\nvotes = collections.Counter()\nfor slug, book in odds[\"bookmakerOdds\"].items():\n    pick = most_balanced(book, lines)\n    if pick:\n        votes[pick[1]] += 1\nprint(\"  consensus:\", votes.most_common(1))\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code># Brisbane Broncos v Melbourne Storm, 81 books with a two-sided handicap rung\nconsensus: [(-1.5, 46)]\n<\/code><\/pre>\n<p>Eighty-one books voted and 46 of them landed on -1.5. Run the same function over the Total family and it returns 49.5 from 65 of the 88 books that quoted a total. Take the mode, never one book.<\/p>\n<p>One more flag to ignore while you are here. Across 43,410 market records in the sample, 2,031 carried <code>marketActive: true<\/code> with every price underneath them dead, and 517 carried <code>marketActive: false<\/code> with all of their prices live. It is wrong in both directions and the dominant direction is the one that hands you a market with nothing behind it. We hit the same thing on <a href=\"https:\/\/oddspapi.io\/blog\/boxing-odds-api\/\">boxing<\/a>. Trust the price-level <code>active<\/code> flag and nothing above it.<\/p>\n<h2>Step 6: What Pinnacle's rugby limits tell you<\/h2>\n<p>Pinnacle publishes a stake limit on every price, and the limit caps the maximum win rather than the stake, so <code>base = limit if price >= 2 else limit * (price - 1)<\/code> recovers the figure the book actually set.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Market<\/th>\n<th>Pinnacle base, rugby union<\/th>\n<th>For comparison<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Winner<\/td>\n<td>$25<\/td>\n<td>MLB moneyline $7,500<\/td>\n<\/tr>\n<tr>\n<td>Handicap<\/td>\n<td>$62<\/td>\n<td>La Liga opener 1X2 $1,500<\/td>\n<\/tr>\n<tr>\n<td>Total<\/td>\n<td>$24 to $25<\/td>\n<td>Boxing winner $125<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Two things fall out. Rugby union carries the smallest Pinnacle base we have measured on any sport, so read its rugby prices as a quote rather than a position it wants. And the ranking inverts: Pinnacle takes two and a half times more on the handicap than on the moneyline, where football runs the match market first and the side markets far behind. <a href=\"https:\/\/oddspapi.io\/blog\/betting-limits-api-stake-sizing\/\">Limits are the book's own confidence signal<\/a>, and here they point at the handicap.<\/p>\n<h2>Step 7: Free historical odds, and one Polymarket rule<\/h2>\n<p><code>\/historical-odds<\/code> returns the full snapshot history for a fixture on the free tier, three bookmakers per call:<\/p>\n<pre class=\"wp-block-code\"><code>hist = api(\"historical-odds\", fixtureId=FIXTURE,\n           bookmakers=\"bet365,hardrockbet,draftkings\")\n\nfor slug, book in hist[\"bookmakers\"].items():\n    firsts, snaps, changes = [], 0, 0\n    for market in book[\"markets\"].values():\n        for outcome in market[\"outcomes\"].values():\n            series = outcome[\"players\"][\"0\"]      # a LIST here, not a dict\n            if not series:\n                continue\n            firsts.append(series[0][\"createdAt\"])\n            snaps += len(series)\n            changes += sum(1 for i in range(1, len(series))\n                           if series[i][\"price\"] != series[i - 1][\"price\"])\n    print(f\"  {slug:14s} opened {min(firsts)[:16]}  {snaps} snapshots  {changes} changes\")\n<\/code><\/pre>\n<p>That prints the day each book opened its board on the fixture and how often it moved the price afterwards. Count price changes rather than snapshots, because the feed records on a cadence and most snapshots repeat the previous price.<\/p>\n<h3>Test coverage on a played fixture, not an upcoming one<\/h3>\n<p>This is the most useful trick in the guide, and it is the one that settles the Pinnacle question at the top of this post.<\/p>\n<p>A live <code>\/odds<\/code> call on an upcoming fixture tells you what is open right now, which on rugby is mostly a statement about the clock. <code>\/historical-odds<\/code> tells you what a book covers, because the price history is retained after the fixture is played and the live prices are dropped. Point the call above at last weekend's fixtures instead of next weekend's and you get each book's full menu. That is how the eight-fixture table at the top of this post was built, and it is why \"Pinnacle prices union and not league\" is a coverage finding rather than a snapshot.<\/p>\n<p><strong>Polymarket rejects the normal call.<\/strong> Ask for it without an outcome and you get HTTP 400: <code>\"When using 'polymarket', you must provide only one bookmaker and exactly one outcomeId\"<\/code>. Kalshi has no such rule, and answers a plain call with 15.7 MB.<\/p>\n<pre class=\"wp-block-code\"><code>hist = api(\"historical-odds\", fixtureId=FIXTURE,\n           bookmakers=\"polymarket\", outcomeId=263)\n<\/code><\/pre>\n<p>Read that series before you trust the slug. Polymarket was present on rugby all week and still posted a median three-way margin of 194.12% across the 9 fixtures it priced. A prediction market being on the board tells you nothing about whether its price means anything yet.<\/p>\n<h2>What this gets you<\/h2>\n<p>Rugby is a board that punishes assumptions. The code split lives in <code>categoryName<\/code>. The match winner lives in two market IDs that are different bets. Six books file an incomplete market under the three-way ID. More than half the prices are posted and suspended. The sharp prices one code and the deep board sits in the other.<\/p>\n<p>Handle those five and you have both codes, up to 107 books on an NRL board, a Pinnacle benchmark on southern-hemisphere union, and free snapshot history back to the day each book opened. OddsPapi aggregates <strong>350+ bookmakers<\/strong> across 69 sports, including sharps and prediction markets, with historical odds on the free tier.<\/p>\n<h2>Get your key<\/h2>\n<p>Stop scraping a hundred bookmaker sites for one NRL game. <a href=\"https:\/\/oddspapi.io\/\">Get your free API key<\/a> and pull the whole board in one call. New to the API? Start with <a href=\"https:\/\/oddspapi.io\/blog\/free-odds-api-350-bookmakers\/\">the free odds API guide<\/a>.<\/p>\n<h2>FAQ<\/h2>\n<h3>Is there a free rugby odds API?<\/h3>\n<p>Yes. OddsPapi's free tier covers sportId=26, which returned 40 priced fixtures across the NRL, Super League, Pro D2 and the National Provincial Championship in a ten-day window sampled on 24 August 2026, with a median of 52 bookmakers per fixture, 107 on the deepest board, and full historical snapshots.<\/p>\n<h3>Why does my parser find fewer books than the payload holds?<\/h3>\n<p>You are probably keying on market 263 only. The match winner ships under market 261 as a two-way and market 263 as a three-way, and books pick one or both. Pinnacle, Hard Rock Bet, Paddy Power, TAB and TabTouch quote 261 and never 263. In the 24 August 2026 sample, 90 books quoted 261 and 116 quoted 263, so reading only 263 loses 1,161 of 3,245 match-winner quotes, or 35.8%.<\/p>\n<h3>Does Pinnacle price rugby?<\/h3>\n<p>Pinnacle prices rugby union and does not price rugby league. Tested on fixtures that had already been played, where the historical endpoint keeps a book's full menu, Pinnacle quoted the National Provincial Championship and the Currie Cup in full and quoted nothing at all on the NRL or Super League. It also opens late: it was live on only 1 of the 40 upcoming fixtures sampled on 24 August 2026, so a census taken days out will usually miss it even on the union boards it covers.<\/p>\n<h3>Why does Kalshi show a negative margin on rugby?<\/h3>\n<p>Kalshi lists the draw as a separate contract, which arrives as outcome 264 of market 263 on its own. One leg inverted by itself always sums to less than 1.0, which reads as a negative margin. Merge Kalshi's market 261 with outcome 264 of market 263 to get its real three-way, and reject any single book whose two-sided prices sum to 1.0 or less.<\/p>\n<h3>Are there rugby player props or try-scorer odds?<\/h3>\n<p>No. Rugby carried four market families across the 40 fixtures and 88,380 prices sampled on 24 August 2026: 1X2 on 116 books, Total on 111, Handicap on 106 and Winner on 90. Every price sat under the single default player slot, so no player-level market appeared on either code.<\/p>\n<h3>Can I trust the mainLine flag to find the main handicap?<\/h3>\n<p>No. On Brisbane Broncos against Melbourne Storm, FanDuel flagged 102 of its 102 prices as mainLine, across all 50 handicap rungs it quoted. Across the whole 24 August 2026 sample only 17.0% of the 88,380 prices carried the flag, and bet365 flagged 3.8% of its own on the same fixture. Resolve the line by taking each book's most balanced rung and using the mode across books.<\/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\": \"Is there a free rugby odds API?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes. OddsPapi's free tier covers sportId=26, which returned 40 priced fixtures across the NRL, Super League, Pro D2 and the National Provincial Championship in a ten-day window sampled on 24 August 2026, with a median of 52 bookmakers per fixture, 107 on the deepest board, and full historical snapshots.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Why does my parser find fewer books than the payload holds?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"You are probably keying on market 263 only. The match winner ships under market 261 as a two-way and market 263 as a three-way, and books pick one or both. Pinnacle, Hard Rock Bet, Paddy Power, TAB and TabTouch quote 261 and never 263. In the 24 August 2026 sample, 90 books quoted 261 and 116 quoted 263, so reading only 263 loses 1,161 of 3,245 match-winner quotes, or 35.8%.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Does Pinnacle price rugby?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Pinnacle prices rugby union and does not price rugby league. Tested on fixtures that had already been played, where the historical endpoint keeps a book's full menu, Pinnacle quoted the National Provincial Championship and the Currie Cup in full and quoted nothing at all on the NRL or Super League. It also opens late: it was live on only 1 of the 40 upcoming fixtures sampled on 24 August 2026, so a census taken days out will usually miss it even on the union boards it covers.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Why does Kalshi show a negative margin on rugby?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Kalshi lists the draw as a separate contract, which arrives as outcome 264 of market 263 on its own. One leg inverted by itself always sums to less than 1.0, which reads as a negative margin. Merge Kalshi's market 261 with outcome 264 of market 263 to get its real three-way, and reject any single book whose two-sided prices sum to 1.0 or less.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Are there rugby player props or try-scorer odds?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"No. Rugby carried four market families across the 40 fixtures and 88,380 prices sampled on 24 August 2026: 1X2 on 116 books, Total on 111, Handicap on 106 and Winner on 90. Every price sat under the single default player slot, so no player-level market appeared on either code.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Can I trust the mainLine flag to find the main handicap?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"No. On Brisbane Broncos against Melbourne Storm, FanDuel flagged 102 of its 102 prices as mainLine, across all 50 handicap rungs it quoted. Across the whole 24 August 2026 sample only 17.0% of the 88,380 prices carried the flag, and bet365 flagged 3.8% of its own on the same fixture. Resolve the line by taking each book's most balanced rung and using the mode across books.\"\n      }\n    }\n  ]\n}\n<\/script><\/p>\n<p><!--\nFocus Keyphrase: rugby odds api\nSEO Title: Rugby Odds API: NRL, Super League and Union From 123 Books\nMeta Description: Pull live NRL, Super League and union odds from up to 107 bookmakers in Python, and resolve the two market IDs each code files the match winner under.\nSlug: rugby-odds-api\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pull live NRL, Super League and union odds from up to 107 bookmakers in Python, and resolve the two market IDs each code files the match winner under.<\/p>\n","protected":false},"author":2,"featured_media":3682,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[8,9,11,93,10],"class_list":["post-3681","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to-guides","tag-free-api","tag-odds-api","tag-python","tag-rugby","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>Rugby Odds API: NRL, Super League and Union From 123 Books | 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\/rugby-odds-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Rugby Odds API: NRL, Super League and Union From 123 Books | OddsPapi Blog\" \/>\n<meta property=\"og:description\" content=\"Pull live NRL, Super League and union odds from up to 107 bookmakers in Python, and resolve the two market IDs each code files the match winner under.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/\" \/>\n<meta property=\"og:site_name\" content=\"OddsPapi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-31T10:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-07T14:01:56+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1429\" \/>\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=\"20 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"Rugby Odds API: NRL, Super League and Union From 123 Books\",\"datePublished\":\"2026-08-31T10:00:00+00:00\",\"dateModified\":\"2026-09-07T14:01:56+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/\"},\"wordCount\":3129,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp\",\"keywords\":[\"Free API\",\"Odds API\",\"Python\",\"Rugby\",\"Sports Betting API\"],\"articleSection\":[\"How To Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/\",\"name\":\"Rugby Odds API: NRL, Super League and Union From 123 Books | OddsPapi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp\",\"datePublished\":\"2026-08-31T10:00:00+00:00\",\"dateModified\":\"2026-09-07T14:01:56+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp\",\"width\":2560,\"height\":1429,\"caption\":\"Rugby Odds API - OddsPapi API Blog\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Rugby Odds API: NRL, Super League and Union From 123 Books\"}]},{\"@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":"Rugby Odds API: NRL, Super League and Union From 123 Books | 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\/rugby-odds-api\/","og_locale":"en_US","og_type":"article","og_title":"Rugby Odds API: NRL, Super League and Union From 123 Books | OddsPapi Blog","og_description":"Pull live NRL, Super League and union odds from up to 107 bookmakers in Python, and resolve the two market IDs each code files the match winner under.","og_url":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/","og_site_name":"OddsPapi Blog","article_published_time":"2026-08-31T10:00:00+00:00","article_modified_time":"2026-09-07T14:01:56+00:00","og_image":[{"width":2560,"height":1429,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.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":"20 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"Rugby Odds API: NRL, Super League and Union From 123 Books","datePublished":"2026-08-31T10:00:00+00:00","dateModified":"2026-09-07T14:01:56+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/"},"wordCount":3129,"commentCount":0,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp","keywords":["Free API","Odds API","Python","Rugby","Sports Betting API"],"articleSection":["How To Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/","url":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/","name":"Rugby Odds API: NRL, Super League and Union From 123 Books | OddsPapi Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp","datePublished":"2026-08-31T10:00:00+00:00","dateModified":"2026-09-07T14:01:56+00:00","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/rugby-odds-api\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/08\/rugby-odds-api-scaled.webp","width":2560,"height":1429,"caption":"Rugby Odds API - OddsPapi API Blog"},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/rugby-odds-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"Rugby Odds API: NRL, Super League and Union From 123 Books"}]},{"@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\/3681","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=3681"}],"version-history":[{"count":4,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/3681\/revisions"}],"predecessor-version":[{"id":3900,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/3681\/revisions\/3900"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/3682"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=3681"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=3681"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=3681"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}