{"id":3878,"date":"2026-09-11T10:00:00","date_gmt":"2026-09-11T10:00:00","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=3878"},"modified":"2026-09-02T13:18:20","modified_gmt":"2026-09-02T13:18:20","slug":"african-football-odds-api","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/","title":{"rendered":"African Football Odds API: betPawa Undercuts Pinnacle in Egypt&#8217;s Tier Two"},"content":{"rendered":"<p>Search for an African football odds feed and you get two answers, both wrong. The first is a scraper repo aimed at one bookmaker&#8217;s website. The second is an enterprise data contract with a sales call attached. Neither tells you what is actually on the board for an Egyptian Premier League match on a Wednesday afternoon.<\/p>\n<p>So we measured it. On <strong>2 September 2026<\/strong> we pulled the live board for 12 fixtures across 12 African competitions and counted every bookmaker, every price, and every duplicate. One Egypt Premier League fixture carried <strong>254 bookmakers<\/strong>. A Uganda Premier League match carried 129. Both numbers are larger than most developers assume the whole market to be.<\/p>\n<p>The counts are not the interesting part. The interesting part is which books price these leagues well, and the answer is not the one the brand names suggest.<\/p>\n<h2>What the African board actually looks like<\/h2>\n<p>The soccer catalogue holds <strong>55 African competitions<\/strong>, counting the 16 domestic categories plus the CAF continental cups. Sixteen of them had future fixtures loaded when we ran the census.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Competition<\/th>\n<th>tournamentId<\/th>\n<th>Future fixtures<\/th>\n<th>Books on a sampled fixture<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Egypt Premier League<\/td>\n<td>808<\/td>\n<td>27<\/td>\n<td><strong>254<\/strong> \/ 239<\/td>\n<\/tr>\n<tr>\n<td>South Africa Premiership<\/td>\n<td>358<\/td>\n<td>25<\/td>\n<td>223 \/ 178<\/td>\n<\/tr>\n<tr>\n<td>Zimbabwe Premier Soccer League<\/td>\n<td>23479<\/td>\n<td>9<\/td>\n<td>152<\/td>\n<\/tr>\n<tr>\n<td>Algeria Ligue 1<\/td>\n<td>841<\/td>\n<td>14<\/td>\n<td>141 \/ 140<\/td>\n<\/tr>\n<tr>\n<td>Uganda Premier League<\/td>\n<td>14864<\/td>\n<td>142<\/td>\n<td>132 \/ 129<\/td>\n<\/tr>\n<tr>\n<td>Egypt 2. Division A<\/td>\n<td>19492<\/td>\n<td>360<\/td>\n<td>101 \/ 101<\/td>\n<\/tr>\n<tr>\n<td>Tanzania Premier League<\/td>\n<td>2436<\/td>\n<td>205<\/td>\n<td>board not yet open<\/td>\n<\/tr>\n<tr>\n<td>CAF Champions League<\/td>\n<td>1054<\/td>\n<td>58<\/td>\n<td>board not yet open<\/td>\n<\/tr>\n<\/tbody>\n<\/table><figcaption>Live <code>\/v4\/odds<\/code> census, 2 September 2026. A census describes the moment you take it.<\/figcaption><\/figure>\n<p>Egypt&#8217;s second division carries 360 loaded fixtures, more than any top flight on the continent, and its matches still draw a hundred bookmakers each. Uganda&#8217;s top flight carries 142. If you are building a model for a league that European tipsters ignore, the data volume is there.<\/p>\n<h3>Three quarters of the board is a copy<\/h3>\n<p>Now the correction. Those 254 books do not give you 254 opinions. Group the quotes by their price tuple and the board collapses:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Fixture<\/th>\n<th>Complete + active 1X2<\/th>\n<th>Independent prices<\/th>\n<th>Collapse<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Egypt PL (14:00 kick-off)<\/td>\n<td>243<\/td>\n<td>77<\/td>\n<td>68.3%<\/td>\n<\/tr>\n<tr>\n<td>Egypt PL (17:00 kick-off)<\/td>\n<td>229<\/td>\n<td>63<\/td>\n<td>72.5%<\/td>\n<\/tr>\n<tr>\n<td>South Africa Premiership<\/td>\n<td>219<\/td>\n<td>66<\/td>\n<td>69.9%<\/td>\n<\/tr>\n<tr>\n<td>South Africa Premiership<\/td>\n<td>175<\/td>\n<td>38<\/td>\n<td><strong>78.3%<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Uganda Premier League<\/td>\n<td>81<\/td>\n<td>20<\/td>\n<td>75.3%<\/td>\n<\/tr>\n<tr>\n<td>Algeria Ligue 1<\/td>\n<td>131<\/td>\n<td>43<\/td>\n<td>67.2%<\/td>\n<\/tr>\n<tr>\n<td>Egypt 2. Division A<\/td>\n<td>101<\/td>\n<td>26<\/td>\n<td>74.3%<\/td>\n<\/tr>\n<tr>\n<td>Zimbabwe PSL<\/td>\n<td>88<\/td>\n<td>25<\/td>\n<td>71.6%<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>On the deepest Egypt fixture, <strong>27 separate slugs quoted the identical 2.63 \/ 2.75 \/ 2.90<\/strong>. That single tuple is 11% of the board on its own. Average across the raw slug list and you weight one pricing desk 27 times.<\/p>\n<p>This matters more here than it does on the Premier League. A wide European board still resolves to 104 to 112 independent feeds, which is enough for a stable consensus. A South Africa Premiership fixture resolves to 38. Our <a href=\"https:\/\/oddspapi.io\/blog\/how-many-bookmakers-backtest\/\">study on how many bookmakers a backtest needs<\/a> put the knee of the accuracy curve at 25 independent books, so an African board sits close to that floor rather than comfortably above it.<\/p>\n<h2>betPawa prices these leagues better than the brands do<\/h2>\n<p>Rank each board by bookmaker margin and the familiar names do badly. On the deeper Egypt Premier League fixture, bet365 ranked 231st of 243 at a 14.77% overround and Betway ranked 240th at 16.67%. 1xBet and 22bet clustered around 9.9% on every league we sampled.<\/p>\n<p><strong>betPawa CM<\/strong>, a book most European developers have never heard of, ranked in the single digits on four of the eight boards we ranked:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Fixture<\/th>\n<th>betPawa CM<\/th>\n<th>Pinnacle<\/th>\n<th>1xBet<\/th>\n<th>bet365<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Egypt PL<\/td>\n<td>7.11% (39th)<\/td>\n<td><strong>4.47% (14th)<\/strong><\/td>\n<td>9.85% (145th)<\/td>\n<td>14.77% (231st)<\/td>\n<\/tr>\n<tr>\n<td>Algeria Ligue 1<\/td>\n<td><strong>8.04% (3rd)<\/strong><\/td>\n<td>absent<\/td>\n<td>9.93% (52nd)<\/td>\n<td>absent<\/td>\n<\/tr>\n<tr>\n<td>Egypt 2. Division A<\/td>\n<td><strong>9.04% (4th)<\/strong><\/td>\n<td>9.18% (10th)<\/td>\n<td>10.15% (19th)<\/td>\n<td>absent<\/td>\n<\/tr>\n<tr>\n<td>Egypt 2. Division A<\/td>\n<td><strong>9.18% (9th)<\/strong><\/td>\n<td>9.28% (15th)<\/td>\n<td>10.18% (28th)<\/td>\n<td>absent<\/td>\n<\/tr>\n<tr>\n<td>South Africa Premiership<\/td>\n<td><strong>7.95% (7th)<\/strong><\/td>\n<td>absent<\/td>\n<td>9.89% (57th)<\/td>\n<td>13.43% (161st)<\/td>\n<\/tr>\n<\/tbody>\n<\/table><figcaption>Overround on the full-time 1X2 market, complete and active quotes only.<\/figcaption><\/figure>\n<p>On both Egypt second-division fixtures betPawa posted a tighter number than Pinnacle. That is a narrow win on a shallow market, and it does not make betPawa a sharp book. It does mean that a scanner which only reads the twelve bookmakers you recognise will miss the best price on these leagues most of the time.<\/p>\n<h3>Pinnacle&#8217;s margin scales with how obscure the league is<\/h3>\n<p>Pinnacle closes an English Premier League three-way at roughly 3%. Across the African boards it priced, the number climbed with the tier:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Competition<\/th>\n<th>Pinnacle 1X2 overround<\/th>\n<th>Rank on that board<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>English Premier League (closing, for reference)<\/td>\n<td>2.95% to 3.20%<\/td>\n<td>23rd to 24th of 175<\/td>\n<\/tr>\n<tr>\n<td>Egypt Premier League<\/td>\n<td>4.38% to 4.47%<\/td>\n<td>14th of 229 to 243<\/td>\n<\/tr>\n<tr>\n<td>South Africa Premiership<\/td>\n<td>7.54%<\/td>\n<td>19th of 219<\/td>\n<\/tr>\n<tr>\n<td>Egypt 2. Division A<\/td>\n<td>9.18% to 9.28%<\/td>\n<td>10th to 15th of 101<\/td>\n<\/tr>\n<tr>\n<td>Uganda Premier League<\/td>\n<td>11.56%<\/td>\n<td>41st of 81<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Pinnacle still prices a Ugandan league match, which is worth knowing on its own. It charges nearly four times its Premier League margin to do it, and 40 books on that board beat it. Treating Pinnacle as the automatic fair-value anchor works on Anfield and stops working in Kampala. Build your anchor from the de-vigged board instead, the way our <a href=\"https:\/\/oddspapi.io\/blog\/consensus-odds-fair-odds-calculator-python\/\">consensus odds calculator<\/a> does.<\/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 bookmaker sites<\/th>\n<th>OddsPapi <code>\/v4<\/code><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Find African competitions<\/td>\n<td>Guess URLs per operator<\/td>\n<td><code>\/tournaments?sportId=10<\/code>, one call<\/td>\n<\/tr>\n<tr>\n<td>Books per fixture<\/td>\n<td>One per scraper you maintain<\/td>\n<td>Up to 254 in one JSON response<\/td>\n<\/tr>\n<tr>\n<td>Local operators<\/td>\n<td>Geo-blocked, JS-rendered, CAPTCHA<\/td>\n<td>betPawa, Mozzart Bet, 1xBet, BetWinner in the same payload<\/td>\n<\/tr>\n<tr>\n<td>Duplicate feeds<\/td>\n<td>Invisible until you diff by hand<\/td>\n<td>Dedupe on the price tuple in four lines<\/td>\n<\/tr>\n<tr>\n<td>Historical prices<\/td>\n<td>Build your own recorder, wait a season<\/td>\n<td><code>\/historical-odds<\/code> on the free tier<\/td>\n<\/tr>\n<tr>\n<td>Cost<\/td>\n<td>Proxies, maintenance, breakage<\/td>\n<td>Free API key<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>Tutorial: pull and rank an African board in Python<\/h2>\n<p>Everything below runs against the live API with a free key. Authentication is a query parameter called <code>apiKey<\/code>, never a header.<\/p>\n<h3>Step 1: a request helper that survives the rate limit<\/h3>\n<p>The API rate-limits per endpoint and returns a real HTTP 429 with the wait time in the body. It also returns HTTP 404 for a date window with no fixtures, which is a normal result rather than a failure. Handle both once, at the bottom, and the rest of your code stays clean.<\/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\"\n\ndef call(path, **params):\n    params[\"apiKey\"] = API_KEY\n    for _ in range(5):\n        r = requests.get(f\"{BASE_URL}{path}\", params=params)\n        if r.status_code == 200:\n            return r.json()\n        if r.status_code == 404:          # empty window, not an error\n            return []\n        if r.status_code == 429:\n            time.sleep(r.json()[\"error\"][\"retryMs\"] \/ 1000 + 0.5)\n            continue\n        raise RuntimeError(f\"{r.status_code} {r.text[:200]}\")\n    raise RuntimeError(\"rate limited five times in a row\")<\/code><\/pre>\n<h3>Step 2: discover the African competitions<\/h3>\n<p>OddsPapi calls a league a <strong>tournament<\/strong>, a game a <strong>fixture<\/strong>, and a team a <strong>participant<\/strong>. Each tournament carries a <code>categoryName<\/code> holding the country, so filtering by country gets you the domestic leagues and a prefix match gets you the CAF cups.<\/p>\n<pre class=\"wp-block-code\"><code>AFRICA = {\"South Africa\", \"Egypt\", \"Algeria\", \"Morocco\", \"Tunisia\", \"Ghana\",\n          \"Kenya\", \"Nigeria\", \"Tanzania\", \"Uganda\", \"Zambia\", \"Zimbabwe\",\n          \"Angola\", \"Cameroon\", \"Ivory Coast\", \"Senegal\"}\n\ntours = call(\"\/tournaments\", sportId=10)\nafrican = [t for t in tours\n           if t[\"categoryName\"] in AFRICA or t[\"tournamentName\"].startswith(\"CAF\")]\nlive = [t for t in african if t[\"futureFixtures\"] &gt; 0]\n\nfor t in sorted(live, key=lambda x: -x[\"futureFixtures\"])[:8]:\n    print(f'{t[\"tournamentId\"]:6d}  {t[\"categoryName\"]:14s} '\n          f'{t[\"tournamentName\"]:26s} {t[\"futureFixtures\"]:4d}')<\/code><\/pre>\n<pre class=\"wp-block-code\"><code> 19492  Egypt          2. Division A                360\n  2436  Tanzania       Premier League               205\n 14864  Uganda         Premier League               142\n  1054  International Clubs CAF Champions League      58\n   808  Egypt          Premier League                27\n   358  South Africa   Premiership                   25<\/code><\/pre>\n<p>Read <code>futureFixtures &gt; 0<\/code> as proof that a competition is running. Do not read a zero as proof that it is dead. Several circuits report zero on <code>\/tournaments<\/code> while carrying priced fixtures inside 48 hours, so discover through <code>\/fixtures<\/code> whenever the count looks wrong.<\/p>\n<h3>Step 3: fixtures inside one competition<\/h3>\n<p><code>tournamentId<\/code> filters <code>\/fixtures<\/code> server-side and cuts the payload by roughly twenty times. Set <code>to<\/code> to the day <em>after<\/em> your last day, because the window ends at midnight UTC on the <code>to<\/code> date.<\/p>\n<pre class=\"wp-block-code\"><code>EGYPT_PL = 808\n\nfixtures = call(\"\/fixtures\", sportId=10, tournamentId=EGYPT_PL,\n                **{\"from\": \"2026-09-02\", \"to\": \"2026-09-12\"})\npriced = [f for f in fixtures if f.get(\"hasOdds\")]\nprint(len(fixtures), \"fixtures,\", len(priced), \"flagged hasOdds\")\n# 14 fixtures, 14 flagged hasOdds<\/code><\/pre>\n<h3>Step 4: parse the 1X2 board<\/h3>\n<p>The odds response nests four levels deep and the price object sits below <code>players[\"0\"]<\/code>, not on the outcome. Two flags decide whether a quote counts: <code>suspended<\/code> at the book level, and <code>active<\/code> on the price itself. A book can pull its market and keep returning numbers, and half a wide board is posted and suspended at any moment.<\/p>\n<pre class=\"wp-block-code\"><code>OUTCOMES = {\"101\": \"home\", \"102\": \"draw\", \"103\": \"away\"}\n\ndef one_x_two(payload):\n    \"\"\"{slug: {home, draw, away}} for complete, active, unsuspended quotes.\"\"\"\n    board = {}\n    for slug, book in payload.get(\"bookmakerOdds\", {}).items():\n        if book.get(\"suspended\") or book.get(\"bookmakerIsActive\") is False:\n            continue\n        market = book.get(\"markets\", {}).get(\"101\")\n        if not market or not market.get(\"marketActive\", True):\n            continue\n        quote = {}\n        for oid, name in OUTCOMES.items():\n            price = market[\"outcomes\"].get(oid, {}).get(\"players\", {}).get(\"0\")\n            if not price or not price.get(\"active\") or not price.get(\"price\"):\n                quote = None\n                break\n            quote[name] = price[\"price\"]\n        if quote:\n            board[slug] = quote\n    return board\n\nfixture = sorted(priced, key=lambda f: f[\"startTime\"])[0]\nodds = call(\"\/odds\", fixtureId=fixture[\"fixtureId\"])\nboard = one_x_two(odds)\n\nprint(len(odds[\"bookmakerOdds\"]), \"books in payload\")\nprint(len(board), \"complete and active 1X2 quotes\")\n# 254 books in payload\n# 243 complete and active 1X2 quotes<\/code><\/pre>\n<h3>Step 5: dedupe before you average anything<\/h3>\n<p>Group by the rounded price tuple. Anything that shares a tuple shares a pricing desk, whatever the catalogue says about clone flags.<\/p>\n<pre class=\"wp-block-code\"><code>tuples = collections.Counter(\n    tuple(round(q[n], 4) for n in (\"home\", \"draw\", \"away\"))\n    for q in board.values())\n\ncollapse = 1 - len(tuples) \/ len(board)\nbiggest, count = tuples.most_common(1)[0]\n\nprint(f\"{len(tuples)} independent prices, {collapse:.1%} of the board is a copy\")\nprint(f\"largest tuple: {count} slugs at {biggest}\")\n# 77 independent prices, 68.3% of the board is a copy\n# largest tuple: 27 slugs at (2.63, 2.75, 2.9)<\/code><\/pre>\n<h3>Step 6: rank the board by margin<\/h3>\n<pre class=\"wp-block-code\"><code>def margin(q):\n    return sum(1 \/ q[n] for n in (\"home\", \"draw\", \"away\")) - 1\n\nranked = sorted(board.items(), key=lambda kv: margin(kv[1]))\nplace = {slug: i + 1 for i, (slug, _) in enumerate(ranked)}\n\nfor slug in (\"pinnacle\", \"betpawa.cm\", \"1xbet\", \"bet365\"):\n    if slug in place:\n        print(f\"{slug:12s} rank {place[slug]:3d} of {len(ranked)}  \"\n              f\"{margin(board[slug]):.2%}\")\n# pinnacle     rank  14 of 243  4.47%\n# betpawa.cm   rank  39 of 243  7.11%\n# 1xbet        rank 145 of 243  9.85%\n# bet365       rank 231 of 243  14.77%<\/code><\/pre>\n<p>The full method, including the power and Shin de-vig variants, is in our <a href=\"https:\/\/oddspapi.io\/blog\/vig-calculator-python-sportsbook-margin\/\">vig calculator walkthrough<\/a>.<\/p>\n<h2>Three traps specific to these boards<\/h2>\n<h3>The board opens about 36 hours out<\/h3>\n<p>Every African fixture that returned bookmakers kicked off within roughly 48 hours of the call. Every fixture kicking off two or more days later returned <em>zero<\/em> bookmakers while still reporting <code>hasOdds: true<\/code>. The flag describes the fixture, not the moment.<\/p>\n<p>The history confirms the window. On a South Africa Premiership match that kicked off at 17:30 UTC on 25 August, the first snapshots landed at 06:54 (betPawa), 09:12 (1xBet) and 10:11 (Pinnacle) on 24 August. That is 31 to 35 hours before kick-off.<\/p>\n<p>So poll inside the window, and test your coverage against a played fixture through <code>\/historical-odds<\/code>. A live call on a Thursday match asked for on Monday reports a false zero.<\/p>\n<h3>An exchange is not automatically the tight book<\/h3>\n<p>On the Egypt Premier League fixture, five exchange slugs shared the tightest quote on the board at a 1.80% overround. On Algeria Ligue 1 the same <code>betfair-ex<\/code> slug quoted 1.60 \/ 1.25 \/ 1.60, which sums to a <strong>105% overround<\/strong>, and 1.14 \/ 1.27 \/ 1.14 on the second fixture, which sums to 154%. Those are near-empty ladders on an illiquid market, and they rank 127th of 131.<\/p>\n<p>Guard the ranking rather than trusting the venue type:<\/p>\n<pre class=\"wp-block-code\"><code>SANE = {slug: q for slug, q in board.items() if 0.005 &lt;= margin(q) &lt;= 0.30}<\/code><\/pre>\n<h3>A sub-100% best price is usually stale, not free money<\/h3>\n<p>Take the best price on each outcome across the whole board and the composite sometimes sums to less than 1.00. On one Uganda fixture it came out at 0.924. That is not an arbitrage you can take. On a board with 20 independent prices it is one book that stopped updating, and a stale longshot inflates the composite. Our <a href=\"https:\/\/oddspapi.io\/blog\/line-shopping-python-best-odds\/\">line shopping guide<\/a> covers the sanity checks that keep an outlier out of the number.<\/p>\n<p>Where the best-price gap is real, it is large. Against the full Egypt Premier League board, the best available price beat betPawa&#8217;s own number by up to 10.9%, 1xBet by 18.0% and Betway by 19.0% on a single outcome.<\/p>\n<h2>Debugging the bookmakers filter<\/h2>\n<p>Passing <code>bookmakers=<\/code> gives you three distinct outcomes, and reading the status code tells you which problem you have.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Status<\/th>\n<th>Code<\/th>\n<th>Meaning<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>400<\/td>\n<td><code>INVALID_PARAMETER<\/code><\/td>\n<td>The slug is not in the catalogue. The error body lists every valid slug, so a deliberate 400 is a free catalogue dump.<\/td>\n<\/tr>\n<tr>\n<td>403<\/td>\n<td><code>RESTRICTED_ACCESS<\/code><\/td>\n<td>The slug is real but your plan does not read it.<\/td>\n<\/tr>\n<tr>\n<td>200<\/td>\n<td>&#8211;<\/td>\n<td>The slug is valid and simply not on that fixture. Use <code>.get()<\/code> rather than indexing.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<pre class=\"wp-block-code\"><code>r = requests.get(f\"{BASE_URL}\/odds\",\n                 params={\"apiKey\": API_KEY, \"fixtureId\": fid,\n                         \"bookmakers\": \"betking\"})\nprint(r.status_code, r.json()[\"error\"][\"code\"])\n# 400 INVALID_PARAMETER   (the real slug set is what the body returns)<\/code><\/pre>\n<h2>Backtesting an African league for free<\/h2>\n<p>Historical prices are the part competitors put behind a paywall. On these leagues they are on the free tier, and finished fixtures keep their history after live prices drop off.<\/p>\n<pre class=\"wp-block-code\"><code># max 3 bookmakers per call; retry a failed batch one slug at a time\nhist = call(\"\/historical-odds\", fixtureId=played_id,\n            bookmakers=\"pinnacle,betpawa.cm,1xbet\")\n\nfor slug, book in hist[\"bookmakers\"].items():\n    snaps = book[\"markets\"][\"101\"][\"outcomes\"][\"101\"][\"players\"][\"0\"]\n    print(slug, len(book[\"markets\"]), \"markets,\", len(snaps), \"snapshots,\",\n          \"opened\", snaps[0][\"createdAt\"][:16])\n# pinnacle   50 markets, 180 snapshots, opened 2026-08-24T10:11\n# 1xbet      90 markets,  87 snapshots, opened 2026-08-24T09:12\n# betpawa.cm 72 markets,  73 snapshots, opened 2026-08-24T06:54<\/code><\/pre>\n<p>Note the shape difference. The top level is <code>bookmakers<\/code> here, not <code>bookmakerOdds<\/code>, and <code>players[\"0\"]<\/code> is a list of snapshots rather than a single price. Pinnacle repriced the home side twice as often as 1xBet on little more than half the menu, which is the pattern you would expect from a book that trades a market rather than parking it.<\/p>\n<p>That one call returned 3.01 MB. Loop three slugs at a time, sleep about 4.5 seconds between calls, and filter <code>createdAt &lt; startTime<\/code> when you want a true closing line, because snapshots keep recording into the second half.<\/p>\n<h2>What to build with this<\/h2>\n<p>Two things follow from the numbers. Dedupe first, because a 68% collapse means an unweighted average is really a vote by one desk repeated 27 times. Then widen, because the books that price these leagues tightest are not the ones with the marketing budget.<\/p>\n<p>Egypt&#8217;s second division alone carries 360 loaded fixtures with a hundred bookmakers each. That is a season of training data for a league nobody is modelling.<\/p>\n<p>Grab a <a href=\"https:\/\/oddspapi.io\/\">free OddsPapi API key<\/a> and run the census yourself. Twelve competitions, 264 distinct bookmakers, no sales call. If you want the same treatment applied to a market with a similar local-versus-global split, our <a href=\"https:\/\/oddspapi.io\/blog\/brasileirao-odds-api\/\">Brasileir\u00e3o breakdown<\/a> found 10 Brazilian slugs collapsing to 3 genuinely independent prices, and the <a href=\"https:\/\/oddspapi.io\/blog\/free-odds-api-350-bookmakers\/\">free odds API overview<\/a> covers the rest of the catalogue.<\/p>\n<h2>Frequently Asked Questions<\/h2>\n<h3>Which African football leagues does the OddsPapi API cover?<\/h3>\n<p>The soccer catalogue holds 55 African competitions, including the Egypt Premier League, Egypt 2. Division A, South Africa Premiership, Uganda Premier League, Tanzania Premier League, Algeria Ligue 1, Ghana Premier League, Kenya Premier League, Tunisia Ligue 1, Morocco Botola Pro, Zimbabwe Premier Soccer League, plus the CAF Champions League and CAF Confederations Cup. Query <code>\/v4\/tournaments?sportId=10<\/code> and filter on <code>categoryName<\/code> for the current list.<\/p>\n<h3>How many bookmakers price an African football match?<\/h3>\n<p>On 2 September 2026, an Egypt Premier League fixture carried 254 bookmakers and a Uganda Premier League fixture carried 132. Across 12 sampled fixtures we saw 264 distinct bookmaker slugs. Dedupe on the price tuple before you use those counts, because 67% to 78% of the quotes on any one board repeat another slug&#8217;s exact prices.<\/p>\n<h3>Does Pinnacle price African leagues?<\/h3>\n<p>Yes, including the Uganda Premier League and Egypt&#8217;s second division. Its margin runs wider than on European football: 4.4% on the Egypt Premier League, 7.5% on the South Africa Premiership and 11.6% on a Uganda Premier League fixture, against roughly 3% on an English Premier League close. Forty books beat Pinnacle on that Uganda board, so build your fair-value anchor from the de-vigged consensus rather than from one book.<\/p>\n<h3>Which bookmaker gives the tightest African football odds?<\/h3>\n<p>betPawa CM ranked in the single digits on four of the eight boards we ranked, including 3rd of 128 on Algeria Ligue 1 and 4th of 100 on Egypt 2. Division A, where it posted a tighter overround than Pinnacle. The bigger international brands ranked far lower on the same fixtures, with bet365 at 231st of 243 and Betway at 240th on one Egypt Premier League match.<\/p>\n<h3>When do odds open for African football fixtures?<\/h3>\n<p>Roughly 36 hours before kick-off. On a South Africa Premiership match, the first recorded snapshots landed 31 to 35 hours out. Fixtures more than two days away return <code>hasOdds: true<\/code> with no bookmakers attached, so test coverage against a played fixture through <code>\/historical-odds<\/code> rather than a live <code>\/odds<\/code> call.<\/p>\n<h3>Is historical African football odds data free?<\/h3>\n<p>Yes. <code>\/v4\/historical-odds<\/code> is on the free tier and finished fixtures keep their full snapshot history after live prices drop. One South Africa Premiership fixture returned 3.01 MB across three bookmakers, with 180 Pinnacle snapshots on the home side alone. The endpoint caps at three bookmakers per call, so loop and merge.<\/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\": \"Which African football leagues does the OddsPapi API cover?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"The soccer catalogue holds 55 African competitions, including the Egypt Premier League, Egypt 2. Division A, South Africa Premiership, Uganda Premier League, Tanzania Premier League, Algeria Ligue 1, Ghana Premier League, Kenya Premier League, Tunisia Ligue 1, Morocco Botola Pro, Zimbabwe Premier Soccer League, plus the CAF Champions League and CAF Confederations Cup. Query \/v4\/tournaments?sportId=10 and filter on categoryName for the current list.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How many bookmakers price an African football match?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"On 2 September 2026, an Egypt Premier League fixture carried 254 bookmakers and a Uganda Premier League fixture carried 132. Across 12 sampled fixtures we saw 264 distinct bookmaker slugs. Dedupe on the price tuple before you use those counts, because 67% to 78% of the quotes on any one board repeat another slug's exact prices.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Does Pinnacle price African leagues?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes, including the Uganda Premier League and Egypt's second division. Its margin runs wider than on European football: 4.4% on the Egypt Premier League, 7.5% on the South Africa Premiership and 11.6% on a Uganda Premier League fixture, against roughly 3% on an English Premier League close. Forty books beat Pinnacle on that Uganda board, so build your fair-value anchor from the de-vigged consensus rather than from one book.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Which bookmaker gives the tightest African football odds?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"betPawa CM ranked in the single digits on four of the eight boards we ranked, including 3rd of 128 on Algeria Ligue 1 and 4th of 100 on Egypt 2. Division A, where it posted a tighter overround than Pinnacle. The bigger international brands ranked far lower on the same fixtures, with bet365 at 231st of 243 and Betway at 240th on one Egypt Premier League match.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"When do odds open for African football fixtures?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Roughly 36 hours before kick-off. On a South Africa Premiership match, the first recorded snapshots landed 31 to 35 hours out. Fixtures more than two days away return hasOdds: true with no bookmakers attached, so test coverage against a played fixture through \/historical-odds rather than a live \/odds call.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Is historical African football odds data free?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes. \/v4\/historical-odds is on the free tier and finished fixtures keep their full snapshot history after live prices drop. One South Africa Premiership fixture returned 3.01 MB across three bookmakers, with 180 Pinnacle snapshots on the home side alone. The endpoint caps at three bookmakers per call, so loop and merge.\"\n      }\n    }\n  ]\n}\n<\/script><\/p>\n<p><!--\nFocus Keyphrase: african football odds api\nSEO Title: African Football Odds API: betPawa Undercuts Pinnacle in Egypt's Tier Two\nMeta Description: Pull African football odds for 12 competitions in Python. An Egyptian Premier League fixture carried 254 bookmakers, and 68% of the prices were copies.\nSlug: african-football-odds-api\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pull African football odds for 12 competitions in Python. An Egyptian Premier League fixture carried 254 bookmakers, and 68% of the prices were copies.<\/p>\n","protected":false},"author":2,"featured_media":3879,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[12,9,11,15,10],"class_list":["post-3878","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to-guides","tag-betting-data","tag-odds-api","tag-python","tag-soccer","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>African Football Odds API: betPawa Undercuts Pinnacle in Egypt&#039;s Tier Two | 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\/african-football-odds-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"African Football Odds API: betPawa Undercuts Pinnacle in Egypt&#039;s Tier Two | OddsPapi Blog\" \/>\n<meta property=\"og:description\" content=\"Pull African football odds for 12 competitions in Python. An Egyptian Premier League fixture carried 254 bookmakers, and 68% of the prices were copies.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/\" \/>\n<meta property=\"og:site_name\" content=\"OddsPapi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-11T10:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-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=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"African Football Odds API: betPawa Undercuts Pinnacle in Egypt&#8217;s Tier Two\",\"datePublished\":\"2026-09-11T10:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/\"},\"wordCount\":2154,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp\",\"keywords\":[\"Betting Data\",\"Odds API\",\"Python\",\"Soccer\",\"Sports Betting API\"],\"articleSection\":[\"How To Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/\",\"name\":\"African Football Odds API: betPawa Undercuts Pinnacle in Egypt's Tier Two | OddsPapi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp\",\"datePublished\":\"2026-09-11T10:00:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp\",\"width\":2560,\"height\":1429,\"caption\":\"African Football Odds API - OddsPapi API Blog\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"African Football Odds API: betPawa Undercuts Pinnacle in Egypt&#8217;s Tier Two\"}]},{\"@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":"African Football Odds API: betPawa Undercuts Pinnacle in Egypt's Tier Two | 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\/african-football-odds-api\/","og_locale":"en_US","og_type":"article","og_title":"African Football Odds API: betPawa Undercuts Pinnacle in Egypt's Tier Two | OddsPapi Blog","og_description":"Pull African football odds for 12 competitions in Python. An Egyptian Premier League fixture carried 254 bookmakers, and 68% of the prices were copies.","og_url":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/","og_site_name":"OddsPapi Blog","article_published_time":"2026-09-11T10:00:00+00:00","og_image":[{"width":2560,"height":1429,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-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":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"African Football Odds API: betPawa Undercuts Pinnacle in Egypt&#8217;s Tier Two","datePublished":"2026-09-11T10:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/"},"wordCount":2154,"commentCount":0,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp","keywords":["Betting Data","Odds API","Python","Soccer","Sports Betting API"],"articleSection":["How To Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/","url":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/","name":"African Football Odds API: betPawa Undercuts Pinnacle in Egypt's Tier Two | OddsPapi Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp","datePublished":"2026-09-11T10:00:00+00:00","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/african-football-odds-api\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/african-football-odds-api-scaled.webp","width":2560,"height":1429,"caption":"African Football Odds API - OddsPapi API Blog"},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/african-football-odds-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"African Football Odds API: betPawa Undercuts Pinnacle in Egypt&#8217;s Tier Two"}]},{"@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\/3878","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=3878"}],"version-history":[{"count":1,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/3878\/revisions"}],"predecessor-version":[{"id":3880,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/3878\/revisions\/3880"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/3879"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=3878"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=3878"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=3878"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}