{"id":4414,"date":"2026-10-05T10:00:00","date_gmt":"2026-10-05T10:00:00","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=4414"},"modified":"2026-09-23T15:24:58","modified_gmt":"2026-09-23T15:24:58","slug":"esports-results-api","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/esports-results-api\/","title":{"rendered":"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series"},"content":{"rendered":"<p>Looking for an esports results API? You need the final score of a CS2, League of Legends or Dota 2 series, keyed to the same fixture ID as the odds you bet into. Most esports data products sell one or the other. The stats vendors sell scores and player telemetry. The odds feeds sell prices and forget the fixture once it finishes.<\/p>\n<p>OddsPapi returns both on one free key. <code>\/v4\/scores<\/code> gives the series score and, on LoL and Dota 2, the map-by-map result. <code>\/v4\/settlements<\/code> grades every Winner, Maps Handicap and Total Maps outcome. <code>\/v4\/historical-odds<\/code> replays the closing price on the same fixture ID. This guide runs all three over every CS2, LoL and Dota 2 fixture that finished between 13 and 23 September 2026, measured on 23 September.<\/p>\n<p>The result: <strong>303 decided series<\/strong>, a self-built grader that agreed with <code>\/v4\/settlements<\/code> on <strong>2,520 of 2,520 outcomes<\/strong>, and a closing favourite that won 69.6% of the time on Pinnacle&#8217;s price. If you only need live esports prices, <a href=\"https:\/\/oddspapi.io\/blog\/esports-odds-api-guide-how-to-get-pinnacle-cs2-lol-data-for-free\/\">the esports odds API guide<\/a> covers the board. This post picks up after the final map.<\/p>\n<h2>Why esports results and esports odds usually live in two products<\/h2>\n<p>The esports stats vendors built their products for broadcasters and team analysts. You get kills, rounds and player telemetry on their own fixture IDs, and you map those IDs to your odds by hand. <a href=\"https:\/\/oddspapi.io\/blog\/pandascore-api-alternative-free-esports-odds\/\">The PandaScore comparison<\/a> covers what a stats plan does and does not include. The generic odds APIs carry the price and drop the fixture once it finishes, so you grade your own bets by hand.<\/p>\n<p>That gap hurts in three places: settling a bet, labelling a training set, and auditing your closing line value. Each one needs the price and the result on the same key.<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Job<\/th>\n<th>Stats vendor plus odds feed<\/th>\n<th>OddsPapi, measured 23 September 2026<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Series score<\/td>\n<td>Stats API, separate fixture IDs to map<\/td>\n<td><code>\/v4\/scores<\/code>: 303 CS2, LoL and Dota 2 series<\/td>\n<\/tr>\n<tr>\n<td>Map-by-map score<\/td>\n<td>Stats API<\/td>\n<td><code>\/v4\/scores<\/code> <code>p1<\/code> to <code>p5<\/code>: 68 Dota 2 and 41 LoL series<\/td>\n<\/tr>\n<tr>\n<td>Graded bets<\/td>\n<td>Your own code<\/td>\n<td><code>\/v4\/settlements<\/code>: 2,220 decided outcomes on 45 series checked<\/td>\n<\/tr>\n<tr>\n<td>Closing price<\/td>\n<td>A second vendor, if it keeps history<\/td>\n<td><code>\/v4\/historical-odds<\/code>: 266 series with a closing Winner price<\/td>\n<\/tr>\n<tr>\n<td>Fixture ID shared with the odds<\/td>\n<td>No, you map two ID systems<\/td>\n<td>Yes, one <code>fixtureId<\/code> on all four endpoints<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>Step 1: pull the played fixtures<\/h2>\n<p>Esports sit under their own sport IDs: 16 is Dota 2, 17 is Counter-Strike and 18 is League of Legends. <code>\/v4\/fixtures<\/code> rejects a <code>sportId<\/code>-only range wider than ten days, so the window below is exactly ten. Treat a 404 as an empty window. The key goes in the query string, never in a header.<\/p>\n<pre class=\"wp-block-code\"><code>import os, time, requests\nfrom datetime import datetime, timedelta, timezone\n\nAPI_KEY = os.environ[&quot;ODDSPAPI_API_KEY&quot;]\nBASE = &quot;https:\/\/api.oddspapi.io\/v4&quot;\n\ndef get(path, **params):\n    &quot;&quot;&quot;GET with the key as a query parameter; honours retryMs on a 429.&quot;&quot;&quot;\n    params[&quot;apiKey&quot;] = API_KEY\n    for attempt in range(6):\n        r = requests.get(f&quot;{BASE}\/{path}&quot;, params=params, timeout=120)\n        if r.status_code == 429:\n            time.sleep(r.json()[&quot;error&quot;].get(&quot;retryMs&quot;, 1000) \/ 1000 + 0.5)\n            continue\n        time.sleep(1.1)\n        return r\n    return r\n\nESPORTS = {16: &quot;Dota 2&quot;, 17: &quot;CS2&quot;, 18: &quot;LoL&quot;}\nend = datetime(2026, 9, 23, tzinfo=timezone.utc)\nstart = end - timedelta(days=10)          # \/fixtures caps a sportId-only range at 10 days\n\nplayed = []\nfor sport_id, name in ESPORTS.items():\n    r = get(&quot;fixtures&quot;, sportId=sport_id,\n            **{&quot;from&quot;: start.strftime(&quot;%Y-%m-%d&quot;), &quot;to&quot;: end.strftime(&quot;%Y-%m-%d&quot;)})\n    if r.status_code == 404:              # an empty window, not an error\n        continue\n    for f in r.json():\n        if f.get(&quot;statusName&quot;) == &quot;Finished&quot;:\n            f[&quot;esport&quot;] = name\n            played.append(f)\n\nprint(len(played), &quot;finished fixtures between&quot;, start.date(), &quot;and&quot;, end.date())\nfor name in ESPORTS.values():\n    print(f&quot;  {name:7s}&quot;, sum(f[&quot;esport&quot;] == name for f in played))<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>305 finished fixtures between 2026-09-13 and 2026-09-23\n  Dota 2  69\n  CS2     180\n  LoL     56<\/code><\/pre>\n<p>Keep only <code>statusName == \"Finished\"<\/code>. A series that still reads <code>Live<\/code> can carry a part-played score such as <code>1-0<\/code> in a best-of-three, and a grader would settle it early.<\/p>\n<h2>Step 2: read the score in all three shapes<\/h2>\n<p><code>\/v4\/scores<\/code> nests everything under <code>scores.periods<\/code>. <code>result<\/code> holds the series score as maps won. What sits next to it depends on the title and the series length:<\/p>\n<ul>\n<li><strong>Dota 2 and LoL best-of-three or best-of-five:<\/strong> <code>p1<\/code> to <code>p5<\/code>, one key per map played, each scored <code>1-0<\/code> or <code>0-1<\/code>, plus <code>fulltime<\/code>. On all 109 series with map keys, the map scores summed exactly to <code>result<\/code>.<\/li>\n<li><strong>LoL best-of-one:<\/strong> <code>result<\/code> only, scored <code>1-0<\/code> or <code>0-1<\/code>. That covers the other 15 LoL series.<\/li>\n<li><strong>CS2:<\/strong> <code>result<\/code> only, as maps won. A <code>2-1<\/code> tells you the series went the distance but not which map each side took.<\/li>\n<\/ul>\n<pre class=\"wp-block-code\"><code>def read_result(fixture_id):\n    &quot;&quot;&quot;Return the series score and, where the feed carries them, the map-by-map scores.&quot;&quot;&quot;\n    r = get(&quot;scores&quot;, fixtureId=fixture_id)\n    if r.status_code == 404:              # no result posted yet\n        return None\n    periods = r.json()[&quot;scores&quot;][&quot;periods&quot;]\n    series = periods.get(&quot;result&quot;)\n    if series is None:\n        return None\n    maps = [periods[k] for k in (&quot;p1&quot;, &quot;p2&quot;, &quot;p3&quot;, &quot;p4&quot;, &quot;p5&quot;) if k in periods]\n    return {\n        &quot;series&quot;: (series[&quot;participant1Score&quot;], series[&quot;participant2Score&quot;]),\n        &quot;maps&quot;: [(m[&quot;participant1Score&quot;], m[&quot;participant2Score&quot;]) for m in maps] or None,\n    }\n\nresults = {}\nfor f in played:\n    res = read_result(f[&quot;fixtureId&quot;])\n    if res and res[&quot;series&quot;][0] != res[&quot;series&quot;][1]:\n        results[f[&quot;fixtureId&quot;]] = res\n\nprint(len(results), &quot;decided series&quot;)\nfor name in ESPORTS.values():\n    ids = [f[&quot;fixtureId&quot;] for f in played if f[&quot;esport&quot;] == name and f[&quot;fixtureId&quot;] in results]\n    with_maps = sum(results[i][&quot;maps&quot;] is not None for i in ids)\n    print(f&quot;  {name:7s} {len(ids):4d} series, {with_maps:4d} with map-by-map scores&quot;)\n\nsample = next(f for f in played if f[&quot;esport&quot;] == &quot;LoL&quot; and f[&quot;fixtureId&quot;] in results\n              and results[f[&quot;fixtureId&quot;]][&quot;maps&quot;])\nprint(sample[&quot;participant1Name&quot;], &quot;v&quot;, sample[&quot;participant2Name&quot;], results[sample[&quot;fixtureId&quot;]])<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>303 decided series\n  Dota 2    68 series,   68 with map-by-map scores\n  CS2      179 series,    0 with map-by-map scores\n  LoL       56 series,   41 with map-by-map scores\nHanwha Life Esports v Gen.g Esports {&#x27;series&#x27;: (1, 3), &#x27;maps&#x27;: [(1, 0), (0, 1), (0, 1), (0, 1)]}<\/code><\/pre>\n<p>The normalised record keeps <code>maps<\/code> as <code>None<\/code> when the payload carries no per-map keys. That one field decides what you can grade in the next step.<\/p>\n<h2>Step 3: grade the bets, then check against \/v4\/settlements<\/h2>\n<p>The series score settles three market families on its own: Winner, Maps Handicap and Total Maps Over Under. Map winner markets need the per-map keys. Two details trip up a hand-written grader:<\/p>\n<ul>\n<li>The <code>handicap<\/code> value in the catalogue belongs to participant 1. On LoL market 1839 (Maps Handicap 2.0), participant 1 gets +2.0 and participant 2 gets -2.0.<\/li>\n<li>Outcome names on Winner and Maps Handicap are literally <code>\"1\"<\/code> and <code>\"2\"<\/code>, and the outcome array order changes from market to market. Read the name, never the position.<\/li>\n<\/ul>\n<pre class=\"wp-block-code\"><code>def grade(res, market, side, line=0.0):\n    &quot;&quot;&quot;Grade a series bet from the score. side 1\/2 = participant, &#x27;over&#x27;\/&#x27;under&#x27; for totals.&quot;&quot;&quot;\n    a, b = res[&quot;series&quot;]\n    if market == &quot;Winner&quot;:\n        return &quot;WIN&quot; if (a &gt; b) == (side == 1) else &quot;LOSE&quot;\n    if market == &quot;Maps Handicap&quot;:         # the catalogue line belongs to participant 1\n        x = (a - b + line) if side == 1 else (b - a - line)\n        return &quot;PUSH&quot; if x == 0 else (&quot;WIN&quot; if x &gt; 0 else &quot;LOSE&quot;)\n    if market == &quot;Total Maps Over Under&quot;:\n        maps = a + b\n        return &quot;PUSH&quot; if maps == line else (&quot;WIN&quot; if (maps &gt; line) == (side == &quot;over&quot;) else &quot;LOSE&quot;)\n    if market.endswith(&quot;Map Winner (incl. overtime)&quot;):\n        idx = [&quot;First&quot;, &quot;Second&quot;, &quot;Third&quot;, &quot;Fourth&quot;, &quot;Fifth&quot;].index(market.split()[0])\n        if not res[&quot;maps&quot;] or idx &gt;= len(res[&quot;maps&quot;]):\n            return &quot;UNDECIDED&quot;            # no per-map score, or the map was never played\n        m1, m2 = res[&quot;maps&quot;][idx]\n        return &quot;WIN&quot; if (m1 &gt; m2) == (side == 1) else &quot;LOSE&quot;\n\ncatalogue = {str(m[&quot;marketId&quot;]): m for m in get(&quot;markets&quot;).json()}\n\nfid = sample[&quot;fixtureId&quot;]\nsettled = get(&quot;settlements&quot;, fixtureId=fid).json()[&quot;markets&quot;]\nagree = total = 0\nfor market_id, m in settled.items():\n    meta = catalogue[market_id]\n    for o in meta[&quot;outcomes&quot;]:\n        theirs = m[&quot;outcomes&quot;][str(o[&quot;outcomeId&quot;])][&quot;players&quot;][&quot;0&quot;][&quot;result&quot;]\n        name = meta[&quot;marketName&quot;]\n        if name == &quot;Total Maps Over Under&quot;:\n            side = &quot;over&quot; if o[&quot;outcomeName&quot;].lower().startswith(&quot;over&quot;) else &quot;under&quot;\n        else:\n            side = int(o[&quot;outcomeName&quot;])  # outcome names are literally &quot;1&quot; and &quot;2&quot;\n        ours = grade(results[fid], name, side, meta.get(&quot;handicap&quot;) or 0.0)\n        total += 1\n        agree += ours == theirs\nprint(f&quot;{agree}\/{total} outcomes agree with \/settlements on {sample[&#x27;participant1Name&#x27;]} v {sample[&#x27;participant2Name&#x27;]}&quot;)<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>56\/56 outcomes agree with \/settlements on Hanwha Life Esports v Gen.g Esports<\/code><\/pre>\n<p>One fixture proves little, so we ran the same grader over 45 series, 15 from each title, picked at random. <strong>It matched <code>\/v4\/settlements<\/code> on 2,520 of 2,520 outcomes<\/strong>: 2,220 graded WIN, LOSE or PUSH and the rest UNDECIDED. The UNDECIDED rows are map markets for maps the series never reached, or for a CS2 series where the feed carries no per-map key. Your grader should return the same thing rather than guess.<\/p>\n<p>Use your own grader when you need a result the second a series ends. Use <code>\/v4\/settlements<\/code> when you want an independent check before you move money. For a full operator settlement pipeline, <a href=\"https:\/\/oddspapi.io\/blog\/odds-feed-provider-sportsbooks-trading-desks\/\">the odds feed provider guide<\/a> covers the grid on football.<\/p>\n<h2>Step 4: join the closing price and grade the favourite<\/h2>\n<p>Finished fixtures ship <code>hasOdds: false<\/code>, so the live <code>\/v4\/odds<\/code> endpoint cannot give you a closing price. <code>\/v4\/historical-odds<\/code> can. It replays every snapshot a book posted, at most three books per call. The closing price is the last <em>active<\/em> snapshot before the series started, not the last snapshot in the list.<\/p>\n<pre class=\"wp-block-code\"><code>WINNER = {&quot;Dota 2&quot;: (161, 162), &quot;CS2&quot;: (171, 172), &quot;LoL&quot;: (181, 182)}\nBOOKS = &quot;pinnacle,bet365,1xbet&quot;          # \/historical-odds takes at most 3 slugs a call\n\ndef closing_prices(fixture):\n    &quot;&quot;&quot;Last ACTIVE pre-match price per book on the series Winner market.&quot;&quot;&quot;\n    kickoff = datetime.fromisoformat((fixture.get(&quot;trueStartTime&quot;) or fixture[&quot;startTime&quot;]).replace(&quot;Z&quot;, &quot;+00:00&quot;))\n    r = get(&quot;historical-odds&quot;, fixtureId=fixture[&quot;fixtureId&quot;], bookmakers=BOOKS)\n    time.sleep(4.6)                       # \/historical-odds wants a longer gap\n    if r.status_code != 200:\n        return {}\n    m1, m2 = WINNER[fixture[&quot;esport&quot;]]\n    out = {}\n    for slug, book in r.json()[&quot;bookmakers&quot;].items():\n        market = book[&quot;markets&quot;].get(str(m1))\n        if not market:\n            continue\n        legs = []\n        for oid in (m1, m2):\n            snaps = market[&quot;outcomes&quot;].get(str(oid), {}).get(&quot;players&quot;, {}).get(&quot;0&quot;, [])\n            live = [s for s in snaps if s[&quot;active&quot;] and s[&quot;price&quot;] &gt; 1.0\n                    and datetime.fromisoformat(s[&quot;createdAt&quot;].replace(&quot;Z&quot;, &quot;+00:00&quot;)) &lt; kickoff]\n            legs.append(live[-1][&quot;price&quot;] if live else None)\n        if None not in legs:\n            out[slug] = tuple(legs)\n    return out\n\ngraded = []\nfor f in played:\n    if f[&quot;fixtureId&quot;] not in results:\n        continue\n    closes = closing_prices(f)\n    if not closes:\n        continue\n    a, b = results[f[&quot;fixtureId&quot;]][&quot;series&quot;]\n    graded.append({&quot;esport&quot;: f[&quot;esport&quot;], &quot;p1_won&quot;: a &gt; b, &quot;closes&quot;: closes})\n\ndef brier(rows, slug):\n    &quot;&quot;&quot;Mean squared error of the de-vigged closing probability for participant 1.&quot;&quot;&quot;\n    errs = []\n    for g in rows:\n        if slug in g[&quot;closes&quot;]:\n            p1, p2 = (1 \/ x for x in g[&quot;closes&quot;][slug])\n            p = p1 \/ (p1 + p2)\n            errs.append((p - g[&quot;p1_won&quot;]) ** 2)\n    return sum(errs) \/ len(errs), len(errs)\n\nprint(len(graded), &quot;decided series with a closing Winner price&quot;)\nfor slug in BOOKS.split(&quot;,&quot;):\n    rows = [g for g in graded if slug in g[&quot;closes&quot;] and g[&quot;closes&quot;][slug][0] != g[&quot;closes&quot;][slug][1]]\n    fav_won = sum((g[&quot;closes&quot;][slug][0] &lt; g[&quot;closes&quot;][slug][1]) == g[&quot;p1_won&quot;] for g in rows)\n    score, n = brier(graded, slug)\n    print(f&quot;  {slug:9s} n={n:3d}  favourite won {fav_won}\/{len(rows)} ({fav_won \/ len(rows):.1%})  Brier {score:.4f}&quot;)\n\ncommon = [g for g in graded if all(s in g[&quot;closes&quot;] for s in BOOKS.split(&quot;,&quot;))]\nprint(len(common), &quot;series priced by all three books&quot;)\nfor slug in BOOKS.split(&quot;,&quot;):\n    print(f&quot;  {slug:9s} Brier {brier(common, slug)[0]:.4f}&quot;)<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>266 decided series with a closing Winner price\n  pinnacle  n=185  favourite won 128\/184 (69.6%)  Brier 0.1943\n  bet365    n=164  favourite won 103\/158 (65.2%)  Brier 0.2143\n  1xbet     n=255  favourite won 181\/250 (72.4%)  Brier 0.1916\n120 series priced by all three books\n  pinnacle  Brier 0.2001\n  bet365    Brier 0.2172\n  1xbet     Brier 0.2129<\/code><\/pre>\n<p><strong>266 of the 303 decided series carry a closing Winner price<\/strong> from at least one of the three books. The favourite at Pinnacle&#8217;s close won 128 of 184 (69.6%), at bet365&#8217;s close 103 of 158, and at 1xbet&#8217;s close 181 of 250.<\/p>\n<p>Do not rank the books on the first three Brier scores. Each book priced a different set of series, and 1xbet covered the most. On the 120 series that all three priced, Pinnacle scores 0.2001 against 0.2129 for 1xbet and 0.2172 for bet365. Rank books on a common set, the same rule <a href=\"https:\/\/oddspapi.io\/blog\/how-many-bookmakers-backtest\/\">the bookmaker-count study<\/a> applies to football.<\/p>\n<p>The close also lands at different times. Pinnacle&#8217;s last active price came a median 4.4 minutes before the series started, bet365&#8217;s 15.4 minutes and 1xbet&#8217;s 24.8 minutes. Median closing overround ran 6.92% on Pinnacle, 8.97% on bet365 and 6.65% on 1xbet. If you audit your closing line value against 1xbet, you compare against a price that is almost half an hour old.<\/p>\n<h2>What this gives you to build<\/h2>\n<ul>\n<li><strong>A bet tracker that settles itself.<\/strong> Poll <code>\/v4\/scores<\/code> on your open fixture IDs and grade with the function from Step 3.<\/li>\n<li><strong>A labelled esports training set.<\/strong> The same loop that powers <a href=\"https:\/\/oddspapi.io\/blog\/football-data-api-betting-predictions\/\">the football labels study<\/a> works here: price features from <code>\/v4\/historical-odds<\/code>, labels from <code>\/v4\/scores<\/code>.<\/li>\n<li><strong>A closing-line audit.<\/strong> Compare the price you took against the Step 4 close. The <a href=\"https:\/\/oddspapi.io\/blog\/backtest-betting-model-free-historical-odds\/\">backtest walkthrough<\/a> shows the rest of that pipeline.<\/li>\n<li><strong>A map-handicap checker.<\/strong> The <a href=\"https:\/\/oddspapi.io\/blog\/cs2-odds-api\/\">CS2 map handicap guide<\/a> resolves the 15 market IDs behind that ladder; this post grades them.<\/li>\n<\/ul>\n<h2>Esports results API FAQ<\/h2>\n<h3>Does OddsPapi have an esports results API?<\/h3>\n<p>Yes. <code>\/v4\/scores<\/code> returns the final series score for CS2, League of Legends and Dota 2 on the free key, and the map-by-map score on LoL and Dota 2 series. On 23 September 2026 it returned a decided score for 303 series played in the previous ten days.<\/p>\n<h3>Can I get esports map scores through the API?<\/h3>\n<p>On League of Legends and Dota 2 series longer than one map, <code>\/v4\/scores<\/code> ships <code>p1<\/code> to <code>p5<\/code>, one key per map played. All 109 such series in our sample summed exactly to the series score.<\/p>\n<h3>How do I settle an esports bet from the API?<\/h3>\n<p>Call <code>\/v4\/settlements<\/code> with the fixture ID. It returns WIN, LOSE, PUSH or UNDECIDED for every outcome on Winner, Maps Handicap, Total Maps and the map winner markets. A grader built from <code>\/v4\/scores<\/code> matched it on 2,520 of 2,520 outcomes across 45 series.<\/p>\n<h3>Which market IDs are the esports match winner?<\/h3>\n<p>Market 161 for Dota 2, 171 for Counter-Strike and 181 for League of Legends. The outcome names are &#8220;1&#8221; and &#8220;2&#8221;, so join the fixture back to <code>\/v4\/fixtures<\/code> for participant names.<\/p>\n<h3>How do I get esports closing odds?<\/h3>\n<p>Call <code>\/v4\/historical-odds<\/code> with the fixture ID and up to three bookmaker slugs, then take the last active snapshot before the start time. Across 185 CS2, LoL and Dota 2 series in September 2026, Pinnacle&#8217;s last active price landed a median 4.4 minutes before the start.<\/p>\n<h2>Grade your first series<\/h2>\n<p>Every call above runs on the free tier: 350+ bookmakers in the catalogue, full price history for backtests, and scores and settlements on the same fixture ID. The WebSocket feed pushes the live side when you outgrow polling. <a href=\"https:\/\/oddspapi.io\/en\/sign-up\">Get a free API key<\/a> and run the four snippets on tonight&#8217;s slate.<\/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\": \"Does OddsPapi have an esports results API?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes. \/v4\/scores returns the final series score for CS2, League of Legends and Dota 2 on the free key, and the map-by-map score on LoL and Dota 2 series. On 23 September 2026 it returned a decided score for 303 series played in the previous ten days.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Can I get esports map scores through the API?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"On League of Legends and Dota 2 series longer than one map, \/v4\/scores ships p1 to p5, one key per map played. All 109 such series in our sample summed exactly to the series score.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How do I settle an esports bet from the API?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Call \/v4\/settlements with the fixture ID. It returns WIN, LOSE, PUSH or UNDECIDED for every outcome on Winner, Maps Handicap, Total Maps and the map winner markets. A grader built from \/v4\/scores matched it on 2,520 of 2,520 outcomes across 45 series.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Which market IDs are the esports match winner?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Market 161 for Dota 2, 171 for Counter-Strike and 181 for League of Legends. The outcome names are \\\"1\\\" and \\\"2\\\", so join the fixture back to \/v4\/fixtures for participant names.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How do I get esports closing odds?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Call \/v4\/historical-odds with the fixture ID and up to three bookmaker slugs, then take the last active snapshot before the start time. Across 185 CS2, LoL and Dota 2 series in September 2026, Pinnacle's last active price landed a median 4.4 minutes before the start.\"\n      }\n    }\n  ]\n}\n<\/script><\/p>\n<p><!--\nFocus Keyphrase: esports results api\nSEO Title: Esports Results API: Settle 303 CS2, LoL and Dota 2 Series\nMeta Description: An esports results API that shares fixture IDs with the odds: our grader matched \/v4\/settlements on 2,520 of 2,520 outcomes across 45 series.\nSlug: esports-results-api\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>An esports results API that shares fixture IDs with the odds: our grader matched \/v4\/settlements on 2,520 of 2,520 outcomes across 45 series.<\/p>\n","protected":false},"author":2,"featured_media":4415,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[99,13,4,9,11],"class_list":["post-4414","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to-guides","tag-cs2","tag-esports","tag-historical-odds","tag-odds-api","tag-python"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Esports Results API: Settle 303 CS2, LoL and Dota 2 Series | 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\/esports-results-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series | OddsPapi Blog\" \/>\n<meta property=\"og:description\" content=\"An esports results API that shares fixture IDs with the odds: our grader matched \/v4\/settlements on 2,520 of 2,520 outcomes across 45 series.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/esports-results-api\/\" \/>\n<meta property=\"og:site_name\" content=\"OddsPapi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-05T10:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"675\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"Odds API Writer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/oddspapi.io\/logo-v2.webp\" \/>\n<meta name=\"twitter:creator\" content=\"@oddspapiapi\" \/>\n<meta name=\"twitter:site\" content=\"@oddspapiapi\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Odds API Writer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series\",\"datePublished\":\"2026-10-05T10:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/\"},\"wordCount\":1309,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp\",\"keywords\":[\"CS2\",\"Esports\",\"historical odds\",\"Odds API\",\"Python\"],\"articleSection\":[\"How To Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/\",\"name\":\"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series | OddsPapi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp\",\"datePublished\":\"2026-10-05T10:00:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/esports-results-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp\",\"width\":1200,\"height\":675,\"caption\":\"Esports Results API - OddsPapi API Blog\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/esports-results-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series\"}]},{\"@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":"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series | 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\/esports-results-api\/","og_locale":"en_US","og_type":"article","og_title":"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series | OddsPapi Blog","og_description":"An esports results API that shares fixture IDs with the odds: our grader matched \/v4\/settlements on 2,520 of 2,520 outcomes across 45 series.","og_url":"https:\/\/oddspapi.io\/blog\/esports-results-api\/","og_site_name":"OddsPapi Blog","article_published_time":"2026-10-05T10:00:00+00:00","og_image":[{"width":1200,"height":675,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp","type":"image\/webp"}],"author":"Odds API Writer","twitter_card":"summary_large_image","twitter_image":"https:\/\/oddspapi.io\/logo-v2.webp","twitter_creator":"@oddspapiapi","twitter_site":"@oddspapiapi","twitter_misc":{"Written by":"Odds API Writer","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series","datePublished":"2026-10-05T10:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/"},"wordCount":1309,"commentCount":0,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp","keywords":["CS2","Esports","historical odds","Odds API","Python"],"articleSection":["How To Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/esports-results-api\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/","url":"https:\/\/oddspapi.io\/blog\/esports-results-api\/","name":"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series | OddsPapi Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp","datePublished":"2026-10-05T10:00:00+00:00","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/esports-results-api\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/09\/esports-results-api.webp","width":1200,"height":675,"caption":"Esports Results API - OddsPapi API Blog"},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/esports-results-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"Esports Results API: Settle 303 CS2, LoL and Dota 2 Series"}]},{"@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\/4414","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=4414"}],"version-history":[{"count":1,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/4414\/revisions"}],"predecessor-version":[{"id":4416,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/4414\/revisions\/4416"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/4415"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=4414"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=4414"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=4414"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}