# SockOdds API Documentation > Australian sports betting odds data API in the SportsGameOdds v2 schema. Odds, player props, lines, totals and head to heads from every Australian bookmaker, plus results and event status. This file contains the complete API documentation. For the orientation index, see https://sockodds.com/llms.txt --- # Documentation URL: https://sockodds.com/docs/ # Documentation [#documentation] Guides, references, SDKs and examples to integrate Australian sports betting odds data into any application — in the SportsGameOdds v2 schema. ### Quickstart [#quickstart] Make your first API call in under 5 minutes. Get your key, send a request, and see real data. ### AI-Assisted Development [#ai-assisted-development] Use Claude Code, Cursor or Copilot with SockOdds. Point the SGO MCP server here or copy-paste AI context. ### Cheat Sheet [#cheat-sheet] Endpoints, authentication, oddID format and response structure. Copy-paste examples for common requests. ### Setup Guide [#setup-guide] Get your API key, configure authentication, and set up your development environment. ## Explore the docs [#explore-the-docs] ### Data Types [#data-types] Understand sports, leagues, teams, events, odds and bookmakers — and how they relate. ### Guides [#guides] In-depth walkthroughs for batching data, handling odds, polling and optimising response times. ### SDK [#sdk] The official SportsGameOdds client libraries for TypeScript, Python, Ruby, Go and Java, pointed at SockOdds. ### Examples [#examples] Full project examples: live odds trackers, arbitrage calculators, multi builders and more. ### FAQ [#faq] Common questions about pricing, rate limits, data coverage and troubleshooting. ### API Reference [#api-reference] Every endpoint with request/response schemas and a live request builder. ```typescript import SportsGameOdds from "sports-odds-api"; const client = new SportsGameOdds({ apiKeyHeader: "YOUR_KEY", baseURL: "https://api.sockodds.com/v2" }); const page = await client.events.get({ leagueID: ["AFL", "NRL"], oddsAvailable: true }); ``` ## See what you get [#see-what-you-get] Browse our data schema interactively to get a better understanding of everything you'll get back from the API. [Try the Explorer](https://sockodds.com/docs/explorer/) ### Event [#event] Basic information, odds, results, team info and lineups `eventID` `status` `odds` `results` `teams` `info` `type` `links` ## Ready to build? [#ready-to-build] [Get free API key](https://sockodds.com/signup/) [Read the Quickstart](https://sockodds.com/docs/basics/quickstart/) > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Introduction URL: https://sockodds.com/docs/basics/ # Introduction [#introduction] Welcome to SockOdds. We provide pre-match odds, event status, results and deeplinks for every Australian bookmaker and every code Australia bets on, served through this API in the SportsGameOdds v2 schema. Whether you're working on an enterprise application or a side project, we want to make it dead simple to integrate Australian odds — and if you already integrate SportsGameOdds, we want it to take one line. ## Philosophy [#philosophy] - **Extend the standard, don't fork it.** Same endpoints, envelope and oddID grammar as SportsGameOdds, so your parsers, SDKs and examples work unchanged. - **Get all the data you need with one request.** One event carries every market and every bookmaker. - **Be honest about freshness.** Suspended prices say so, stale events are flagged, and a plan that filtered a response tells you in `notice`. - **Good odds data shouldn't cost thousands a month.** Plans are priced on request rate and scope, not on how much you pull. ## Pricing model [#pricing-model] **We charge per event returned, not per market or bookmaker returned.** A user needs 10 AFL games, each with 250 markets across 18 bookmakers: on SockOdds that is 10 objects; on a per-market-per-book API it is 45,000. ## Full coverage [#full-coverage] - **34 leagues across 12 sports** — AFL, NRL, cricket, rugby, soccer, tennis, the US majors and more - **25 bookmakers**, 21 Australian — Sportsbet, Unibet, TAB, PlayUp, TABtouch, BetRight and more - **Pre-match odds** refreshed every ~2 minutes - **Main markets, player props, game props** — 1192 market patterns - **Status and results** — everything in one place - **Retained events** for backtesting and analysis - **Partial game odds** (halves, quarters, innings, overs, sets) - **Deeplinks** straight to the event and the bet slip on the bookmaker ## Battle-tested [#battle-tested] - The pipeline behind the feed runs our own betting analytics every day - Per-minute metering proven under concurrency on Postgres - A fresh-clone end-to-end test with 45+ live assertions on every release ## How it works [#how-it-works] ### 1. Sign up & get your API key [#1-sign-up-get-your-api-key] [Sign up here](https://sockodds.com/signup/) and get a free Lite key instantly — no card, no expiry. ### 2. Make your first request [#2-make-your-first-request] ```bash curl -X GET "https://api.sockodds.com/v2/events?leagueID=AFL,NRL&oddsAvailable=true&apiKey=YOUR_API_KEY_GOES_HERE" ``` Simply replace `YOUR_API_KEY_GOES_HERE` with your actual API key. ### 3. Build something amazing [#3-build-something-amazing] Use our [SDK guide](https://sockodds.com/docs/sdk/), [examples](https://sockodds.com/docs/examples/live-odds-tracker/) and [guides](https://sockodds.com/docs/guides/data-batches/) to build faster. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Setup Guide - API Keys and Authentication URL: https://sockodds.com/docs/basics/setup/ # Setup Guide - API Keys and Authentication [#setup-guide---api-keys-and-authentication] There are only 2 things you need to get started — an API key and a way to make requests. ## API key [#api-key] - **[Get an API key here](https://sockodds.com/signup/).** An API key is required to make requests. - **We offer an eternally free plan.** The Lite key covers AFL and NRL from 3 bookmakers at 10 requests a minute, no card. - **Your key is shown once.** Only a hash is stored on our side, so a lost key cannot be recovered — mint a new one. - **Keep your API key secret.** Never expose it publicly. Your API key is your password to the API. - **Include your API key in all requests.** Either the `x-api-key` header or the `apiKey` query param. ## Making requests manually [#making-requests-manually] ### Reference docs tool [#reference-docs-tool] You can make requests directly from the [API Reference](https://sockodds.com/docs/reference/): 1. Open an endpoint page (Events, Teams, Sports, …). 2. Paste your key into the **API key** field. 3. Fill in query parameters. 4. Click **Send**. The response appears below with its status, timing and rate-limit headers. > The reference pages also give you ready-to-use code snippets in multiple languages. ### Postman [#postman] 1. **Install Postman**: [postman.com/downloads](https://www.postman.com/downloads/). 2. **Download our collection**: [SockOdds Postman Collection](https://sockodds.com/SockOdds_Postman_Collection.json). 3. **Import the collection**: click Import and drop the file in. 4. **Set your API key**: select `SockOdds API` → Variables → replace `YOUR_API_KEY_HERE` → Save. 5. **Make a request**: expand Events → Get Events → set `oddsAvailable` to true → Send. ### Directly in the browser [#directly-in-the-browser] Use the `apiKey` parameter to authenticate in the address bar: ``` https://api.sockodds.com/v2/sports/?apiKey=YOUR_API_KEY ``` Add or remove query parameters to adjust what you receive. To get only head-to-head prices on the events endpoint add `&oddID=points-home-game-ml-home,points-away-game-ml-away`. > API responses can be large and may slow your browser tab. Great for quick tests, not recommended for regular use. ## Making requests in code [#making-requests-in-code] ### With HTTP libraries [#with-http-libraries] Replace `YOUR_API_KEY` with your actual API key: **Browser URL** · **cURL** · **JavaScript** · **Python** · **Ruby** · **PHP** · **Java** · **Go** ```text https://api.sockodds.com/v2/sports/?apiKey=YOUR_API_KEY ``` ```bash curl -X GET "https://api.sockodds.com/v2/sports/?" -H "x-api-key: YOUR_API_KEY" ``` ```javascript const response = await fetch("https://api.sockodds.com/v2/sports/?", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { data, nextCursor, notice } = await response.json(); console.log(JSON.stringify(data, null, 2)); ``` ```python import requests response = requests.get("https://api.sockodds.com/v2/sports/", params=dict(), headers={"x-api-key": "YOUR_API_KEY"}) output = response.json() print(output["data"]) ``` ```ruby require 'net/http' require 'json' uri = URI("https://api.sockodds.com/v2/sports/?") req = Net::HTTP::Get.new(uri) req['x-api-key'] = 'YOUR_API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.pretty_generate(JSON.parse(res.body)['data']) ``` ```php response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/sports/?", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(string(body)) ``` ### With the SDK [#with-the-sdk] The official SportsGameOdds SDKs are generated from the same v2 schema SockOdds serves, so they work here unchanged — set the base URL. First, install: **JavaScript (TypeScript)** · **Python** · **Ruby** · **Go** · **Java** ```bash npm install sports-odds-api # or yarn add / pnpm add ``` ```bash pip install sports-odds-api ``` ```bash gem install sports-odds-api ``` ```bash go get github.com/SportsGameOdds/sports-odds-api-go ``` ```groovy // Gradle implementation 'com.sportsgameodds.api:sports-odds-api:1.0.0' ``` Then make a request: **TypeScript** · **Python** · **Ruby** · **Go** · **Java** ```typescript import SportsGameOdds from "sports-odds-api"; const client = new SportsGameOdds({ apiKeyHeader: "YOUR_API_KEY", baseURL: "https://api.sockodds.com/v2" }); const res = await client.sports.get(); console.log(res.data); ``` ```python from sports_odds_api import SportsGameOdds client = SportsGameOdds(api_key_header="YOUR_API_KEY", base_url="https://api.sockodds.com/v2") res = client.sports.get() print(res.data) ``` ```ruby require "sports_odds_api" client = SportsOddsAPI::Client.new(api_key_header: "YOUR_API_KEY", base_url: "https://api.sockodds.com/v2") res = client.sports.get puts res.data ``` ```go client := sportsoddsapi.NewClient(option.WithAPIKeyHeader("YOUR_API_KEY"), option.WithBaseURL("https://api.sockodds.com/v2")) res, err := client.Sports.Get(context.Background()) if err != nil { log.Fatal(err) } fmt.Println(res.Data) ``` ```java SportsGameOddsClient client = SportsGameOddsOkHttpClient.builder() .apiKeyHeader("YOUR_API_KEY").baseUrl("https://api.sockodds.com/v2").build(); var res = client.sports().get(); System.out.println(res.items()); ``` > Learn more about the SDKSee the [SDK guide](https://sockodds.com/docs/sdk/) for pagination, error handling, filtering and advanced features. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Quickstart - First API Call in 5 Minutes URL: https://sockodds.com/docs/basics/quickstart/ # Quickstart - First API Call in 5 Minutes [#quickstart---first-api-call-in-5-minutes] A complete, copy-paste tutorial to fetch Australian odds. ## Step 1: Get your API key [#step-1-get-your-api-key] 1. Sign up at [sockodds.com/signup](https://sockodds.com/signup/) 2. Copy the key it shows you — it is shown once 3. Keep it handy for the next step ## Step 2: Make a request [#step-2-make-a-request] 1. Copy one of the examples below into a file 2. Replace `YOUR_API_KEY` with your actual API key 3. Run it! **Browser URL** · **cURL** · **JavaScript** · **Python** · **Ruby** · **PHP** · **Java** · **Go** ```text https://api.sockodds.com/v2/events?apiKey=YOUR_API_KEY&leagueID=AFL,NRL&oddsAvailable=true ``` ```bash curl -X GET "https://api.sockodds.com/v2/events?leagueID=AFL,NRL&oddsAvailable=true" -H "x-api-key: YOUR_API_KEY" ``` ```javascript const response = await fetch("https://api.sockodds.com/v2/events?leagueID=AFL,NRL&oddsAvailable=true", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { data, nextCursor, notice } = await response.json(); console.log(JSON.stringify(data, null, 2)); ``` ```python import requests response = requests.get("https://api.sockodds.com/v2/events", params=dict(leagueID="AFL,NRL", oddsAvailable="true"), headers={"x-api-key": "YOUR_API_KEY"}) output = response.json() print(output["data"]) ``` ```ruby require 'net/http' require 'json' uri = URI("https://api.sockodds.com/v2/events?leagueID=AFL,NRL&oddsAvailable=true") req = Net::HTTP::Get.new(uri) req['x-api-key'] = 'YOUR_API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.pretty_generate(JSON.parse(res.body)['data']) ``` ```php response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/events?leagueID=AFL,NRL&oddsAvailable=true", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(string(body)) ``` > If you don't want to write code, paste the "Browser URL" example into your browser. ## Step 3: See live odds [#step-3-see-live-odds] You'll get a response like this (trimmed to one market): ```json { "success": true, "nextCursor": "afl_2026-09-03_fremantle_vs_hawthorn", "data": [ { "eventID": "afl_2026-09-03_fremantle_vs_hawthorn", "sportID": "AUSSIE_RULES", "leagueID": "AFL", "type": "match", "teams": { "home": { "teamID": "FREMANTLE_AFL", "names": { "long": "Fremantle", "medium": "Fremantle", "short": "FRE" }, "statEntityID": "home" }, "away": { "teamID": "HAWTHORN_AFL", "names": { "long": "Hawthorn", "medium": "Hawthorn", "short": "HAW" }, "statEntityID": "away" } }, "status": { "started": false, "ended": false, "cancelled": false, "live": false, "finalized": false, "oddsPresent": true, "oddsAvailable": true, "startsAt": "2026-09-03T09:40:00Z", "displayLong": "Upcoming" }, "info": { "displayName": "Hawthorn @ Fremantle", "commenceTime": "2026-09-03T09:40:00Z", "lastUpdatedAt": "2026-09-03T08:27:48.000Z", "stale": false }, "odds": { "points-all-game-eo-odd": { "oddID": "points-all-game-eo-odd", "opposingOddID": "points-all-game-eo-even", "marketName": "Odd/Even Total Points", "statID": "points", "statEntityID": "all", "periodID": "game", "betTypeID": "eo", "sideID": "odd", "bookOdds": "-116", "fairOdds": null, "bookOverUnder": null, "bookSpread": null, "byBookmaker": { "unibet": { "bookmakerID": "unibet", "odds": "-115", "decimal": 1.87, "available": true, "lastUpdatedAt": "2026-09-03T10:22:09.000Z" }, "tabtouch": { "bookmakerID": "tabtouch", "odds": "-118", "decimal": 1.85, "available": true, "lastUpdatedAt": "2026-09-03T10:21:03.000Z" } } } }, "links": { "bookmakers": { "sportsbet": "https://www.sportsbet.com.au/betting/australian-rules/afl/…", "tab": "https://www.tab.com.au/sports/betting/Australian%20Rules/competitions/AFL/matches/…" } }, "players": {} } ] } ``` ### Understand the response [#understand-the-response] **Event details:** - `eventID` — unique identifier for this game - `teams.home.names.long` — home team - `status.startsAt` — when the game starts (UTC) - `info.stale` — whether the source has stopped writing this event **Odds data:** each odd is keyed by `oddID`, which follows this pattern: ``` {statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID} ``` Note: the oddID does NOT include bookmakerID. Each oddID contains odds from ALL available bookmakers nested under `byBookmaker`. **Example:** `points-home-game-ml-home` means: - `points` — stat being bet on (the score) - `home` — entity (home team) - `game` — the full game - `ml` — head to head / moneyline (betTypeID) - `home` — side of the bet **The odds show:** - Each oddID contains odds from multiple bookmakers under `byBookmaker` - Every book carries `odds` (American string) and `decimal` - All odds values are **strings**, not numbers — `decimal` is a number ## Done! [#done] **Congratulations!** You just fetched Australian odds from the SockOdds API. ## Next steps [#next-steps] ### Build something real [#build-something-real] - **[Live Odds Tracker](https://sockodds.com/docs/examples/live-odds-tracker/)** — track line movement - **[Arbitrage Calculator](https://sockodds.com/docs/examples/arbitrage-calculator/)** — find cross-book margins - **[Odds Comparison Dashboard](https://sockodds.com/docs/examples/odds-comparison-dashboard/)** - **[Player Props Analyzer](https://sockodds.com/docs/examples/player-props-analyzer/)** - **[Parlay Builder](https://sockodds.com/docs/examples/parlay-builder/)** ### Learn the basics [#learn-the-basics] - **[Understanding odds data](https://sockodds.com/docs/data-types/odds/)** - **[Glossary](https://sockodds.com/docs/info/glossary/)** - **[Setup](https://sockodds.com/docs/basics/setup/)** - **[Rate limiting](https://sockodds.com/docs/info/rate-limiting/)** ### Use the SDK [#use-the-sdk] ```bash npm install sports-odds-api pip install sports-odds-api gem install sports-odds-api go get github.com/SportsGameOdds/sports-odds-api-go ``` [Full SDK guide →](https://sockodds.com/docs/sdk/) ### Explore all endpoints [#explore-all-endpoints] - **[API Reference](https://sockodds.com/docs/reference/)** - **[Data Explorer](https://sockodds.com/docs/explorer/)** ## Common next questions [#common-next-questions] ### How do I get odds from multiple bookmakers? [#how-do-i-get-odds-from-multiple-bookmakers] By default every bookmaker your plan allows is returned under `byBookmaker`. Filter with `bookmakerID=sportsbet,tab`, or in code: `Object.entries(odd.byBookmaker)`. [All bookmakers →](https://sockodds.com/docs/data-types/bookmakers/) ### How do I get event status? [#how-do-i-get-event-status] It is on every event: `status.started`, `status.live`, `status.ended`, `status.finalized`; filter with `live=true`. ### How do I get historical data? [#how-do-i-get-historical-data] Use `startsAfter` and `startsBefore` with `finalized=true`. Finished events are retained from 2026-09. ### How often should I poll? [#how-often-should-i-poll] Every 2–5 minutes. The source refreshes every ~2 minutes, so faster polling wastes quota. [Best practices →](https://sockodds.com/docs/info/best-practices/) ### What counts as an "object"? [#what-counts-as-an-object] One event (game) with ALL its markets and ALL its bookmakers. 10 events = 10 objects; the same 10 polled twice = 20. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # API Cheat Sheet - Quick Reference URL: https://sockodds.com/docs/basics/cheat-sheet/ # API Cheat Sheet - Quick Reference [#api-cheat-sheet---quick-reference] > TLDRGet an API key [here](https://sockodds.com/signup/). It is shown once.Place it in the `apiKey` query param or the `x-api-key` header.A full list of endpoints and parameters is in the [reference](https://sockodds.com/docs/reference/).Responses are JSON. The `data` field contains what you queried for.Use the [Data Explorer](https://sockodds.com/docs/explorer/) to see the schema.The most-used endpoint is `/events`. Common params: `oddsAvailable=true`, `leagueID=AFL,NRL`, `oddID=points-home-game-ml-home`, `includeAltLines=true`. ## Endpoints [#endpoints] The API is on version 2, so each endpoint is prefixed with `https://api.sockodds.com/v2`. For example the `/leagues` endpoint is at `https://api.sockodds.com/v2/leagues`. The main endpoint for odds and status is [/events](https://sockodds.com/docs/endpoints/getEvents/). | I want… | Call | | --- | --- | | upcoming AFL events with all odds | `/v2/events/?leagueID=AFL` | | one event | `/v2/events/?eventID=afl_2026-09-03_fremantle_vs_hawthorn` | | just the head to head on every NRL event | `/v2/events/?leagueID=NRL&oddID=points-home-game-ml-home&includeOpposingOdds=true` | | only two bookmakers | `/v2/events/?leagueID=AFL&bookmakerID=sportsbet,tab` | | events that finished already | `/v2/events/?leagueID=MLB&finalized=true` | | the next page | `/v2/events/?leagueID=EPL&cursor={nextCursor}` | | which leagues exist right now | `/v2/leagues/` | | which markets exist for a league | `/v2/markets/?leagueID=AFL` | | which stats a sport prices | `/v2/stats/?sportID=CRICKET` | | players on an event | `/v2/players/?eventID=…` | | my limits and usage | `/v2/account/usage` | ## Authentication [#authentication] All requests require an [API key](https://sockodds.com/signup/). Header: ```javascript fetch("https://api.sockodds.com/v2/events", { headers: { "x-api-key": "your-api-key-here" } }); ``` Or query param: ```javascript fetch("https://api.sockodds.com/v2/events?apiKey=YOUR_API_KEY_GOES_HERE"); ``` ## Response format [#response-format] ```json { "success": true, "data": [...], "nextCursor": "...", "notice": "..." } ``` `success` tells you whether the request succeeded. `data` is the list of objects you queried for. `nextCursor` is the cursor for the next page where paging applies. `notice` is present only when your plan filtered the response. Errors carry `success: false` and `error`. ## Example request [#example-request] ``` https://api.sockodds.com/v2/events?leagueID=AFL,NRL&oddsAvailable=true&limit=1&apiKey=YOUR_API_KEY ``` - `leagueID=AFL,NRL` — only AFL and NRL events - `oddsAvailable=true` — only events with open markets - `limit=1` — one event; results are ordered by eventID - `apiKey=…` — authenticates the request ## Data schema [#data-schema] The best way to get a sense of the data is the [Data Explorer](https://sockodds.com/docs/explorer/). In general it is organised as: 1. Sports 2. Leagues — each sport has one or more 3. Teams — each league has one or more 4. Events — each team has one or more The core unit is the Event. It contains all odds markets for that event at `Event.odds.`, and bookmaker-specific data at `Event.odds..byBookmaker.`. Each oddID combines: - `statID` — the statistic (points, disposals, tries…) - `statEntityID` — whose performance (home, away, all, a playerID) - `periodID` — the period (game, 1h, 1q…) - `betTypeID` — the type of bet (sp, ml, ou…) - `sideID` — the side (home, away, over, under…) > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Pagination Guide - Cursor-Based Batching URL: https://sockodds.com/docs/guides/data-batches/ # Pagination Guide - Cursor-Based Batching [#pagination-guide---cursor-based-batching] This applies to `/events/`, `/teams/`, `/players/` and `/markets/`. Other endpoints always return every result that matches your query. ## How it works [#how-it-works] 1. Make a request. Each request has a limit on the number of items returned. 2. If there are more items, you get a `nextCursor` in the response. 3. Repeat the query with that value in the `cursor` parameter. 4. Repeat until `nextCursor` is null. ## The cursor parameter [#the-cursor-parameter] - Always use the value from the last response's `nextCursor`. Don't reverse-engineer it. - Don't change any other query parameters between cursor requests. - Percent-encode the cursor — some ids contain non-ASCII characters. ## The limit parameter [#the-limit-parameter] - `/events`: default 10, max 100. - `/teams` and `/players`: default 50, max 250. - `/markets`: default 100, max 10,000. ## Example [#example] Grab every unfinalized NRL event: **JavaScript** · **Python** · **Ruby** · **PHP** · **Java** ```javascript const allEvents = []; let cursor = null; do { const url = new URL("https://api.sockodds.com/v2/events"); url.search = new URLSearchParams({ leagueID: "NRL", finalized: "false", limit: "100", ...(cursor ? { cursor } : {}) }); const r = await fetch(url, { headers: { "x-api-key": KEY } }); const body = await r.json(); allEvents.push(...body.data); cursor = body.nextCursor; } while (cursor); console.log(`Found ${allEvents.length} events`); ``` ```python import requests all_events, cursor = [], None while True: r = requests.get("https://api.sockodds.com/v2/events", params={"leagueID": "NRL", "finalized": "false", "limit": 100, **({"cursor": cursor} if cursor else {})}, headers={"x-api-key": KEY}) r.raise_for_status(); body = r.json() all_events.extend(body["data"]); cursor = body.get("nextCursor") if not cursor: break print(f"Found {len(all_events)} events") ``` ```ruby require "net/http"; require "json" all = []; cursor = nil loop do uri = URI("https://api.sockodds.com/v2/events"); uri.query = URI.encode_www_form({ leagueID: "NRL", finalized: false, limit: 100, cursor: cursor }.compact) req = Net::HTTP::Get.new(uri); req["x-api-key"] = KEY body = JSON.parse(Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }.body) all.concat(body["data"]); cursor = body["nextCursor"]; break unless cursor end ``` ```php $all = []; $cursor = null; do { $params = ['leagueID' => 'NRL', 'finalized' => 'false', 'limit' => 100]; if ($cursor) $params['cursor'] = $cursor; $ch = curl_init("https://api.sockodds.com/v2/events?" . http_build_query($params)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: $KEY"]); $body = json_decode(curl_exec($ch), true); curl_close($ch); $all = array_merge($all, $body['data']); $cursor = $body['nextCursor'] ?? null; } while ($cursor); ``` ```java List all = new ArrayList<>(); String cursor = null; do { String url = "https://api.sockodds.com/v2/events?leagueID=NRL&finalized=false&limit=100" + (cursor != null ? "&cursor=" + URLEncoder.encode(cursor, "UTF-8") : ""); HttpRequest req = HttpRequest.newBuilder().uri(URI.create(url)).header("x-api-key", KEY).GET().build(); JsonObject body = JsonParser.parseString(client.send(req, HttpResponse.BodyHandlers.ofString()).body()).getAsJsonObject(); body.getAsJsonArray("data").forEach(e -> all.add(e.getAsJsonObject())); cursor = body.has("nextCursor") && !body.get("nextCursor").isJsonNull() ? body.get("nextCursor").getAsString() : null; } while (cursor != null); ``` > SockOdds noteWithout a date window, `eventID`, `finalized` or `includeFinished=true`, events that kicked off more than 24 hours ago are omitted from listings — the source never prunes finished events, so this keeps the default page current. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Handling Odds - Parse Results and Grade Bets URL: https://sockodds.com/docs/guides/handling-odds/ # Handling Odds - Parse Results and Grade Bets [#handling-odds---parse-results-and-grade-bets] ## Overview [#overview] The SockOdds API carries odds and, where the source has them, results for every event. This guide shows how to fetch and parse the odds for a group of events and grade them. ## The three prices [#the-three-prices] | Field | Meaning | | --- | --- | | `bookOdds` | Consensus book price as an American string ("-108", "+240"), present whenever any book prices the market | | `fairOdds` | The de-vigged fair price from exchange/sharp consensus; `null` when it can't be computed and on Lite keys — `fairOddsAvailable` says which | | `byBookmaker[book]` | That book's own price: `odds` (American), `decimal`, `available`, `lastUpdatedAt`, and `overUnder`/`spread` where relevant | ### American to decimal [#american-to-decimal] Every bookmaker entry already carries `decimal`; for the consensus fields: ```python def decimal(american: str) -> float: a = int(american) return 1 + (a / 100 if a > 0 else 100 / -a) ``` ### Two-sided markets [#two-sided-markets] `opposingOddID` names the other side (`…-over` ↔ `…-under`, `…-home` ↔ `…-away`). Alt lines live under `altLines` keyed by line, each with its own `byBookmaker`, when you ask with `includeAltLines=true`. ## Example: grading last week's NRL totals [#example-grading-last-weeks-nrl-totals] ```javascript let cursor = null, events = []; do { const url = new URL("https://api.sockodds.com/v2/events"); url.search = new URLSearchParams({ leagueID: "NRL", startsAfter: "2026-08-31", startsBefore: "2026-09-07", finalized: "true", ...(cursor ? { cursor } : {}) }); const body = await (await fetch(url, { headers: { "x-api-key": KEY } })).json(); events = events.concat(body.data); cursor = body.nextCursor; } while (cursor); for (const event of events) { for (const odd of Object.values(event.odds)) { if (odd.betTypeID !== "ou" || !odd.scoringSupported) continue; // the stat that settles this market lives at results.{periodID}.{statEntityID}.{statID} const actual = event.results?.[odd.periodID]?.[odd.statEntityID]?.[odd.statID]; const line = parseFloat(odd.bookOverUnder); if (actual == null || Number.isNaN(line)) continue; console.log(odd.oddID, actual > line ? "Over wins" : actual === line ? "Push" : "Under wins"); } } ``` > **Two flags you must read.** A book with `available: false` is listed but suspended. An event with `info.stale: true` has had no source write for 45 minutes — the prices are the last known, not current. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Teams Guide - Fetch by ID, League, or Sport URL: https://sockodds.com/docs/guides/fetching-teams/ # Teams Guide - Fetch by ID, League, or Sport [#teams-guide---fetch-by-id-league-or-sport] ## Overview [#overview] The `/teams` endpoint returns the teams on current events. Use `sportID` or `leagueID` for a list, or `teamID` for a single team (still returned as an array with one object). `/players` works the same way with `eventID`, `teamID` or `playerID`, and returns the production SGO `playerID`s (`SONNY_GRAY_1_MLB`). ## Fetching by team [#fetching-by-team] **Browser URL** · **cURL** · **JavaScript** · **Python** · **Ruby** · **PHP** · **Java** · **Go** ```text https://api.sockodds.com/v2/teams?apiKey=YOUR_API_KEY&teamID=BRISBANE_LIONS_AFL ``` ```bash curl -X GET "https://api.sockodds.com/v2/teams?teamID=BRISBANE_LIONS_AFL" -H "x-api-key: YOUR_API_KEY" ``` ```javascript const response = await fetch("https://api.sockodds.com/v2/teams?teamID=BRISBANE_LIONS_AFL", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { data, nextCursor, notice } = await response.json(); console.log(JSON.stringify(data, null, 2)); ``` ```python import requests response = requests.get("https://api.sockodds.com/v2/teams", params=dict(teamID="BRISBANE_LIONS_AFL"), headers={"x-api-key": "YOUR_API_KEY"}) output = response.json() print(output["data"]) ``` ```ruby require 'net/http' require 'json' uri = URI("https://api.sockodds.com/v2/teams?teamID=BRISBANE_LIONS_AFL") req = Net::HTTP::Get.new(uri) req['x-api-key'] = 'YOUR_API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.pretty_generate(JSON.parse(res.body)['data']) ``` ```php response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/teams?teamID=BRISBANE_LIONS_AFL", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(string(body)) ``` Returns something like: ```json { "success": true, "data": [ { "sportID": "AUSSIE_RULES", "leagueID": "AFL", "teamID": "BRISBANE_LIONS_AFL", "names": { "long": "Brisbane Lions", "medium": "Brisbane Lions", "short": "BRI" }, "colors": { "primary": "", "secondary": "", "primaryContrast": "", "secondaryContrast": "" }, "statEntityID": "home" } ] } ``` ## Fetching by league [#fetching-by-league] **Browser URL** · **cURL** · **JavaScript** · **Python** · **Ruby** · **PHP** · **Java** · **Go** ```text https://api.sockodds.com/v2/teams?apiKey=YOUR_API_KEY&leagueID=AFL ``` ```bash curl -X GET "https://api.sockodds.com/v2/teams?leagueID=AFL" -H "x-api-key: YOUR_API_KEY" ``` ```javascript const response = await fetch("https://api.sockodds.com/v2/teams?leagueID=AFL", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { data, nextCursor, notice } = await response.json(); console.log(JSON.stringify(data, null, 2)); ``` ```python import requests response = requests.get("https://api.sockodds.com/v2/teams", params=dict(leagueID="AFL"), headers={"x-api-key": "YOUR_API_KEY"}) output = response.json() print(output["data"]) ``` ```ruby require 'net/http' require 'json' uri = URI("https://api.sockodds.com/v2/teams?leagueID=AFL") req = Net::HTTP::Get.new(uri) req['x-api-key'] = 'YOUR_API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.pretty_generate(JSON.parse(res.body)['data']) ``` ```php response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/teams?leagueID=AFL", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(string(body)) ``` Up to 50 teams per page by default (max 250); a `nextCursor` is returned when there are more. ## Fetching by sport [#fetching-by-sport] **Browser URL** · **cURL** · **JavaScript** · **Python** · **Ruby** · **PHP** · **Java** · **Go** ```text https://api.sockodds.com/v2/teams?apiKey=YOUR_API_KEY&sportID=RUGBY_LEAGUE ``` ```bash curl -X GET "https://api.sockodds.com/v2/teams?sportID=RUGBY_LEAGUE" -H "x-api-key: YOUR_API_KEY" ``` ```javascript const response = await fetch("https://api.sockodds.com/v2/teams?sportID=RUGBY_LEAGUE", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { data, nextCursor, notice } = await response.json(); console.log(JSON.stringify(data, null, 2)); ``` ```python import requests response = requests.get("https://api.sockodds.com/v2/teams", params=dict(sportID="RUGBY_LEAGUE"), headers={"x-api-key": "YOUR_API_KEY"}) output = response.json() print(output["data"]) ``` ```ruby require 'net/http' require 'json' uri = URI("https://api.sockodds.com/v2/teams?sportID=RUGBY_LEAGUE") req = Net::HTTP::Get.new(uri) req['x-api-key'] = 'YOUR_API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.pretty_generate(JSON.parse(res.body)['data']) ``` ```php response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/teams?sportID=RUGBY_LEAGUE", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(string(body)) ``` ## Players [#players] **Browser URL** · **cURL** · **JavaScript** · **Python** · **Ruby** · **PHP** · **Java** · **Go** ```text https://api.sockodds.com/v2/players?apiKey=YOUR_API_KEY&eventID=afl_2026-09-03_fremantle_vs_hawthorn ``` ```bash curl -X GET "https://api.sockodds.com/v2/players?eventID=afl_2026-09-03_fremantle_vs_hawthorn" -H "x-api-key: YOUR_API_KEY" ``` ```javascript const response = await fetch("https://api.sockodds.com/v2/players?eventID=afl_2026-09-03_fremantle_vs_hawthorn", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { data, nextCursor, notice } = await response.json(); console.log(JSON.stringify(data, null, 2)); ``` ```python import requests response = requests.get("https://api.sockodds.com/v2/players", params=dict(eventID="afl_2026-09-03_fremantle_vs_hawthorn"), headers={"x-api-key": "YOUR_API_KEY"}) output = response.json() print(output["data"]) ``` ```ruby require 'net/http' require 'json' uri = URI("https://api.sockodds.com/v2/players?eventID=afl_2026-09-03_fremantle_vs_hawthorn") req = Net::HTTP::Get.new(uri) req['x-api-key'] = 'YOUR_API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.pretty_generate(JSON.parse(res.body)['data']) ``` ```php response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/players?eventID=afl_2026-09-03_fremantle_vs_hawthorn", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(string(body)) ``` Player-prop markets put the `playerID` in the `statEntityID` slot of the oddID, so a player's props on an event are the `odds` keys containing their id. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Optimize Response Speed and Latency URL: https://sockodds.com/docs/guides/response-speed/ # Optimize Response Speed and Latency [#optimize-response-speed-and-latency] We balance making as much data available as possible with letting you fetch it quickly. This guide covers the parameters that keep responses small and fast. ## Use the oddID parameter [#use-the-oddid-parameter] - The most common cause of slow responses is fetching every market on many events at once. - Use `oddID` on `/events` to fetch only the markets you need — a comma-separated list of oddIDs (see the [markets browser](https://sockodds.com/docs/data-types/markets/)). - Set `includeOpposingOdds=true` to also include the other side of each oddID you list. - Replace the playerID portion of any oddID with `PLAYER_ID` to fetch that market across all players. Example: `disposals-LACHIE_NEALE_1_AFL-game-ou-under` is one player's disposals under. To fetch every player's disposals, both sides: ``` oddID=disposals-PLAYER_ID-game-ou-under&includeOpposingOdds=true ``` ## Use bookmakerID [#use-bookmakerid] `bookmakerID=sportsbet,tab` trims every `byBookmaker` block to the books you show. ## One league at a time [#one-league-at-a-time] A full-league page is a few hundred KB; a multi-league page is several. Page at 100 and cache by `info.lastUpdatedAt` — nothing changes between source refreshes (~2 min). ## Leave alt lines off [#leave-alt-lines-off] `includeAltLines=true` can multiply the payload; ask only when you need them. ## Don't poll faster than the source [#dont-poll-faster-than-the-source] A 429 is still a counted request. Poll every 2–5 minutes. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Real-Time Data (Polling) - Streaming API URL: https://sockodds.com/docs/guides/realtime-streaming-api/ # Real-Time Data (Polling) - Streaming API [#real-time-data-polling---streaming-api] > No WebSocketSockOdds does not stream. The source refreshes every ~2 minutes, and `/v2/stream/events` answers `501 Not Implemented` so a client written for SportsGameOdds fails loudly instead of waiting on a socket that never opens. This page shows the polling-and-diff pattern that replaces it, in the same shape the SGO streaming examples use, so the rest of your code stays the same. ## How it works [#how-it-works] 1. **Seed**: fetch the events you care about into a map keyed by `eventID`. 2. **Poll**: every 2–5 minutes fetch them again (one league at a time, or by `eventIDs`). 3. **Diff**: compare `byBookmaker[*].decimal`, `available` and `status` with what you have; act on changes. ## Available "feeds" [#available-feeds] | Feed | Equivalent query | Required parameters | | --- | --- | --- | | `events:live` | `/events?live=true` | None | | `events:upcoming` | `/events?leagueID=…&oddsAvailable=true` | `leagueID` | | `events:byid` | `/events?eventIDs=…` | `eventID` | ## Quick start example [#quick-start-example] **JavaScript/Node.js** · **Python** · **Ruby** ```javascript const API_BASE_URL = "https://api.sockodds.com/v2"; const API_KEY = process.env.SOCKODDS_KEY; const EVENTS = new Map(); async function fetchEvents(params) { const url = new URL(API_BASE_URL + "/events"); url.search = new URLSearchParams(params); return (await (await fetch(url, { headers: { "x-api-key": API_KEY } })).json()).data; } function diff(prev, next) { const changes = []; for (const [oddID, odd] of Object.entries(next.odds)) for (const [book, q] of Object.entries(odd.byBookmaker)) { const was = prev?.odds?.[oddID]?.byBookmaker?.[book]; if (!was || was.decimal !== q.decimal || was.available !== q.available) changes.push({ oddID, book, from: was?.decimal, to: q.decimal, available: q.available }); } return changes; } async function tick() { for (const e of await fetchEvents({ leagueID: "AFL", oddsAvailable: "true", limit: "100" })) { const changes = diff(EVENTS.get(e.eventID), e); if (changes.length) console.log(e.eventID, changes); EVENTS.set(e.eventID, e); } } await tick(); setInterval(tick, 150_000); // 2.5 minutes ``` ```python import os, time, requests API = "https://api.sockodds.com/v2"; KEY = os.environ["SOCKODDS_KEY"]; EVENTS = {} def fetch_events(**params): return requests.get(f"{API}/events", params=params, headers={"x-api-key": KEY}).json()["data"] def diff(prev, nxt): out = [] for odd_id, odd in nxt["odds"].items(): for book, q in odd["byBookmaker"].items(): was = (prev or {}).get("odds", {}).get(odd_id, {}).get("byBookmaker", {}).get(book) if not was or was["decimal"] != q["decimal"] or was["available"] != q["available"]: out.append((odd_id, book, was and was["decimal"], q["decimal"], q["available"])) return out while True: for e in fetch_events(leagueID="AFL", oddsAvailable="true", limit=100): changes = diff(EVENTS.get(e["eventID"]), e) if changes: print(e["eventID"], changes) EVENTS[e["eventID"]] = e time.sleep(150) ``` ```ruby require "net/http"; require "json" API = "https://api.sockodds.com/v2"; KEY = ENV["SOCKODDS_KEY"]; events = {} def fetch_events(params) uri = URI("#{API}/events"); uri.query = URI.encode_www_form(params) req = Net::HTTP::Get.new(uri); req["x-api-key"] = KEY JSON.parse(Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }.body)["data"] end loop do fetch_events(leagueID: "AFL", oddsAvailable: true, limit: 100).each do |e| prev = events[e["eventID"]] e["odds"].each do |odd_id, odd| odd["byBookmaker"].each do |book, q| was = prev&.dig("odds", odd_id, "byBookmaker", book) puts "#{e["eventID"]} #{odd_id} #{book} #{was&.dig("decimal")} -> #{q["decimal"]}" if was.nil? || was["decimal"] != q["decimal"] end end events[e["eventID"]] = e end sleep 150 end ``` ## Update detection [#update-detection] Because every bookmaker entry carries `lastUpdatedAt`, you can also skip the diff and simply act on entries whose timestamp is newer than your last poll. ## Connection management [#connection-management] There is nothing to keep open. Handle `429` by honouring `Retry-After`, and `5xx` with one retry after a short delay. ## Troubleshooting [#troubleshooting] - **No changes detected** — the source refreshes every ~2 minutes; polling faster shows nothing new. - **An event vanished from the listing** — it kicked off more than 24 hours ago; add `includeFinished=true` or query by `eventIDs`. - **Prices look frozen** — check `info.stale`. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Data Types Overview - API Schema Reference URL: https://sockodds.com/docs/data-types/ # Data Types Overview - API Schema Reference [#data-types-overview---api-schema-reference] The API organises data into a hierarchy. Understanding these relationships helps you navigate the API efficiently. Odds markets are identified using an **oddID** made up of **`{statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID}`**. | Data Type | Identifier | Description | Example Values | | --- | --- | --- | --- | | [Sports](https://sockodds.com/docs/data-types/sports/) | `sportID` | Top-level sports | `AUSSIE_RULES`, `RUGBY_LEAGUE`, `SOCCER` | | [Leagues](https://sockodds.com/docs/data-types/leagues/) | `leagueID` | Leagues within each sport | `AFL`, `NRL`, `EPL` | | [Bookmakers](https://sockodds.com/docs/data-types/bookmakers/) | `bookmakerID` | Sportsbooks that offer odds | `sportsbet`, `tab`, `pinnacle` | | [Periods](https://sockodds.com/docs/data-types/periods/) | `periodID` | Time period for a stat or bet | `game`, `1h`, `1q` | | [Stats](https://sockodds.com/docs/data-types/stats/) | `statID` | Statistic being tracked | `points`, `disposals`, `tries` | | [Stat Entity](https://sockodds.com/docs/data-types/stat-entity/) | `statEntityID` | Who the stat applies to | `home`, `away`, `all`, `LACHIE_NEALE_1_AFL` | | [Bet Types](https://sockodds.com/docs/data-types/bet-types/) | `betTypeID` | Type of bet | `ml`, `sp`, `ou` | | [Sides](https://sockodds.com/docs/data-types/bet-types/) | `sideID` | Outcome a bet is taking | `home`, `away`, `over`, `under` | | [Odds](https://sockodds.com/docs/data-types/odds/) | `oddID` | A betting odds market | `points-all-game-ou-over` | > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Supported Markets - Odds Coverage by League URL: https://sockodds.com/docs/data-types/markets/ # Supported Markets - Odds Coverage by League [#supported-markets---odds-coverage-by-league] Below is every odds market the feed makes available. Each row is a unique oddID pattern you can pass to `oddID` on the events endpoint (`PLAYER_ID` stands for any player). Additional markets can be made available on the [Platform plan](https://sockodds.com/contact-us/?plan=platform). For reference an oddID has the format `{statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID}`. > This table is a snapshot of the live store from 2026-09-07. For the most up-to-date data query the [GET /markets](https://sockodds.com/docs/endpoints/getMarkets/) endpoint, which returns every market keyed by oddID with per-league and per-bookmaker support. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Bookmaker Data Type - bookmakerID URL: https://sockodds.com/docs/data-types/bookmakers/ # Bookmaker Data Type - bookmakerID [#bookmaker-data-type---bookmakerid] Each `bookmakerID` corresponds to a sportsbook, exchange, prediction market or pick'em platform. It is the key inside `byBookmaker` and `links.bookmakers`. > Some books appear under lane suffixes (`dabble__pickem`, `betfocus__sgm_only`) when a separate product of theirs is priced separately. The canonical id is the part before `__`. | Name | bookmakerID | Type | Region | Leagues | | --- | --- | --- | --- | --- | | [bet365](https://sockodds.com/bookmakers/bet365-odds-api/) | `bet365` | Sportsbook | AU | RUGBYLEAGUE_STATE_ORIGIN | | [BetDeluxe](https://sockodds.com/bookmakers/betdeluxe-odds-api/) | `betdeluxe` | Sportsbook | AU | AFL, AFLW, ATP, BUNDESLIGA, EPL, FIFA_WORLD_CUP, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS, NBA, NFL, NRL, RUGBYLEAGUE_NRLW, RUGBYUNION_INTERNATIONALS, UCL, WTA | | [Betfair Exchange](https://sockodds.com/bookmakers/betfairexchange-odds-api/) | `betfairexchange` | Betting Exchange | AU | AFL, AFLW, ATP, BUNDESLIGA, EPL, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, MLS, NFL, RUGBYLEAGUE_NRLW, RUGBYUNION_INTERNATIONALS, UCL, WTA | | [BetFocus](https://sockodds.com/bookmakers/betfocus-odds-api/) | `betfocus` | Sportsbook | AU | AFL, ATP, EPL, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS, NRL, RUGBYUNION_INTERNATIONALS, WTA | | [BetFocus](https://sockodds.com/bookmakers/betfocus-odds-api/) same-game multi | `betfocus__sgm_only` | Sportsbook | AU | AFL, EPL, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS, RUGBYUNION_INTERNATIONALS | | [betr](https://sockodds.com/bookmakers/betr-odds-api/) | `betr` | Sportsbook | AU | NFL, UCL | | [BetRight](https://sockodds.com/bookmakers/betright-odds-api/) | `betright` | Sportsbook | AU | AFL, AFLW, ATP, BR_SERIE_A, BUNDESLIGA, EPL, F1, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, MLS, NFL, NHL, NRL, RUGBYUNION_INTERNATIONALS, SCOTTISH_PREM, UCL, UECL, UEL, UFC, WTA | | [betr (sportsbook)](https://sockodds.com/bookmakers/betrsportsbook-odds-api/) | `betrsportsbook` | Sportsbook | AU | AFL, AFLW, ATP, BR_SERIE_A, BUNDESLIGA, EPL, F1, IPL, MLB, MLS, NHL, NRL, RUGBYUNION_INTERNATIONALS, SCOTTISH_PREM, UCL, UECL, UFC, WTA | | [CrownBet](https://sockodds.com/bookmakers/crownbet-odds-api/) | `crownbet` | Sportsbook | AU | ATP, RUGBYUNION_INTERNATIONALS, WTA | | [Dabble](https://sockodds.com/bookmakers/dabble-odds-api/) | `dabble` | Pick'em | AU | AFL, AFLW, ATP, BUNDESLIGA, EPL, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, NFL, RUGBYUNION_INTERNATIONALS, WTA | | [Dabble](https://sockodds.com/bookmakers/dabble-odds-api/) pick'em | `dabble__pickem` | Pick'em | AU | AFLW, BUNDESLIGA, EPL, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB | | [Dabble](https://sockodds.com/bookmakers/dabble-odds-api/) same-game multi | `dabble__sgm_only` | Pick'em | AU | AFLW, BUNDESLIGA, EPL, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, RUGBYUNION_INTERNATIONALS | | [Epic Odds](https://sockodds.com/bookmakers/epicodds-odds-api/) | `epicodds` | Sportsbook | AU | FIFA_WORLD_CUP | | [Kalshi](https://sockodds.com/bookmakers/kalshi-odds-api/) | `kalshi` | Prediction Market | Global | ATP, BUNDESLIGA, EPL, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS, NFL, WTA | | [Ladbrokes](https://sockodds.com/bookmakers/ladbrokes-odds-api/) | `ladbrokes` | Sportsbook | AU | ATP, BR_SERIE_A, EPL, F1, IPL, MLS, NFL, NHL, UCL, UECL, UEL, UFC, WTA | | [MintBet](https://sockodds.com/bookmakers/mintbet-odds-api/) | `mintbet` | Sportsbook | AU | AFL, ATP, EPL, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS, NRL, RUGBYUNION_INTERNATIONALS, WTA | | [MintBet](https://sockodds.com/bookmakers/mintbet-odds-api/) same-game multi | `mintbet__sgm_only` | Sportsbook | AU | EPL, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS | | [Neds](https://sockodds.com/bookmakers/neds-odds-api/) | `neds` | Sportsbook | AU | AFL, AFLW, ATP, BR_SERIE_A, BUNDESLIGA, EPL, F1, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, MLS, NFL, NHL, NRL, RUGBYUNION_INTERNATIONALS, UCL, UECL, UEL, UFC, WTA | | [Picklebet](https://sockodds.com/bookmakers/picklebet-odds-api/) | `picklebet` | Sportsbook | AU | AFL, AFLW, ATP, BUNDESLIGA, F1, FR_LIGUE_1, LA_LIGA, MLB, MLS, NFL, NHL, RUGBYUNION_INTERNATIONALS, UCL, UECL, UFC, WTA | | [Picklebet](https://sockodds.com/bookmakers/picklebet-odds-api/) same-game multi | `picklebet__sgm_only` | Sportsbook | AU | AFLW, BUNDESLIGA, FR_LIGUE_1, LA_LIGA, MLB, MLS, NHL, UCL | | [Pinnacle](https://sockodds.com/bookmakers/pinnacle-odds-api/) | `pinnacle` | Sportsbook | Global | AFL, ATP, BR_SERIE_A, BUNDESLIGA, EPL, F1, FIFA_WORLD_CUP, FR_LIGUE_1, ICELAND_1_DEILD, ICELAND_BESTA_DEILD, IT_SERIE_A, LA_LIGA, MLB, MLS, NFL, NHL, NRL, RUGBYUNION_INTERNATIONALS, SLOVAK_2_LIGA, SLOVAK_SUPER_LIGA, UCL, UECL, UEFA_CHAMPIONS_LEAGUE_QUALIFIERS, UEFA_CONFERENCE_LEAGUE_QUALIFIERS, UEFA_EUROPA_LEAGUE_QUALIFIERS, UEL, UFC, WTA | | [PlayUp](https://sockodds.com/bookmakers/playup-odds-api/) | `playup` | Sportsbook | AU | AFL, AFLW, ATP, BR_SERIE_A, BUNDESLIGA, EPL, FIFA_WORLD_CUP, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, MLS, NBA, NFL, NHL, NRL, UCL, UEL, UFC, WTA | | [PointsBet](https://sockodds.com/bookmakers/pointsbet-odds-api/) | `pointsbet` | Sportsbook | AU | AFL, EPL, F1, FIFA_WORLD_CUP, IPL, MLB, MLS, NBA, NFL, NHL, NRL, RUGBYUNION_INTERNATIONALS, UCL, UECL, UEL, UFC | | [Polymarket](https://sockodds.com/bookmakers/polymarket-odds-api/) | `polymarket` | Prediction Market | Global | ATP, BUNDESLIGA, EPL, FR_LIGUE_1, LA_LIGA, MLB, MLS, NFL, WTA | | [ProphetX](https://sockodds.com/bookmakers/prophetexchange-odds-api/) | `prophetexchange` | Betting Exchange | Global | BUNDESLIGA, EPL, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS, NFL | | [Sportsbet](https://sockodds.com/bookmakers/sportsbet-odds-api/) | `sportsbet` | Sportsbook | AU | AFL, ATP, BR_SERIE_A, EPL, F1, FIFA_WORLD_CUP, FR_LIGUE_1, ICELAND_1_DEILD, ICELAND_BESTA_DEILD, IPL, IT_SERIE_A, LA_LIGA, MLB, MLS, NBA, NFL, NHL, NRL, SLOVAK_2_LIGA, SLOVAK_SUPER_LIGA, UCL, UECL, UEFA_CHAMPIONS_LEAGUE_QUALIFIERS, UEFA_CONFERENCE_LEAGUE_QUALIFIERS, UEFA_EUROPA_LEAGUE_QUALIFIERS, UEL, UFC, WTA | | [SwiftBet](https://sockodds.com/bookmakers/swiftbet-odds-api/) | `swiftbet` | Sportsbook | AU | AFL, AFLW, ATP, EPL, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, MLS, NRL, WTA | | [TAB](https://sockodds.com/bookmakers/tab-odds-api/) | `tab` | Sportsbook | AU | AFL, AFLW, BR_SERIE_A, BUNDESLIGA, EPL, FIFA_WORLD_CUP, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, MLS, NBA, NFL, NHL, NRL, RUGBYLEAGUE_NRLW, RUGBYUNION_INTERNATIONALS, UCL, UECL, UEL, UFC, WTA | | [TABtouch](https://sockodds.com/bookmakers/tabtouch-odds-api/) | `tabtouch` | Sportsbook | AU | AFL, AFLW, ATP, BUNDESLIGA, EPL, FIFA_WORLD_CUP, FR_LIGUE_1, IT_SERIE_A, LA_LIGA, MLB, NBA, NFL, NRL, RUGBYLEAGUE_NRLW, WTA | | [Unibet](https://sockodds.com/bookmakers/unibet-odds-api/) | `unibet` | Sportsbook | AU | AFL, AFLW, ATP, BR_SERIE_A, BUNDESLIGA, EPL, F1, FIFA_WORLD_CUP, FR_LIGUE_1, IPL, IT_SERIE_A, LA_LIGA, MLB, MLS, NBA, NFL, NHL, NRL, RUGBYLEAGUE_NRLW, RUGBYUNION_INTERNATIONALS, UCL, UECL, UEL, UFC, WTA | Not seeing a bookmaker you're looking for? More can be made available on the [Platform plan](https://sockodds.com/contact-us/?plan=platform). > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Sports Data Type - sportID URL: https://sockodds.com/docs/data-types/sports/ # Sports Data Type - sportID [#sports-data-type---sportid] > This page may include sports not in your plan. For the up-to-date list available to your key call the `/sports` endpoint. Each `sportID` corresponds to a specific sport. Sports can have more than one league. | sportID | Name | Leagues | points means | | --- | --- | --- | --- | | `AUSSIE_RULES` | [Aussie Rules](https://sockodds.com/sports/aussie-rules-football-odds-api/) | `AFL` `AFLW` | points | | `BASEBALL` | [Baseball](https://sockodds.com/sports/baseball-odds-api/) | `MLB` | runs | | `BASKETBALL` | [Basketball](https://sockodds.com/sports/basketball-odds-api/) | `NBA` | points | | `CRICKET` | [Cricket](https://sockodds.com/sports/cricket-odds-api/) | `IPL` | runs | | `FOOTBALL` | [American Football](https://sockodds.com/sports/football-odds-api/) | `NFL` | points | | `ICE_HOCKEY` | [Ice Hockey](https://sockodds.com/sports/hockey-odds-api/) | `NHL` | goals | | `MMA` | [MMA](https://sockodds.com/sports/mma-odds-api/) | `UFC` | 1 for the winner | | `MOTOR_RACING` | [Motorsports](https://sockodds.com/sports/motorsports-odds-api/) | `F1` | finishing position | | `RUGBY_LEAGUE` | [Rugby League](https://sockodds.com/sports/rugby-league-odds-api/) | `NRL` `RUGBYLEAGUE_NRLW` `RUGBYLEAGUE_STATE_ORIGIN` | points | | `RUGBY_UNION` | [Rugby Union](https://sockodds.com/sports/rugby-union-odds-api/) | `RUGBYUNION_INTERNATIONALS` | points | | `SOCCER` | [Soccer](https://sockodds.com/sports/soccer-odds-api/) | `BR_SERIE_A` `BUNDESLIGA` `EPL` `FIFA_WORLD_CUP` `FR_LIGUE_1` `ICELAND_1_DEILD` `ICELAND_BESTA_DEILD` `IT_SERIE_A` `LA_LIGA` `MLS` `SCOTTISH_PREM` `SLOVAK_2_LIGA` `SLOVAK_SUPER_LIGA` `UCL` `UECL` `UEFA_CHAMPIONS_LEAGUE_QUALIFIERS` `UEFA_CONFERENCE_LEAGUE_QUALIFIERS` `UEFA_EUROPA_LEAGUE_QUALIFIERS` `UEL` | goals | | `TENNIS` | [Tennis](https://sockodds.com/sports/tennis-odds-api/) | `ATP` `WTA` | sets | > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # League Data Type - leagueID URL: https://sockodds.com/docs/data-types/leagues/ # League Data Type - leagueID [#league-data-type---leagueid] A `leagueID` uniquely identifies each league. Each league belongs to exactly one `sportID`, but each sport typically has several leagues. Teams (`teamID`) and players (`playerID`) are defined per league: Arsenal in the Premier League and Arsenal in the Champions League are `ARSENAL_EPL` and `ARSENAL_UCL`. > League keys are the feed's own vocabulary and are never constructed: `RUGBYLEAGUE_NRLW`, not `NRLW`; `FR_LIGUE_1`, not `LIGUE_1`. Look them up from `/leagues`, which also reports `enabled` per your plan. | leagueID | Name | sportID | Events | Sample eventID | | --- | --- | --- | --- | --- | | `AFL` | [AFL](https://sockodds.com/leagues/afl-odds-api/) | `AUSSIE_RULES` | 6 | afl_2026-09-03_fremantle_vs_hawthorn | | `AFLW` | [AFLW](https://sockodds.com/leagues/aflw-odds-api/) | `AUSSIE_RULES` | 9 | aflw_2026-09-05_brisbane_lions_vs_gws_giants | | `ATP` | [ATP](https://sockodds.com/leagues/atp-odds-api/) | `TENNIS` | 12 | atp_2026-08-20_alexei_popyrin_vs_hugo_gaston | | `BR_SERIE_A` | [Brasileiro Série A](https://sockodds.com/leagues/br-serie-a-odds-api/) | `SOCCER` | 19 | brazilian_serie_a_2026-08-15_athletico_paranaense_vs_bragantino | | `BUNDESLIGA` | [Bundesliga](https://sockodds.com/leagues/bundesliga-odds-api/) | `SOCCER` | 27 | bundesliga_2026-09-04_vfb_stuttgart_vs_1_fc_koln | | `EPL` | [Premier League](https://sockodds.com/leagues/epl-odds-api/) | `SOCCER` | 39 | epl_2026-08-22_brentford_vs_tottenham_hotspur | | `F1` | [Formula 1](https://sockodds.com/leagues/f1-odds-api/) | `MOTOR_RACING` | 3 | f1_2026_canadian_grand_prix | | `FIFA_WORLD_CUP` | [FIFA World Cup](https://sockodds.com/leagues/fifa-world-cup-odds-api/) | `SOCCER` | 1 | mens_world_cup_2026-07-19_spain_vs_argentina | | `FR_LIGUE_1` | [Ligue 1](https://sockodds.com/leagues/fr-ligue-1-odds-api/) | `SOCCER` | 27 | ligue_1_2026-09-03_toulouse_vs_lille | | `ICELAND_1_DEILD` | [Iceland 1. deild](https://sockodds.com/leagues/iceland-1-deild-odds-api/) | `SOCCER` | 1 | iceland_1_deild_2026-08-03_vestri_vs_grotta | | `ICELAND_BESTA_DEILD` | [Besta deild](https://sockodds.com/leagues/iceland-besta-deild-odds-api/) | `SOCCER` | 3 | iceland_besta_deild_2026-08-23_ka_akureyri_vs_kr_reykjavik | | `IPL` | [IPL](https://sockodds.com/leagues/ipl-odds-api/) | `CRICKET` | 5 | ipl_2026-05-21_gujarat_titans_vs_chennai_super_kings | | `IT_SERIE_A` | [Serie A](https://sockodds.com/leagues/it-serie-a-odds-api/) | `SOCCER` | 33 | serie_a_2026-09-04_genoa_vs_como | | `LA_LIGA` | [La Liga](https://sockodds.com/leagues/la-liga-odds-api/) | `SOCCER` | 41 | la_liga_2026-09-03_real_sociedad_vs_celta_vigo | | `MLB` | [MLB](https://sockodds.com/leagues/mlb-odds-api/) | `BASEBALL` | 85 | mlb_2026-09-01_arizona_diamondbacks_vs_philadelphia_phillies | | `MLS` | [MLS](https://sockodds.com/leagues/mls-odds-api/) | `SOCCER` | 96 | mls_2026-08-15_atlanta_united_vs_new_york_red_bulls | | `NBA` | [NBA](https://sockodds.com/leagues/nba-odds-api/) | `BASKETBALL` | 1 | nba_2026-06-13_new_york_knicks_at_san_antonio_spurs | | `NFL` | [NFL](https://sockodds.com/leagues/nfl-odds-api/) | `FOOTBALL` | 130 | nfl_2026-08-21_houston_texans_vs_las_vegas_raiders | | `NHL` | [NHL](https://sockodds.com/leagues/nhl-odds-api/) | `ICE_HOCKEY` | 1 | nhl_2026-06-15_carolina_hurricanes_at_vegas_golden_knights | | `NRL` | [NRL](https://sockodds.com/leagues/nrl-odds-api/) | `RUGBY_LEAGUE` | 16 | nrl_2026-09-03_canterbury_bulldogs_vs_brisbane_broncos | | `RUGBYLEAGUE_NRLW` | [NRLW](https://sockodds.com/leagues/nrlw-odds-api/) | `RUGBY_LEAGUE` | 9 | nrlw_2026-09-05_new_zealand_warriors_vs_cronulla_sharks | | `RUGBYLEAGUE_STATE_ORIGIN` | [State of Origin](https://sockodds.com/leagues/state-of-origin-odds-api/) | `RUGBY_LEAGUE` | 1 | state_of_origin_2026-07-08_queensland_maroons_vs_new_south_wales_blues | | `RUGBYUNION_INTERNATIONALS` | [Rugby Union Internationals](https://sockodds.com/leagues/rugby-union-internationals-odds-api/) | `RUGBY_UNION` | 8 | ru_internationals_2026-09-04_japan_women_vs_fiji_women | | `SCOTTISH_PREM` | [Scottish Premiership](https://sockodds.com/leagues/scottish-premiership-odds-api/) | `SOCCER` | 6 | scottish_prem_2026-08-22_dundee_united_vs_dundee | | `SLOVAK_2_LIGA` | [Slovak 2. Liga](https://sockodds.com/leagues/slovak-2-liga-odds-api/) | `SOCCER` | 1 | slovak_2_liga_2026-08-03_inter_bratislava_vs_zilina_ii | | `SLOVAK_SUPER_LIGA` | [Slovak Super Liga](https://sockodds.com/leagues/slovak-super-liga-odds-api/) | `SOCCER` | 2 | slovak_super_liga_2026-08-22_zemplin_michalovce_vs_skalica | | `UCL` | [Champions League](https://sockodds.com/leagues/uefa-champions-league-odds-api/) | `SOCCER` | 17 | champions_league_2026-05-30_paris_saint-germain_vs_arsenal | | `UECL` | [Conference League](https://sockodds.com/leagues/uefa-conference-league-odds-api/) | `SOCCER` | 1 | conference_league_2026-05-27_crystal_palace_vs_rayo_vallecano | | `UEFA_CHAMPIONS_LEAGUE_QUALIFIERS` | [Champions League Qualifiers](https://sockodds.com/leagues/uefa-champions-league-qualifiers-odds-api/) | `SOCCER` | 5 | ucl_qualifiers_2026-08-18_dinamo_zagreb_vs_vikingur_reykjavik | | `UEFA_CONFERENCE_LEAGUE_QUALIFIERS` | [Conference League Qualifiers](https://sockodds.com/leagues/uefa-conference-league-qualifiers-odds-api/) | `SOCCER` | 19 | uecl_qualifiers_2026-08-20_braga_vs_austria_wien | | `UEFA_EUROPA_LEAGUE_QUALIFIERS` | [Europa League Qualifiers](https://sockodds.com/leagues/uefa-europa-league-qualifiers-odds-api/) | `SOCCER` | 8 | uel_qualifiers_2026-08-20_benfica_vs_agf | | `UEL` | [Europa League](https://sockodds.com/leagues/uefa-europa-league-odds-api/) | `SOCCER` | 2 | epl_2026-05-20_sc_freiburg_vs_aston_villa | | `UFC` | [UFC](https://sockodds.com/leagues/ufc-odds-api/) | `MMA` | 54 | ufc_2026-05-30_amorim_jaqueline_vs_lookboonmee_loma | | `WTA` | [WTA](https://sockodds.com/leagues/wta-odds-api/) | `TENNIS` | 4 | wta_2026-08-20_amanda_anisimova_vs_jessica_pegula | Not seeing a league you're looking for? [Contact us](https://sockodds.com/contact-us/). > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Stat Data Type - statID URL: https://sockodds.com/docs/data-types/stats/ # Stat Data Type - statID [#stat-data-type---statid] A **statID** corresponds to a specific statistic. Each sport has its own set. It is used to find results (`results...`) and to define odds markets (`odds..statID`). ## The points statID [#the-points-statid] `points` is special: in every sport it is the stat that determines the winner. In baseball and cricket it is runs; in soccer and hockey goals; in tennis sets won; in MMA the winner has 1 and the loser 0; in motorsports it is finishing position. The full-game `points` value includes extra time and shootouts where the book settles that way. ## Stats by sport [#stats-by-sport] ### Aussie Rules [#aussie-rules] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `goals` | Goals | 1,720 | all, team, player | | `points` | Points / score | 1,710 | all, team | | `disposals` | Disposals | 236 | player | | `firstGoalscorer` | First goalscorer | 154 | player | | `lastGoalscorer` | Last goalscorer | 95 | player | | `hsq` | Highest scoring quarter | 60 | all | | `hsh` | Highest scoring half | 45 | all | | `teamhsh` | Team highest scoring half | 30 | team | | `teamhsq` | Team highest scoring quarter | 30 | team | | `firstGoalscorerHome` | First goalscorer (home) | 29 | player | | `firstGoalscorerAway` | First goalscorer (away) | 18 | player | ### Tennis [#tennis] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 78 | all, team | | `games` | Games | 32 | all, team | ### Soccer [#soccer] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 31,312 | all, team, player | | `correctScore` | Correct score | 18,994 | all | | `firstToScore` | First to score | 9,964 | team, player | | `lastToScore` | Last to score | 5,720 | team, player | | `shots_onGoal` | Shots on goal | 3,930 | all, team, player | | `combinedCards` | Cards | 3,910 | all, team, player | | `winningMargin` | Winning margin | 3,398 | all, team | | `cornerKicks` | Corners | 3,202 | all, team | | `goals+assists` | Goals + assists | 2,882 | player | | `assists` | Assists | 2,870 | player | | `htft` | Half-time / full-time | 2,497 | all | | `bothTeamsScored` | Both teams to score | 2,000 | all | | `tackles` | Tackles | 1,969 | player | | `winToNil` | Win to nil | 1,473 | team | | `fouls` | Fouls | 1,358 | team, player | | `cleanSheet` | Clean sheet | 970 | team | | `winsEitherHalf` | Wins either half | 911 | team | | `foulsDrawn` | Fouls drawn | 806 | player | | `winsBothHalves` | Wins both halves | 793 | team | | `teamGoalInBothHalves` | Team scores in both halves | 780 | team | | `highestScoringHalf` | Highest scoring half | 606 | all | | `whichTeamsScore` | Which teams score | 539 | all | | `goalInBothHalves` | Goal in both halves | 484 | all | | `teamWinFromBehind` | Win from behind | 468 | all, team | | `bothTeamsScored2plus` | Both teams score 2+ | 440 | all | | `firstGoalscorer` | First goalscorer | 396 | player | | `saves` | Saves | 349 | all, team, player | | `passes_attempted` | Passes attempted | 335 | player | | `ownGoal` | Own goal | 322 | all | | `teamFirstGoalHalf` | Half of first goal | 288 | all | | `shotsOnTarget` | Shots on target | 274 | all, player | | `bookingPoints` | Booking points | 126 | all, team | | `shots` | Shots | 73 | player | | `totalShots` | Total shots | 50 | all | | `headerGoal` | Header goal | 46 | player | | `totalFouls` | Total fouls | 43 | all | | `outsideBoxGoal` | Goal from outside the box | 41 | player | | `woodwork` | Woodwork | 39 | all, team | | `bothTeamsCarded` | Both teams carded | 28 | all | | `bothTeamsCarded2plus` | Both teams carded 2+ | 28 | all | | `bothTeamsCarded3plus` | Both teams carded 3+ | 28 | all | | `bothHalvesScored` | Scores in both halves | 16 | all | | `scoreMethod` | Scoring method | 8 | player | | `redCards` | Red cards | 1 | all | ### Motorsports [#motorsports] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `podium` | Podium | 22 | player | | `points` | Points / score | 22 | player | | `raceWins` | Race wins | 22 | player | | `championship` | Championship | 22 | player | | `constructorRace` | Constructor race | 11 | player | | `constructorRaceWins` | Constructor race wins | 11 | player | | `constructorChampionship` | Constructor championship | 11 | player | ### Cricket [#cricket] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `runs` | Runs | 1,053 | team, player | | `fours` | Fours | 395 | team, player | | `wickets` | Wickets | 366 | player | | `sixes` | Sixes | 260 | team, player | | `playerOfMatch` | Player of the match | 194 | player | | `wicket` | Wicket | 100 | team | | `performance` | Performance | 96 | player | | `dismissal` | Method of dismissal | 90 | team | | `strikeRate` | Strike rate | 32 | player | | `firstOut` | First out | 24 | team | | `partnership` | Partnership | 16 | team | | `century` | Century | 10 | all | | `toss` | Toss | 8 | team | | `superOver` | Super over | 8 | all | | `halfCentury` | Half century | 8 | all | | `hatTrick` | Hat-trick | 4 | all | | `sixSixes` | Six sixes | 4 | all | ### Baseball [#baseball] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 19,138 | all, team, player | | `batting_hits` | Hits | 4,992 | all, team, player | | `batting_homeRuns` | Home runs | 3,140 | player | | `batting_RBI` | RBIs | 3,026 | player | | `batting_totalBases` | Total bases | 3,008 | player | | `batting_doubles` | Doubles | 2,926 | player | | `batting_singles` | Singles | 2,730 | player | | `batting_triples` | Triples | 1,362 | player | | `batting_hits+runs+rbi` | Hits + runs + RBIs | 966 | player | | `batting_strikeouts` | Batter strikeouts | 888 | player | | `margin` | Margin | 850 | team | | `batting_stolenBases` | Stolen bases | 766 | player | | `batting_basesOnBalls` | Walks | 690 | player | | `pitching_strikeouts` | Pitcher strikeouts | 318 | player | | `pitching_outs` | Outs recorded | 262 | player | | `lastToScore` | Last to score | 170 | team | | `firstToScore` | First to score | 170 | team | | `pitching_hits` | Hits allowed | 108 | player | | `pitching_basesOnBalls` | Walks allowed | 76 | player | | `pitching_win` | Pitcher win | 52 | player | | `pitching_earnedRuns` | Earned runs | 10 | player | ### Basketball [#basketball] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 129 | all, team, player | | `points+rebounds` | Points + rebounds | 34 | player | | `points+rebounds+assists` | Points + rebounds + assists | 34 | player | | `rebounds` | Rebounds | 32 | player | | `threePointersMade` | Three-pointers made | 32 | player | | `steals` | Steals | 28 | player | | `points+assists` | Points + assists | 28 | player | | `rebounds+assists` | Rebounds + assists | 28 | player | | `assists` | Assists | 26 | player | | `doubleDouble` | Double-double | 22 | player | | `firstBasket` | First basket | 20 | player | | `blocks` | Blocks | 16 | player | | `tripleDouble` | Triple-double | 6 | player | | `firstTo15` | First to 15 | 2 | team | | `firstTo20` | First to 20 | 2 | team | | `firstTo25` | First to 25 | 2 | team | ### American Football [#american-football] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 1,066 | all, team | | `firstToScore` | First to score | 8 | team | ### Ice Hockey [#ice-hockey] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 232 | all, team, player | | `goals+assists` | Goals + assists | 146 | player | | `assists` | Assists | 93 | player | | `lastToScore` | Last to score | 86 | team, player | | `shots_onGoal` | Shots on goal | 78 | player | | `firstToScore` | First to score | 74 | team, player | | `goals` | Goals | 37 | player | | `powerPlay_goals+assists` | Power-play points | 32 | player | | `blocks` | Blocks | 21 | player | | `bothTeamsScored` | Both teams to score | 8 | all | | `goalie_saves` | Goalie saves | 4 | player | | `goalie_goalsAgainst` | Goals against | 4 | player | ### Rugby League [#rugby-league] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 1,014 | all, team | | `tries` | Tries | 595 | all, team, player | | `htft` | Half-time / full-time | 234 | all | | `firstTryScorer` | First try scorer | 175 | player | | `margin` | Margin | 156 | team | | `winsBothHalves` | Wins both halves | 104 | team | | `winsEitherHalf` | Wins either half | 104 | team | | `race10` | Race to 10 | 78 | all, team | | `firstToScore` | First to score | 52 | team | | `firstTryScored` | First try scored | 52 | team | | `anytimeTryScorer` | Anytime try scorer | 35 | player | | `lastTryScorerAway` | Last try scorer (away) | 18 | player | | `firstTryScorerAway` | First try scorer (away) | 18 | player | | `lastTryScorerHome` | Last try scorer (home) | 18 | player | | `firstTryScorerHome` | First try scorer (home) | 18 | player | ### Rugby Union [#rugby-union] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 210 | all, team, player | | `firstGoalscorer` | First goalscorer | 30 | player | | `htft` | Half-time / full-time | 27 | all | | `highestScoringHalf` | Highest scoring half | 21 | all | | `firstToScore` | First to score | 12 | team | | `winsBothHalves` | Wins both halves | 9 | team | | `winningMargin` | Winning margin | 8 | all, team | | `lastToScore` | Last to score | 2 | team | ### MMA [#mma] | statID | Name | Markets | Levels | | --- | --- | --- | --- | | `points` | Points / score | 216 | all, team | | `wonBy_knockout` | Win by KO/TKO | 216 | all, team | | `wonBy_decision` | Win by decision | 108 | all | | `wonBy_submission` | Win by submission | 108 | all | | `roundsCompleted` | Rounds completed | 108 | all | Live from `/v2/stats/?sportID=…`. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Stat Entity Data Type - statEntityID URL: https://sockodds.com/docs/data-types/stat-entity/ # Stat Entity Data Type - statEntityID [#stat-entity-data-type---statentityid] A `statEntityID` identifies **whose** performance on a statistic we're tracking. If the [statID](https://sockodds.com/docs/data-types/stats/) is WHAT, the statEntityID is WHO. ## Possible values [#possible-values] | Value | Description | | --- | --- | | `all` | The combined performance of both teams. A total-points over/under uses `all`. | | `home` | The home team. | | `away` | The away team. | | *playerID* | Player props: the statEntityID is a specific playerID (`LACHIE_NEALE_1_AFL`). | | *teamID* | Only in events where `type` = `tournament` (an F1 race): there is no home and away, so the entity is a teamID. | ## Usage in odds [#usage-in-odds] For an over/under disposals bet (`sideID=over`, `betTypeID=ou`, `statID=disposals`): `statEntityID=LACHIE_NEALE_1_AFL` is that player's disposals; `home` would be the home team's total; `all` both teams combined. ## Fixed statEntityID values [#fixed-statentityid-values] For some bet types the entity is fixed by the side, and included for consistency: | betTypeID | sideID | statEntityID | | --- | --- | --- | | `ml` | `home` | `home` | | `ml` | `away` | `away` | | `sp` | `home` | `home` | | `sp` | `away` | `away` | | `ml3way` | `home` | `home` | | `ml3way` | `away` | `away` | | `ml3way` | `draw` | `all` | | `ml3way` | `home+draw` | `home` | | `ml3way` | `away+draw` | `away` | | `ml3way` | `not_draw` | `all` | Distribution in the feed right now: `` 80,562, `all` 45,309, `home` 18,944, `away` 18,702. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Period Data Type - periodID URL: https://sockodds.com/docs/data-types/periods/ # Period Data Type - periodID [#period-data-type---periodid] A period is the portion of an event a stat or bet covers. Each sport has its own periods. > Deprecation notice`1ix5` (first five innings) and `1h` both appear for baseball where books price them; prefer `1h` in new code, matching SportsGameOdds' deprecation of `1ix5`. ## Full event scores [#full-event-scores] The `game` periodID covers the entire event, so `results.game.{home,away}.points` includes extra time and shootouts where applicable (`game` = `reg` + `ot` + `so`). Read any segment from its own periodID inside `results`. | Segment | Read from | | --- | --- | | Regulation only | `results.reg.{home,away}.points` | | Each half / quarter / period | `results.1h`, `results.2h`, `results.1q`, … | | Extra time only | `results.ot.{home,away}.points` | | Penalty shootout | `results.so.{home,away}.points` | ## Periods in the feed [#periods-in-the-feed] | periodID | Meaning | Markets | | --- | --- | --- | | `game` | Full game including extra time where the book settles that way | 123,152 | | `1h` | 1st half | 9,227 | | `reg` | Regulation time only | 8,285 | | `2h` | 2nd half | 6,449 | | `1i` | 1st inning | 1,530 | | `1ix3` | First three innings | 1,530 | | `1ix5` | First five innings | 1,530 | | `1ix7` | First seven innings | 1,020 | | `2i` | 2nd inning | 850 | | `3i` | 3rd inning | 850 | | `4i` | 4th inning | 850 | | `5i` | 5th inning | 850 | | `6i` | 6th inning | 850 | | `7i` | 7th inning | 850 | | `8i` | 8th inning | 850 | | `2q` | 2nd quarter | 674 | | `3q` | 3rd quarter | 590 | | `4q` | 4th quarter | 590 | | `1q` | 1st quarter | 468 | | `bothhalves` | Both halves | 458 | | `lead2i` | Lead after 2 innings | 255 | | `lead3i` | Lead after 3 innings | 255 | | `lead4i` | Lead after 4 innings | 255 | | `lead5i` | Lead after 5 innings | 255 | | `lead6i` | Lead after 6 innings | 255 | | `lead7i` | Lead after 7 innings | 255 | | `1wkt` | First wicket | 132 | | `season` | Season / futures | 66 | | `1ov` | 1st over | 44 | | `3ov` | 3rd over | 36 | | `4ov` | 4th over | 36 | | `2ov` | 2nd over | 32 | | `5ov` | 5th over | 32 | | `6ov` | 6th over | 32 | | `6ov_cum` | First six overs (cumulative) | 30 | | `1p` | 1st period | 20 | | `2p` | 2nd period | 20 | | `3p` | 3rd period | 20 | | `10ov_cum` | First ten overs | 18 | | `8ov_cum` | First eight overs | 16 | > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Bet Type and Side Data Types - betTypeID and sideID URL: https://sockodds.com/docs/data-types/bet-types/ # Bet Type and Side Data Types - betTypeID and sideID [#bet-type-and-side-data-types---bettypeid-and-sideid] A `betTypeID` corresponds to the style/grading of a bet. A `sideID` is the outcome of that bet being selected. ## Number of sides [#number-of-sides] For a 2-way betTypeID there are 2 sideIDs (`ou`: `over`, `under`). For a 3-way betTypeID there are 6: one for each core outcome plus the inverse of each (`ml3way`: `home` and the inverse `away+draw`, and so on). Pick markets (`pick`, `race10`, `topbat`) have one side per option. Events with `type=prop` are one-off proposition bets with `betTypeID=prop` and sides `side1`/`side2`; ignore them while getting started. ## Bet types in the feed [#bet-types-in-the-feed] | betTypeID | Meaning | Markets | | --- | --- | --- | | `ou` | over/under a line | 60,189 | | `yn` | yes/no | 45,324 | | `pick` | pick one of N | 26,777 | | `ml3way` | 1X2 (with the draw) | 10,214 | | `ml` | moneyline / head to head | 5,493 | | `sp` | spread / line / handicap | 4,282 | | `eo` | even/odd | 3,440 | | `sp3way` | 3-way handicap | 2,129 | | `exact` | exact value (e.g. correct score) | 1,006 | | `btts1` | both teams to score (variant 1) | 680 | | `btts2` | both teams to score (variant 2) | 680 | | `btts3` | both teams to score (variant 3) | 680 | | `topbat` | top batter | 219 | | `ei` | exact innings | 170 | | `btts4` | BTTS variant | 170 | | `btts5` | BTTS variant | 170 | | `btts6` | BTTS variant | 170 | | `btts7` | BTTS variant | 170 | | `race2` | race to 2 | 170 | | `race3` | race to 3 | 170 | | `race4` | race to 4 | 170 | | `race5` | race to 5 | 170 | | `race6` | race to 6 | 170 | | `race7` | race to 7 | 170 | | `topbowl` | top bowler | 143 | | `dismissal` | method of dismissal | 90 | | `most` | most of (sixes, etc.) | 88 | | `hsh` | highest-scoring half | 78 | | `race10` | race to 10 | 30 | | `race15` | race to 15 | 30 | | `race20` | race to 20 | 30 | | `race25` | race to 25 | 30 | | `hop` | handicap on points | 15 | ## Sides in the feed [#sides-in-the-feed] | sideID | Markets | | --- | --- | | `yes` | 41,569 | | `over` | 35,847 | | `under` | 21,307 | | `home` | 8,437 | | `away` | 8,429 | | `no` | 5,995 | | `draw` | 2,995 | | `odd` | 1,720 | | `even` | 1,720 | | `anytime` | 1,277 | | `not_draw` | 1,187 | | `away+draw` | 1,122 | | `home+draw` | 1,121 | | `3+` | 916 | | `2+` | 910 | | `by_1` | 450 | | `by_2` | 450 | | `by_3` | 446 | | `by_4plus` | 438 | | `1h` | 346 | | `2h` | 346 | | `dd` | 307 | | `dh` | 307 | | `hd` | 307 | | `hh` | 307 | | `aa` | 306 | | `ad` | 306 | | `ah` | 306 | | `da` | 306 | | `ha` | 306 | | `h1_a2` | 276 | | `h2_a1` | 276 | | `h3_a2` | 276 | | `h1_a0` | 275 | | `h1_a3` | 275 | | `h1_a4` | 275 | | `h2_a0` | 275 | | `h2_a3` | 275 | | `h3_a1` | 275 | | `h4_a1` | 275 | > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Odds Data Type - oddID URL: https://sockodds.com/docs/data-types/odds/ # Odds Data Type - oddID [#odds-data-type---oddid] An `oddID` is a unique identifier for a specific betting option: a specific side/outcome within a specific market. ## oddID format [#oddid-format] ``` {statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID} ``` | Component | Description | Examples | | --- | --- | --- | | [statID](https://sockodds.com/docs/data-types/stats/) | The statistic being wagered on | `points`, `disposals`, `tries` | | [statEntityID](https://sockodds.com/docs/data-types/stat-entity/) | Who the stat applies to | `home`, `away`, `all`, or a playerID | | [periodID](https://sockodds.com/docs/data-types/periods/) | The time period covered | `game`, `1h`, `1q` | | [betTypeID](https://sockodds.com/docs/data-types/bet-types/) | The type of bet | `ml`, `sp`, `ou` | | [sideID](https://sockodds.com/docs/data-types/bet-types/) | Which side of the bet | `home`, `away`, `over`, `under` | ## Example oddIDs [#example-oddids] | oddID | Description | | --- | --- | | `points-home-game-ml-home` | Head to head on the home team, full game | | `points-away-game-sp-away` | Line on the away team, full game | | `points-all-game-ou-over` | Over on total points, full game | | `points-home-1h-ml-home` | Head to head on the home team, 1st half | | `disposals-LACHIE_NEALE_1_AFL-game-ou-over` | Over on Lachie Neale's disposals | | `anytimeTryScorer-PLAYER_ID-game-yn-yes` | Anytime try scorer, every player (query form) | ## Accessing odds [#accessing-odds] Each event from `/events` has an `odds` object keyed by oddID: ```json { "eventID": "…", "odds": { "points-home-game-ml-home": { "oddID": "points-home-game-ml-home", "statID": "points", "statEntityID": "home", "periodID": "game", "betTypeID": "ml", "sideID": "home", "opposingOddID": "points-away-game-ml-away", "marketName": "Head to Head — Brisbane Lions", "fairOdds": "-115", "fairOddsAvailable": true, "bookOdds": "-108", "bookOddsAvailable": true, "bookSpread": null, "bookOverUnder": null, "fairSpread": null, "fairOverUnder": null, "started": false, "ended": false, "cancelled": false, "scoringSupported": true, "byBookmaker": { "sportsbet": { "bookmakerID": "sportsbet", "odds": "-108", "decimal": 1.93, "available": true, "lastUpdatedAt": "2026-09-06T08:27:48.000Z", "eventName": "Brisbane - Adelaide" }, "tab": { "bookmakerID": "tab", "odds": "-110", "decimal": 1.91, "available": true, "lastUpdatedAt": "2026-09-06T08:27:31.000Z" } } }, "points-all-game-ou-over": { "oddID": "points-all-game-ou-over", "statID": "points", "statEntityID": "all", "periodID": "game", "betTypeID": "ou", "sideID": "over", "fairOdds": "-108", "fairOverUnder": "165.5", "bookOdds": "-110", "bookOverUnder": "165.5", "byBookmaker": { "sportsbet": { "odds": "-110", "decimal": 1.91, "overUnder": "165.5", "available": true } } } } } ``` ## Key odds fields [#key-odds-fields] | Field | Description | | --- | --- | | `fairOdds` | Consensus odds without juice (vig removed), from exchange/sharp sources; Base and above | | `bookOdds` | Consensus odds across bookmakers (includes juice) | | `fairSpread` / `bookSpread` | The line for spread bets | | `fairOverUnder` / `bookOverUnder` | The line for O/U bets | | `fairOddsAvailable` / `bookOddsAvailable` | Whether each could be computed / is open at at least one book | | `byBookmaker` | Odds broken down by individual bookmaker, each with `decimal` | | `started` / `ended` / `cancelled` | Whether the bet period has started, ended or been cancelled | | `scoringSupported` | Whether the source can grade this market from results | | `altLines` | Alternate lines keyed by line (with `includeAltLines=true`) | ### Open/close odds by bookmaker [#openclose-odds-by-bookmaker] SportsGameOdds returns `openOdds`, `closeOdds`, `openSpread`, `closeSpread`, `openOverUnder` and `closeOverUnder` with `includeOpenCloseOdds=true`. SockOdds accepts the parameter for compatibility but does not snapshot opening and closing prices yet, so those fields are absent; use `lastUpdatedAt` and your own snapshots. ## Filtering by oddID [#filtering-by-oddid] ``` /events?oddID=points-home-game-ml-home,points-all-game-ou-over&includeOpposingOdds=true ``` Use `includeOpposingOdds=true` to automatically include the opposite side of each requested oddID. See [Supported Markets](https://sockodds.com/docs/data-types/markets/) for the complete list of oddIDs by league and bookmaker. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /events - Fetch Games, Odds, and Results URL: https://sockodds.com/docs/endpoints/getEvents/ # GET /events - Fetch Games, Odds, and Results [#get-events---fetch-games-odds-and-results] Get a list of Events `GET https://api.sockodds.com/v2/events/` Retrieve sports events with odds, status, teams, players, results and bookmaker links. Filter by league, team, date or odds market. Includes every bookmaker line your plan allows. > eventID takes priority over all other filtersWhen you provide eventID or eventIDs, the API uses only those IDs to fetch results — all other query filters (sportID, leagueID, live, started, finalized, startsAfter, startsBefore, includeFinished) are ignored. Response-shaping parameters like oddID, bookmakerID, playerID, includeAltLines and includeOpposingOdds still apply. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `eventID` | `string` | An eventID to get Event data for e.g. `afl_2026-09-12_brisbane_lions_vs_adelaide_crows` | | `eventIDs` | `string` | A comma separated list of eventIDs to get Event data for | | `sportID` | `string` | A sportID or comma-separated list of sportIDs to get Events for e.g. `AUSSIE_RULES` | | `leagueID` | `string` | A leagueID or comma-separated list of leagueIDs to get Events for e.g. `AFL,NRL` | | `type` | `string` | Only include Events of the specified type (match, tournament, prop) | | `oddsAvailable` | `boolean` | Whether you want only Events which do (true) or do not (false) have odds markets which are currently available (open for wagering) | | `oddsPresent` | `boolean` | Whether you want only Events which do (true) or do not (false) have any associated odds markets regardless of whether those odds markets are currently available | | `oddID` | `string` | An oddID or comma-separated list of oddIDs to include odds for. Replace the playerID slot with PLAYER_ID to match every player. `oddIDs` is accepted as an alias. e.g. `points-home-game-ml-home` | | `includeOpposingOdds` | `boolean` | Whether to include opposing odds for each included oddID (`includeOpposingOddIDs` is accepted as an alias) | | `includeAltLines` | `boolean` | Whether to include alternate lines in the odds byBookmaker data. Off by default — alt lines can multiply the payload. | | `expandResults` note | `boolean` | Whether to expand the results object to include all stat values rather than just the base setAccepted for compatibility. SockOdds always returns every result value the source carries, so this flag has no effect. | | `includeOpenCloseOdds` note | `boolean` | Whether to include open and close odds values (openOdds, closeOdds, openSpread, closeSpread, openOverUnder, closeOverUnder) in the odds byBookmaker dataAccepted for compatibility. SockOdds does not snapshot opening and closing prices yet, so these fields are absent whatever you pass. | | `bookmakerID` | `string` | A bookmakerID or comma-separated list of bookmakerIDs to include odds for e.g. `sportsbet,tab` | | `teamID` | `string` | A teamID or comma-separated list of teamIDs to include Events for e.g. `BRISBANE_LIONS_AFL` | | `playerID` | `string` | A playerID or comma-separated list of playerIDs to include Events (and associated odds) for | | `finalized` | `boolean` | Only include finalized Events (true), exclude unfinalized Events (false) or all Events (omit) | | `live` | `boolean` | Only include live Events (true), only non-live Events (false) or all Events (omit) | | `started` | `boolean` | Only include Events which have previously started (true), only Events which have not previously started (false) or all Events (omit) | | `ended` | `boolean` | Only include Events which have ended (true), only Events which have not ended (false) or all Events (omit) | | `cancelled` | `boolean` | Only include cancelled Events (true), only non-cancelled Events (false) or all Events (omit) | | `startsAfter` | `date-time` | Get Events that start after this date e.g. `2026-09-01T00:00:00Z` | | `startsBefore` | `date-time` | Get Events that start before this date | | `includeFinished` note | `boolean` | SockOdds extension. Without a date window, eventID or this flag, events that kicked off more than 24 hours ago are omitted. Pass true to list everything the store retains.SockOdds-only parameter; SportsGameOdds ignores it. | | `limit` | `number` | The maximum number of Events to return (default 10, max 100) | | `cursor` | `string` | The cursor for the request. Used to get the next group of results. This is an opaque token — pass the nextCursor value from the prior response unchanged. | ## Response [#response] Paginated: `{ "success": true, "data": Event[], "nextCursor": string | null, "notice": string? }` ### Event [#event] Contains basic information, consensus odds, results (stats & scores), team info, lineups and bookmaker links for a given event/game | Field | Type | Description | | --- | --- | --- | | `eventID` | `string` | Unique identifier for the event (e.g. afl_2026-09-12_brisbane_lions_vs_adelaide_crows) | | `sportID` | `string` | The sport (AUSSIE_RULES, RUGBY_LEAGUE, SOCCER, …) | | `leagueID` | `string` | The league (AFL, NRL, RUGBYLEAGUE_NRLW, EPL, …) — always the feed's own key, never constructed | | `type` | `string` | match, tournament or prop | | `status` | `object` | started, ended, live, cancelled, delayed, finalized, completed, startsAt, oddsPresent, oddsAvailable, currentPeriodID, periods{started,ended}, displayShort/displayLong | | `info` | `object` | displayName, commenceTime, lastUpdatedAt, stale (no source write for 45 min), bookmakersPresent, sourceBookmakerCount | | `teams` | `object` | home/away → { teamID, names{long,medium,short}, colors, statEntityID, score } | | `players` | `object` | playerID → { playerID, teamID, name, firstName, lastName } — the production SGO player ids | | `results` | `object` | Nested results in the format {periodID}.{statEntityID}.{statID} → number, where the source carries them | | `odds` | `object` | oddID → Odds object. Every market the event is priced on, all bookmakers nested under byBookmaker | | `links` | `object` | bookmakers{bookmakerID → event URL} and betslip{bookmakerID → bet-slip strategy} deeplinks, straight from each book's own event ids | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `400` | `{"success":false,"error":"Invalid params"}` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | | `500` | `{"success":false,"error":"Query failed"}` | ### Example response [#example-response] Trimmed to one market; see the [quickstart](https://sockodds.com/docs/basics/quickstart/) for a full event. ```json { "success": true, "nextCursor": "afl_2026-09-03_fremantle_vs_hawthorn", "data": [ { "eventID": "afl_2026-09-03_fremantle_vs_hawthorn", "sportID": "AUSSIE_RULES", "leagueID": "AFL", "type": "match", "status": { "started": false, "ended": false, "live": false, "cancelled": false, "finalized": false, "oddsPresent": true, "oddsAvailable": true }, "info": { "stale": false }, "odds": { "points-all-game-eo-odd": { "oddID": "points-all-game-eo-odd", "opposingOddID": "points-all-game-eo-even", "marketName": "Odd/Even Total Points", "statID": "points", "statEntityID": "all", "periodID": "game", "betTypeID": "eo", "sideID": "odd", "bookOdds": "-116", "fairOdds": null, "bookOverUnder": null, "bookSpread": null, "byBookmaker": { "unibet": { "bookmakerID": "unibet", "odds": "-115", "decimal": 1.87, "available": true, "lastUpdatedAt": "2026-09-03T10:22:09.000Z" }, "tabtouch": { "bookmakerID": "tabtouch", "odds": "-118", "decimal": 1.85, "available": true, "lastUpdatedAt": "2026-09-03T10:21:03.000Z" } } } } } ] } ``` ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/events/?leagueID=AFL&oddsAvailable=true&limit=1" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/events/?leagueID=AFL&oddsAvailable=true&limit=1", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/events/", params=dict(leagueID="AFL", oddsAvailable="true", limit="1"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/events/?leagueID=AFL&oddsAvailable=true&limit=1", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/events/?leagueID=AFL&oddsAvailable=true&limit=1")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/events/?leagueID=AFL&oddsAvailable=true&limit=1"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /odds/history - Fetch Odds Movement URL: https://sockodds.com/docs/endpoints/getOddsHistory/ # GET /odds/history - Fetch Odds Movement [#get-oddshistory---fetch-odds-movement] Get the tick-by-tick odds history of an Event `GET https://api.sockodds.com/v2/odds/history/` SockOdds extension. Every price, line and bet-limit change every bookmaker has published for one event, oldest first — the raw movement behind opening, closing and steam. Captured since 2026-08-14 and kept indefinitely; the last ~28 days are answered from the live store and everything older from the archive. > Usage notesFilter with oddID and bookmakerID — an NFL game logs ~25,000 ticks a day, and each request reads at most 50,000 ticks per tier before paging. Your plan's league scope and bookmaker cap apply exactly as on /events/. One bookmaker is one series: where a book reaches the log under two sources, the higher-ranked source is kept for the whole window. `notice` says when the archive tier did not answer, in which case the series is incomplete at its old end. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `eventID` | `string` | The eventID to get odds history for (required) e.g. `afl_2026-09-12_brisbane_lions_vs_adelaide_crows` | | `oddID` | `string` | An oddID or comma-separated list of oddIDs to include ticks for. `oddIDs` is accepted as an alias. e.g. `points-home-game-ml-home` | | `bookmakerID` | `string` | A bookmakerID or comma-separated list of bookmakerIDs to include ticks for e.g. `sportsbet,pinnacle` | | `from` | `date-time` | Start of the window. Defaults to 21 days before the event's kickoff date. e.g. `2026-08-22T00:00:00Z` | | `to` | `date-time` | End of the window (exclusive, capped at now). Defaults to 2 days after kickoff. from/to may span at most 400 days. | | `limit` | `number` | The maximum number of ticks to return (default 1000, max 5000) | | `cursor` | `string` | The cursor for the request. Used to get the next group of results. This is an opaque token — pass the nextCursor value from the prior response unchanged. | ## Response [#response] Paginated: `{ "success": true, "data": OddsHistoryTick[], "nextCursor": string | null }` ### OddsHistoryTick [#oddshistorytick] One recorded change to one bookmaker's price on one market (SockOdds extension). Odds are given as American strings (bookOdds) and decimals. | Field | Type | Description | | --- | --- | --- | | `eventID` | `string` | The event | | `oddID` | `string` | The market side, in the oddID format (statID-statEntityID-periodID-betTypeID-sideID) | | `bookmakerID` | `string` | The bookmaker | | `changedAt` | `string (date-time)` | When the bookmaker's quote changed | | `kind` | `string` | open = first quote seen · move = price, line or main-line status changed · limit = only the bet limit changed (Pinnacle) · close = the quote was withdrawn | | `bookOdds` | `string \| null` | The new price as American odds; null on a close | | `decimalOdds` | `number \| null` | The new price as decimal odds | | `previousBookOdds` | `string \| null` | The price this change replaced (American); null on an open | | `previousDecimalOdds` | `number \| null` | The price this change replaced (decimal) | | `line` | `number \| null` | The spread or total the price applies to; null for moneylines and yes/no markets | | `isMainLine` | `boolean \| null` | Whether this line was the bookmaker's headline line at the time | | `layOdds` | `number \| null` | Exchange lay price, where the bookmaker is an exchange | | `maxStake` | `number \| null` | The bet limit after the change, where the bookmaker publishes one | | `previousMaxStake` | `number \| null` | The bet limit before the change | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `400` | `{"success":false,"error":"Invalid params"}` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `404` | `{"success":false,"error":"Not found"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | | `500` | `{"success":false,"error":"Query failed"}` | | `503` | `` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/odds/history/?eventID=afl_2026-09-12_brisbane_lions_vs_adelaide_crows&oddID=points-home-game-ml-home&bookmakerID=sportsbet,pinnacle" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/odds/history/?eventID=afl_2026-09-12_brisbane_lions_vs_adelaide_crows&oddID=points-home-game-ml-home&bookmakerID=sportsbet,pinnacle", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/odds/history/", params=dict(eventID="afl_2026-09-12_brisbane_lions_vs_adelaide_crows", oddID="points-home-game-ml-home", bookmakerID="sportsbet,pinnacle"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/odds/history/?eventID=afl_2026-09-12_brisbane_lions_vs_adelaide_crows&oddID=points-home-game-ml-home&bookmakerID=sportsbet,pinnacle", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/odds/history/?eventID=afl_2026-09-12_brisbane_lions_vs_adelaide_crows&oddID=points-home-game-ml-home&bookmakerID=sportsbet,pinnacle")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/odds/history/?eventID=afl_2026-09-12_brisbane_lions_vs_adelaide_crows&oddID=points-home-game-ml-home&bookmakerID=sportsbet,pinnacle"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /teams - Fetch Team Data URL: https://sockodds.com/docs/endpoints/getTeams/ # GET /teams - Fetch Team Data [#get-teams---fetch-team-data] Get a list of Teams by ID or league `GET https://api.sockodds.com/v2/teams/` Retrieve team information including names, colours and identifiers. Filter by teamID, leagueID or sportID. Supports pagination. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `sportID` | `string` | A single sportID or comma-separated list of sportIDs to get Teams for | | `leagueID` | `string` | A single leagueID or comma-separated list of leagueIDs to get Teams for e.g. `AFL` | | `teamID` | `string` | A single teamID or comma-separated list of teamIDs to get data for e.g. `BRISBANE_LIONS_AFL` | | `limit` | `number` | The maximum number of Teams to return (default 50, max 250) | | `cursor` | `string` | The cursor for the request. Used to get the next group of results. This is an opaque token — pass the nextCursor value from the prior response unchanged. | ## Response [#response] Paginated: `{ "success": true, "data": Team[], "nextCursor": string | null }` ### Team [#team] Contains information on a team | Field | Type | Description | | --- | --- | --- | | `teamID` | `string` | e.g. BRISBANE_LIONS_AFL — defined per league | | `sportID` | `string` | | | `leagueID` | `string` | | | `names` | `object` | { long, medium, short } | | `colors` | `object` | { primary, secondary, primaryContrast, secondaryContrast } where known | | `statEntityID` | `string` | home or away on the event the team was read from | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `400` | `{"success":false,"error":"Invalid params"}` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | | `500` | `{"success":false,"error":"Query failed"}` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/teams/?leagueID=AFL" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/teams/?leagueID=AFL", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/teams/", params=dict(leagueID="AFL"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/teams/?leagueID=AFL", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/teams/?leagueID=AFL")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/teams/?leagueID=AFL"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /players - Fetch Player Data URL: https://sockodds.com/docs/endpoints/getPlayers/ # GET /players - Fetch Player Data [#get-players---fetch-player-data] Get a list of Players for a specific Team or Event `GET https://api.sockodds.com/v2/players/` Retrieve player information including names, team and identifiers. Filter by playerID, teamID or eventID. Supports pagination. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `teamID` | `string` | TeamID to get Players data for | | `eventID` | `string` | EventID to get Players data for | | `playerID` | `string` | PlayerID to get data for e.g. `SONNY_GRAY_1_MLB` | | `leagueID` note | `string` | SockOdds extension: a leagueID or comma-separated list to scope the player listSockOdds-only parameter. | | `limit` | `number` | The maximum number of Players to return (default 50, max 250) | | `cursor` | `string` | The cursor for the request. Used to get the next group of results. This is an opaque token — pass the nextCursor value from the prior response unchanged. | ## Response [#response] Paginated: `{ "success": true, "data": Player[], "nextCursor": string | null }` ### Player [#player] Contains information on a player | Field | Type | Description | | --- | --- | --- | | `playerID` | `string` | e.g. SONNY_GRAY_1_MLB — the production SportsGameOdds id format, unique across teams in a league | | `teamID` | `string` | | | `sportID` | `string` | | | `leagueID` | `string` | | | `name / firstName / lastName` | `string` | | | `position / jerseyNumber` | `string \| number` | Where the source carries them | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `400` | `{"success":false,"error":"Invalid params"}` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | | `500` | `{"success":false,"error":"Query failed"}` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/players/?leagueID=MLB&limit=5" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/players/?leagueID=MLB&limit=5", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/players/", params=dict(leagueID="MLB", limit="5"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/players/?leagueID=MLB&limit=5", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/players/?leagueID=MLB&limit=5")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/players/?leagueID=MLB&limit=5"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /sports - List All Sports URL: https://sockodds.com/docs/endpoints/getSports/ # GET /sports - List All Sports [#get-sports---list-all-sports] Get a list of sports `GET https://api.sockodds.com/v2/sports/` Retrieve all supported sports with their sportID values. ## Query parameters [#query-parameters] No query parameters. Authentication only. ## Response [#response] `{ "success": true, "data": Sport[], "nextCursor": string | null }` ### Sport [#sport] Contains information on a sport | Field | Type | Description | | --- | --- | --- | | `sportID` | `string` | | | `name / shortName` | `string` | | | `enabled` | `boolean` | Whether your plan can read this sport | | `activeEvents` | `number` | Events currently carried | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/sports/" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/sports/", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/sports/", params={}, headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/sports/", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/sports/")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/sports/"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /leagues - List All Leagues URL: https://sockodds.com/docs/endpoints/getLeagues/ # GET /leagues - List All Leagues [#get-leagues---list-all-leagues] Get a list of Leagues `GET https://api.sockodds.com/v2/leagues/` Retrieve all supported leagues with leagueID values. `enabled` reflects what your plan can see. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `sportID` | `string` | The sport to get leagues for | | `leagueID` | `string` | The league to get data for | ## Response [#response] `{ "success": true, "data": League[], "nextCursor": string | null }` ### League [#league] Contains information on a specific league | Field | Type | Description | | --- | --- | --- | | `leagueID` | `string` | | | `sportID` | `string` | | | `name / shortName` | `string` | | | `enabled` | `boolean` | Whether your plan can read this league | | `activeEvents` | `number` | Events currently carried | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `400` | `{"success":false,"error":"Invalid params"}` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/leagues/?sportID=RUGBY_LEAGUE" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/leagues/?sportID=RUGBY_LEAGUE", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/leagues/", params=dict(sportID="RUGBY_LEAGUE"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/leagues/?sportID=RUGBY_LEAGUE", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/leagues/?sportID=RUGBY_LEAGUE")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/leagues/?sportID=RUGBY_LEAGUE"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /stats - List All Stats URL: https://sockodds.com/docs/endpoints/getStats/ # GET /stats - List All Stats [#get-stats---list-all-stats] Get a list of StatIDs `GET https://api.sockodds.com/v2/stats/` Retrieve all supported statistics with statID values. Filter by sportID and by the level (all, team, player) the stat is priced at. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `statID` | `string` | StatID to get data for | | `sportID` | `string` | SportID to get StatIDs for | | `statLevel` | `string` | Level of the stat, must be used in combination with sportID. Must be one of all, player, or team. Shows stats that are applicable to that specified entity, defaults to all. | ## Response [#response] `{ "success": true, "data": Stat[], "nextCursor": string | null }` ### Stat [#stat] Contains information on a statistic (statID) | Field | Type | Description | | --- | --- | --- | | `statID` | `string` | | | `supportedLevels` | `object` | { all, team, player } — which statEntityID classes it is priced at | | `displays` | `object` | { short, long } | | `isScoreStat` | `boolean` | True for `points`, the stat that decides the event | | `supportedSports` | `object` | sportID → true | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `400` | `{"success":false,"error":"Invalid params"}` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/stats/?sportID=CRICKET" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/stats/?sportID=CRICKET", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/stats/", params=dict(sportID="CRICKET"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/stats/?sportID=CRICKET", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/stats/?sportID=CRICKET")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/stats/?sportID=CRICKET"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /markets - Fetch Market Metadata URL: https://sockodds.com/docs/endpoints/getMarkets/ # GET /markets - Fetch Market Metadata [#get-markets---fetch-market-metadata] Get a list of Markets `GET https://api.sockodds.com/v2/markets/` Retrieve metadata about markets, keyed by oddID: identifiers, display names, classification (main markets, props, prop type, sub-periods) and which leagues and bookmakers currently price each one. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `oddID` | `string` | A single oddID or comma-separated list of oddIDs. Used to specify specific Markets to return. | | `sportID` | `string` | A single sportID or comma-separated list of sportIDs to filter Markets by | | `leagueID` | `string` | A single leagueID or comma-separated list of leagueIDs to filter Markets by | | `bookmakerID` | `string` | A single bookmakerID or comma-separated list of bookmakerIDs to filter Markets by | | `statID` | `string` | A single statID or comma-separated list of statIDs to filter Markets by | | `statEntityID` | `string` | A single statEntityID or comma-separated list of statEntityIDs to filter Markets by | | `periodID` | `string` | A single periodID or comma-separated list of periodIDs to filter Markets by | | `betTypeID` | `string` | A single betTypeID or comma-separated list of betTypeIDs to filter Markets by | | `sideID` | `string` | A single sideID or comma-separated list of sideIDs to filter Markets by | | `isMainMarket` | `boolean` | Filter to only include main markets (main period moneyline, spread, and over/under) | | `isProp` | `boolean` | Filter by whether it is any type of prop bet market | | `isSubPeriod` | `boolean` | Filter by whether it tracks a sub/non-main period | | `propType` | `string` | Filter by prop type (game_prop, team_prop, player_prop, other_prop) | | `isSupported` | `boolean` | Filter whether this market is priced by at least 1 bookmaker in at least 1 league. Defaults to true if not specified. | | `limit` | `number` | The maximum number of Markets to return (default: 100, max: 10000) | | `cursor` | `string` | The cursor for the request. Used to get the next group of results. This is an opaque token — pass the nextCursor value from the prior response unchanged. | ## Response [#response] Paginated: `{ "success": true, "data": Market[], "nextCursor": string | null }` ### Market [#market] Contains information about a betting market, including its classification and support data across leagues and bookmakers | Field | Type | Description | | --- | --- | --- | | `oddID` | `string` | The unique identifier for this market | | `statID / statEntityID / periodID / betTypeID / sideID` | `string` | The decomposed grammar | | `playerID / teamID` | `string` | Set for player props and tournament team markets | | `marketGroupID / marketGroupName` | `string` | The group (all sides of the market) this market belongs to, and its display name | | `isMainMarket` | `boolean` | True for the full-game moneyline, spread and total | | `isMainDerivative` | `boolean` | True for a sub-period of a main market | | `isSubPeriod` | `boolean` | True when the market tracks a non-main period | | `isProp` | `boolean` | True if this is a prop bet | | `propType` | `string` | game_prop, team_prop, player_prop or other_prop | | `isSupported` | `boolean` | True if at least one bookmaker prices it in at least one league | | `activeEvents` | `number` | Unique events with this market priced right now | | `support` | `object` | leagueID → { bookmakerID → true } | | `bookmakers` | `string[]` | SockOdds extension: the flat list of bookmakers pricing it | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `400` | `{"success":false,"error":"Invalid params"}` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | | `500` | `{"success":false,"error":"Query failed"}` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/markets/?leagueID=AFL&isMainMarket=true" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/markets/?leagueID=AFL&isMainMarket=true", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/markets/", params=dict(leagueID="AFL", isMainMarket="true"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/markets/?leagueID=AFL&isMainMarket=true", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/markets/?leagueID=AFL&isMainMarket=true")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/markets/?leagueID=AFL&isMainMarket=true"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # GET /account/usage - Check API Usage and Limits URL: https://sockodds.com/docs/endpoints/getUsageData/ # GET /account/usage - Check API Usage and Limits [#get-accountusage---check-api-usage-and-limits] Get rate-limits and usage data about your API key `GET https://api.sockodds.com/v2/account/usage` Check your API key's tier, rate limits and current usage by interval. Calling it counts as a request like any other. ## Query parameters [#query-parameters] No query parameters. Authentication only. ## Response [#response] `{ "success": true, "data": AccountUsage[], "nextCursor": string | null }` ### AccountUsage [#accountusage] Your API key's tier, rate limits and current usage | Field | Type | Description | | --- | --- | --- | | `keyID` | `string` | The hashed identifier for the API key (the first 16 hex of sha256) | | `customerID` | `string \| null` | Billing customer id where one exists | | `email` | `string \| null` | | | `isActive` | `boolean` | | | `tier` | `string` | lite, base or internal | | `rateLimits` | `object` | per-second / per-minute / per-hour / per-day / per-month → RateLimitInterval | ### Status codes [#status-codes] | Status | Body | | --- | --- | | `200` | `Success` | | `401` | `{"success":false,"error":"Invalid API key"}` | | `403` | `{"success":false,"error":"Inactive API key"}` | | `404` | `{"success":false,"error":"Not found"}` | | `429` | `{"success":false,"error":"Rate limit exceeded"} (Retry-After header)` | ### Example response [#example-response] ```json { "success": true, "data": [ { "keyID": "5fb8c85833e6dcf5", "customerID": null, "email": "you@example.com", "isActive": true, "tier": "base", "rateLimits": { "per-second": { "max-requests": null, "current-requests": null, "max-entities": null, "current-entities": null }, "per-minute": { "max-requests": 60, "current-requests": 3, "max-entities": null, "current-entities": null }, "per-hour": { "max-requests": null, "current-requests": null, "max-entities": null, "current-entities": null }, "per-day": { "max-requests": null, "current-requests": 412, "max-entities": null, "current-entities": 2210 }, "per-month": { "max-requests": null, "current-requests": null, "max-entities": null, "current-entities": null } } } ] } ``` ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/account/usage" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/account/usage", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/account/usage", params={}, headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/account/usage", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/account/usage")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/account/usage"); ``` ## Try it [#try-it] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Stream Events URL: https://sockodds.com/docs/endpoints/streamEvents/ # Stream Events [#stream-events] Setup streamed (WebSocket) connection `GET https://api.sockodds.com/v2/stream/events` Reserved. SockOdds does not stream: the source refreshes every ~2 minutes and this endpoint answers 501 so a client written for SportsGameOdds fails loudly instead of waiting on a socket that never opens. ## Query parameters [#query-parameters] | Parameter | Type | Description | | --- | --- | --- | | `feed` | `string` | The feed you would like to subscribe to (events:live, events:upcoming, events:byid) | | `leagueID` | `string` | A leagueID to stream events for | | `eventID` | `string` | An eventID to stream events for | ## Response [#response] `{ "success": true, "data": StreamEventsPusherResponse[], "nextCursor": string | null }` ### Status codes [#status-codes] | Status | Body | | --- | --- | | `501` | `{"success":false,"error":"Not Implemented"}` | ## Code samples [#code-samples] **cURL** · **JavaScript** · **Python** · **Go** · **Java** · **C#** ```bash curl -X GET "https://api.sockodds.com/v2/stream/events?feed=events:live" \ -H "x-api-key: YOUR_API_KEY" ``` ```javascript const r = await fetch("https://api.sockodds.com/v2/stream/events?feed=events:live", { headers: { "x-api-key": "YOUR_API_KEY" } }); const { success, data, nextCursor, notice } = await r.json(); ``` ```python import requests r = requests.get("https://api.sockodds.com/v2/stream/events", params=dict(feed="events:live"), headers={"x-api-key": "YOUR_API_KEY"}) body = r.json() ``` ```go req, _ := http.NewRequest("GET", "https://api.sockodds.com/v2/stream/events?feed=events:live", nil) req.Header.Set("x-api-key", "YOUR_API_KEY") res, err := http.DefaultClient.Do(req) ``` ```java HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.sockodds.com/v2/stream/events?feed=events:live")) .header("x-api-key", "YOUR_API_KEY").GET().build(); HttpResponse res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); ``` ```csharp using var http = new HttpClient(); http.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY"); var body = await http.GetStringAsync("https://api.sockodds.com/v2/stream/events?feed=events:live"); ``` > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # API Reference - Endpoints and Parameters URL: https://sockodds.com/docs/reference/ # API Reference - Endpoints and Parameters [#api-reference---endpoints-and-parameters] Base URL `https://api.sockodds.com/v2`. All responses use the envelope `{ success, data, nextCursor, notice }`; every endpoint except `/health` needs a key. Each endpoint page has the full parameter table, response schema, code samples in six languages and a live request builder. Machine-readable: [openapi.json](https://sockodds.com/openapi.json) · [openapi.yaml](https://sockodds.com/openapi.yaml) · [Postman](https://sockodds.com/SockOdds_Postman_Collection.json). | Endpoint | Summary | Query parameters | | --- | --- | --- | | [GET /events/](https://sockodds.com/docs/endpoints/getEvents/) | Get a list of Events | eventID, eventIDs, sportID, leagueID, type, oddsAvailable, oddsPresent, oddID, includeOpposingOdds, includeAltLines, expandResults, includeOpenCloseOdds, bookmakerID, teamID, playerID, finalized, live, started, ended, cancelled, startsAfter, startsBefore, includeFinished, limit, cursor | | [GET /odds/history/](https://sockodds.com/docs/endpoints/getOddsHistory/) | Get the tick-by-tick odds history of an Event | eventID, oddID, bookmakerID, from, to, limit, cursor | | [GET /teams/](https://sockodds.com/docs/endpoints/getTeams/) | Get a list of Teams by ID or league | sportID, leagueID, teamID, limit, cursor | | [GET /players/](https://sockodds.com/docs/endpoints/getPlayers/) | Get a list of Players for a specific Team or Event | teamID, eventID, playerID, leagueID, limit, cursor | | [GET /sports/](https://sockodds.com/docs/endpoints/getSports/) | Get a list of sports | — | | [GET /leagues/](https://sockodds.com/docs/endpoints/getLeagues/) | Get a list of Leagues | sportID, leagueID | | [GET /stats/](https://sockodds.com/docs/endpoints/getStats/) | Get a list of StatIDs | statID, sportID, statLevel | | [GET /markets/](https://sockodds.com/docs/endpoints/getMarkets/) | Get a list of Markets | oddID, sportID, leagueID, bookmakerID, statID, statEntityID, periodID, betTypeID, sideID, isMainMarket, isProp, isSubPeriod, propType, isSupported, limit, cursor | | [GET /account/usage](https://sockodds.com/docs/endpoints/getUsageData/) | Get rate-limits and usage data about your API key | — | | [GET /stream/events](https://sockodds.com/docs/endpoints/streamEvents/) | Setup streamed (WebSocket) connection | feed, leagueID, eventID | ## Authentication [#authentication] `x-api-key` header (case-insensitive) or `?apiKey=`. Missing or unknown → `401`; deactivated → `403`. ## Health [#health] ```json GET /health → {"status":"ok","timestamp":"…","version":"…","uptimeSeconds":123,"pipeline":"healthy|stale|empty"} ``` ## Schemas [#schemas] ### OddsHistoryTick [#oddshistorytick] One recorded change to one bookmaker's price on one market (SockOdds extension). Odds are given as American strings (bookOdds) and decimals. | Field | Type | Description | | --- | --- | --- | | `eventID` | `string` | The event | | `oddID` | `string` | The market side, in the oddID format (statID-statEntityID-periodID-betTypeID-sideID) | | `bookmakerID` | `string` | The bookmaker | | `changedAt` | `string (date-time)` | When the bookmaker's quote changed | | `kind` | `string` | open = first quote seen · move = price, line or main-line status changed · limit = only the bet limit changed (Pinnacle) · close = the quote was withdrawn | | `bookOdds` | `string \| null` | The new price as American odds; null on a close | | `decimalOdds` | `number \| null` | The new price as decimal odds | | `previousBookOdds` | `string \| null` | The price this change replaced (American); null on an open | | `previousDecimalOdds` | `number \| null` | The price this change replaced (decimal) | | `line` | `number \| null` | The spread or total the price applies to; null for moneylines and yes/no markets | | `isMainLine` | `boolean \| null` | Whether this line was the bookmaker's headline line at the time | | `layOdds` | `number \| null` | Exchange lay price, where the bookmaker is an exchange | | `maxStake` | `number \| null` | The bet limit after the change, where the bookmaker publishes one | | `previousMaxStake` | `number \| null` | The bet limit before the change | ### Event [#event] Contains basic information, consensus odds, results (stats & scores), team info, lineups and bookmaker links for a given event/game | Field | Type | Description | | --- | --- | --- | | `eventID` | `string` | Unique identifier for the event (e.g. afl_2026-09-12_brisbane_lions_vs_adelaide_crows) | | `sportID` | `string` | The sport (AUSSIE_RULES, RUGBY_LEAGUE, SOCCER, …) | | `leagueID` | `string` | The league (AFL, NRL, RUGBYLEAGUE_NRLW, EPL, …) — always the feed's own key, never constructed | | `type` | `string` | match, tournament or prop | | `status` | `object` | started, ended, live, cancelled, delayed, finalized, completed, startsAt, oddsPresent, oddsAvailable, currentPeriodID, periods{started,ended}, displayShort/displayLong | | `info` | `object` | displayName, commenceTime, lastUpdatedAt, stale (no source write for 45 min), bookmakersPresent, sourceBookmakerCount | | `teams` | `object` | home/away → { teamID, names{long,medium,short}, colors, statEntityID, score } | | `players` | `object` | playerID → { playerID, teamID, name, firstName, lastName } — the production SGO player ids | | `results` | `object` | Nested results in the format {periodID}.{statEntityID}.{statID} → number, where the source carries them | | `odds` | `object` | oddID → Odds object. Every market the event is priced on, all bookmakers nested under byBookmaker | | `links` | `object` | bookmakers{bookmakerID → event URL} and betslip{bookmakerID → bet-slip strategy} deeplinks, straight from each book's own event ids | ### Odds [#odds] One side of one market on one event, with every bookmaker's price nested inside | Field | Type | Description | | --- | --- | --- | | `oddID` | `string` | {statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID} | | `opposingOddID` | `string \| null` | The oddID of the other side (over ↔ under, home ↔ away); null for pick-N markets | | `marketName` | `string` | Human-readable market name, e.g. "Head to Head — Brisbane Lions" | | `statID` | `string` | The statistic being wagered on | | `statEntityID` | `string` | home, away, all, a playerID or a teamID | | `periodID` | `string` | game, 1h, 1q, 1i, 1ix5, 1s, … | | `betTypeID` | `string` | ml, sp, ou, ml3way, yn, eo, pick, … | | `sideID` | `string` | home, away, over, under, yes, no, draw, … | | `playerID` | `string` | Set when the market is a player prop | | `bookOdds` | `string` | Consensus American price across the books pricing this side, e.g. "-108" (the vig left in) | | `bookOddsAvailable` | `boolean` | Whether at least one bookmaker has this side open for wagering | | `bookSpread / bookOverUnder` | `string \| null` | Consensus line for spread and total markets | | `fairOdds` | `string \| null` | The de-vigged fair price derived from exchange and sharp-book consensus; null on plans without fair odds | | `fairOddsAvailable` | `boolean` | Whether enough exchange/sharp data existed to compute fairOdds | | `fairSpread / fairOverUnder` | `string \| null` | The fair line for spread and total markets | | `started / ended / cancelled` | `boolean` | Whether the period this market settles on has started, ended or been cancelled | | `scoringSupported` | `boolean` | Whether the source can grade this market from results | | `byBookmaker` | `object` | bookmakerID → ByBookmakerOdds | | `altLines` | `object` | Alternate lines keyed by line, each with its own byBookmaker (only with includeAltLines=true) | ### ByBookmakerOdds [#bybookmakerodds] One bookmaker's price for one side of one market | Field | Type | Description | | --- | --- | --- | | `bookmakerID` | `string` | The bookmaker (sportsbet, tab, pinnacle, betfairexchange, …) | | `odds` | `string` | American price, e.g. "-108" or "+240" | | `decimal` | `number` | The same price as decimal odds, e.g. 1.93 — Australian books quote decimal | | `overUnder / spread` | `string` | This book's line where the market has one | | `available` | `boolean` | Whether the book has the side open right now (a suspended market is listed with available: false) | | `lastUpdatedAt` | `date-time` | When this book's price was last observed | | `eventName` | `string` | The book's own name for the event | | `deeplink` | `string` | Direct URL to the market on the book where one is known (also see Event.links) | ### Team [#team] Contains information on a team | Field | Type | Description | | --- | --- | --- | | `teamID` | `string` | e.g. BRISBANE_LIONS_AFL — defined per league | | `sportID` | `string` | | | `leagueID` | `string` | | | `names` | `object` | { long, medium, short } | | `colors` | `object` | { primary, secondary, primaryContrast, secondaryContrast } where known | | `statEntityID` | `string` | home or away on the event the team was read from | ### Player [#player] Contains information on a player | Field | Type | Description | | --- | --- | --- | | `playerID` | `string` | e.g. SONNY_GRAY_1_MLB — the production SportsGameOdds id format, unique across teams in a league | | `teamID` | `string` | | | `sportID` | `string` | | | `leagueID` | `string` | | | `name / firstName / lastName` | `string` | | | `position / jerseyNumber` | `string \| number` | Where the source carries them | ### Sport [#sport] Contains information on a sport | Field | Type | Description | | --- | --- | --- | | `sportID` | `string` | | | `name / shortName` | `string` | | | `enabled` | `boolean` | Whether your plan can read this sport | | `activeEvents` | `number` | Events currently carried | ### League [#league] Contains information on a specific league | Field | Type | Description | | --- | --- | --- | | `leagueID` | `string` | | | `sportID` | `string` | | | `name / shortName` | `string` | | | `enabled` | `boolean` | Whether your plan can read this league | | `activeEvents` | `number` | Events currently carried | ### Stat [#stat] Contains information on a statistic (statID) | Field | Type | Description | | --- | --- | --- | | `statID` | `string` | | | `supportedLevels` | `object` | { all, team, player } — which statEntityID classes it is priced at | | `displays` | `object` | { short, long } | | `isScoreStat` | `boolean` | True for `points`, the stat that decides the event | | `supportedSports` | `object` | sportID → true | ### Market [#market] Contains information about a betting market, including its classification and support data across leagues and bookmakers | Field | Type | Description | | --- | --- | --- | | `oddID` | `string` | The unique identifier for this market | | `statID / statEntityID / periodID / betTypeID / sideID` | `string` | The decomposed grammar | | `playerID / teamID` | `string` | Set for player props and tournament team markets | | `marketGroupID / marketGroupName` | `string` | The group (all sides of the market) this market belongs to, and its display name | | `isMainMarket` | `boolean` | True for the full-game moneyline, spread and total | | `isMainDerivative` | `boolean` | True for a sub-period of a main market | | `isSubPeriod` | `boolean` | True when the market tracks a non-main period | | `isProp` | `boolean` | True if this is a prop bet | | `propType` | `string` | game_prop, team_prop, player_prop or other_prop | | `isSupported` | `boolean` | True if at least one bookmaker prices it in at least one league | | `activeEvents` | `number` | Unique events with this market priced right now | | `support` | `object` | leagueID → { bookmakerID → true } | | `bookmakers` | `string[]` | SockOdds extension: the flat list of bookmakers pricing it | ### AccountUsage [#accountusage] Your API key's tier, rate limits and current usage | Field | Type | Description | | --- | --- | --- | | `keyID` | `string` | The hashed identifier for the API key (the first 16 hex of sha256) | | `customerID` | `string \| null` | Billing customer id where one exists | | `email` | `string \| null` | | | `isActive` | `boolean` | | | `tier` | `string` | lite, base or internal | | `rateLimits` | `object` | per-second / per-minute / per-hour / per-day / per-month → RateLimitInterval | ### RateLimitInterval [#ratelimitinterval] One rate-limit window | Field | Type | Description | | --- | --- | --- | | `max-requests` | `integer \| null` | Maximum allowed requests in the interval (null = unlimited) | | `current-requests` | `integer` | Requests made so far in the interval | | `max-entities` | `integer \| null` | Maximum objects in the interval (null = unlimited) | | `current-entities` | `integer \| null` | Objects served so far in the interval | > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Data Explorer - Browse API Data Schema URL: https://sockodds.com/docs/explorer/ # Data Explorer - Browse API Data Schema [#data-explorer---browse-api-data-schema] Browse the objects the API returns and their fields, then run a real request against the live feed with your key. Nothing is stored. ## OddsHistoryTick [#oddshistorytick] One recorded change to one bookmaker's price on one market (SockOdds extension). Odds are given as American strings (bookOdds) and decimals. | Field | Type | Description | | --- | --- | --- | | `eventID` | `string` | The event | | `oddID` | `string` | The market side, in the oddID format (statID-statEntityID-periodID-betTypeID-sideID) | | `bookmakerID` | `string` | The bookmaker | | `changedAt` | `string (date-time)` | When the bookmaker's quote changed | | `kind` | `string` | open = first quote seen · move = price, line or main-line status changed · limit = only the bet limit changed (Pinnacle) · close = the quote was withdrawn | | `bookOdds` | `string \| null` | The new price as American odds; null on a close | | `decimalOdds` | `number \| null` | The new price as decimal odds | | `previousBookOdds` | `string \| null` | The price this change replaced (American); null on an open | | `previousDecimalOdds` | `number \| null` | The price this change replaced (decimal) | | `line` | `number \| null` | The spread or total the price applies to; null for moneylines and yes/no markets | | `isMainLine` | `boolean \| null` | Whether this line was the bookmaker's headline line at the time | | `layOdds` | `number \| null` | Exchange lay price, where the bookmaker is an exchange | | `maxStake` | `number \| null` | The bet limit after the change, where the bookmaker publishes one | | `previousMaxStake` | `number \| null` | The bet limit before the change | ## Event [#event] Contains basic information, consensus odds, results (stats & scores), team info, lineups and bookmaker links for a given event/game | Field | Type | Description | | --- | --- | --- | | `eventID` | `string` | Unique identifier for the event (e.g. afl_2026-09-12_brisbane_lions_vs_adelaide_crows) | | `sportID` | `string` | The sport (AUSSIE_RULES, RUGBY_LEAGUE, SOCCER, …) | | `leagueID` | `string` | The league (AFL, NRL, RUGBYLEAGUE_NRLW, EPL, …) — always the feed's own key, never constructed | | `type` | `string` | match, tournament or prop | | `status` | `object` | started, ended, live, cancelled, delayed, finalized, completed, startsAt, oddsPresent, oddsAvailable, currentPeriodID, periods{started,ended}, displayShort/displayLong | | `info` | `object` | displayName, commenceTime, lastUpdatedAt, stale (no source write for 45 min), bookmakersPresent, sourceBookmakerCount | | `teams` | `object` | home/away → { teamID, names{long,medium,short}, colors, statEntityID, score } | | `players` | `object` | playerID → { playerID, teamID, name, firstName, lastName } — the production SGO player ids | | `results` | `object` | Nested results in the format {periodID}.{statEntityID}.{statID} → number, where the source carries them | | `odds` | `object` | oddID → Odds object. Every market the event is priced on, all bookmakers nested under byBookmaker | | `links` | `object` | bookmakers{bookmakerID → event URL} and betslip{bookmakerID → bet-slip strategy} deeplinks, straight from each book's own event ids | ## Odds [#odds] One side of one market on one event, with every bookmaker's price nested inside | Field | Type | Description | | --- | --- | --- | | `oddID` | `string` | {statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID} | | `opposingOddID` | `string \| null` | The oddID of the other side (over ↔ under, home ↔ away); null for pick-N markets | | `marketName` | `string` | Human-readable market name, e.g. "Head to Head — Brisbane Lions" | | `statID` | `string` | The statistic being wagered on | | `statEntityID` | `string` | home, away, all, a playerID or a teamID | | `periodID` | `string` | game, 1h, 1q, 1i, 1ix5, 1s, … | | `betTypeID` | `string` | ml, sp, ou, ml3way, yn, eo, pick, … | | `sideID` | `string` | home, away, over, under, yes, no, draw, … | | `playerID` | `string` | Set when the market is a player prop | | `bookOdds` | `string` | Consensus American price across the books pricing this side, e.g. "-108" (the vig left in) | | `bookOddsAvailable` | `boolean` | Whether at least one bookmaker has this side open for wagering | | `bookSpread / bookOverUnder` | `string \| null` | Consensus line for spread and total markets | | `fairOdds` | `string \| null` | The de-vigged fair price derived from exchange and sharp-book consensus; null on plans without fair odds | | `fairOddsAvailable` | `boolean` | Whether enough exchange/sharp data existed to compute fairOdds | | `fairSpread / fairOverUnder` | `string \| null` | The fair line for spread and total markets | | `started / ended / cancelled` | `boolean` | Whether the period this market settles on has started, ended or been cancelled | | `scoringSupported` | `boolean` | Whether the source can grade this market from results | | `byBookmaker` | `object` | bookmakerID → ByBookmakerOdds | | `altLines` | `object` | Alternate lines keyed by line, each with its own byBookmaker (only with includeAltLines=true) | ## ByBookmakerOdds [#bybookmakerodds] One bookmaker's price for one side of one market | Field | Type | Description | | --- | --- | --- | | `bookmakerID` | `string` | The bookmaker (sportsbet, tab, pinnacle, betfairexchange, …) | | `odds` | `string` | American price, e.g. "-108" or "+240" | | `decimal` | `number` | The same price as decimal odds, e.g. 1.93 — Australian books quote decimal | | `overUnder / spread` | `string` | This book's line where the market has one | | `available` | `boolean` | Whether the book has the side open right now (a suspended market is listed with available: false) | | `lastUpdatedAt` | `date-time` | When this book's price was last observed | | `eventName` | `string` | The book's own name for the event | | `deeplink` | `string` | Direct URL to the market on the book where one is known (also see Event.links) | ## Team [#team] Contains information on a team | Field | Type | Description | | --- | --- | --- | | `teamID` | `string` | e.g. BRISBANE_LIONS_AFL — defined per league | | `sportID` | `string` | | | `leagueID` | `string` | | | `names` | `object` | { long, medium, short } | | `colors` | `object` | { primary, secondary, primaryContrast, secondaryContrast } where known | | `statEntityID` | `string` | home or away on the event the team was read from | ## Player [#player] Contains information on a player | Field | Type | Description | | --- | --- | --- | | `playerID` | `string` | e.g. SONNY_GRAY_1_MLB — the production SportsGameOdds id format, unique across teams in a league | | `teamID` | `string` | | | `sportID` | `string` | | | `leagueID` | `string` | | | `name / firstName / lastName` | `string` | | | `position / jerseyNumber` | `string \| number` | Where the source carries them | ## Sport [#sport] Contains information on a sport | Field | Type | Description | | --- | --- | --- | | `sportID` | `string` | | | `name / shortName` | `string` | | | `enabled` | `boolean` | Whether your plan can read this sport | | `activeEvents` | `number` | Events currently carried | ## League [#league] Contains information on a specific league | Field | Type | Description | | --- | --- | --- | | `leagueID` | `string` | | | `sportID` | `string` | | | `name / shortName` | `string` | | | `enabled` | `boolean` | Whether your plan can read this league | | `activeEvents` | `number` | Events currently carried | ## Stat [#stat] Contains information on a statistic (statID) | Field | Type | Description | | --- | --- | --- | | `statID` | `string` | | | `supportedLevels` | `object` | { all, team, player } — which statEntityID classes it is priced at | | `displays` | `object` | { short, long } | | `isScoreStat` | `boolean` | True for `points`, the stat that decides the event | | `supportedSports` | `object` | sportID → true | ## Market [#market] Contains information about a betting market, including its classification and support data across leagues and bookmakers | Field | Type | Description | | --- | --- | --- | | `oddID` | `string` | The unique identifier for this market | | `statID / statEntityID / periodID / betTypeID / sideID` | `string` | The decomposed grammar | | `playerID / teamID` | `string` | Set for player props and tournament team markets | | `marketGroupID / marketGroupName` | `string` | The group (all sides of the market) this market belongs to, and its display name | | `isMainMarket` | `boolean` | True for the full-game moneyline, spread and total | | `isMainDerivative` | `boolean` | True for a sub-period of a main market | | `isSubPeriod` | `boolean` | True when the market tracks a non-main period | | `isProp` | `boolean` | True if this is a prop bet | | `propType` | `string` | game_prop, team_prop, player_prop or other_prop | | `isSupported` | `boolean` | True if at least one bookmaker prices it in at least one league | | `activeEvents` | `number` | Unique events with this market priced right now | | `support` | `object` | leagueID → { bookmakerID → true } | | `bookmakers` | `string[]` | SockOdds extension: the flat list of bookmakers pricing it | ## AccountUsage [#accountusage] Your API key's tier, rate limits and current usage | Field | Type | Description | | --- | --- | --- | | `keyID` | `string` | The hashed identifier for the API key (the first 16 hex of sha256) | | `customerID` | `string \| null` | Billing customer id where one exists | | `email` | `string \| null` | | | `isActive` | `boolean` | | | `tier` | `string` | lite, base or internal | | `rateLimits` | `object` | per-second / per-minute / per-hour / per-day / per-month → RateLimitInterval | ## RateLimitInterval [#ratelimitinterval] One rate-limit window | Field | Type | Description | | --- | --- | --- | | `max-requests` | `integer \| null` | Maximum allowed requests in the interval (null = unlimited) | | `current-requests` | `integer` | Requests made so far in the interval | | `max-entities` | `integer \| null` | Maximum objects in the interval (null = unlimited) | | `current-entities` | `integer \| null` | Objects served so far in the interval | ## Run a live query [#run-a-live-query] > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # AI-Assisted Development - MCP Server and Context URL: https://sockodds.com/docs/info/ai-vibe-coding/ # AI-Assisted Development - MCP Server and Context [#ai-assisted-development---mcp-server-and-context] AI tools speed up development when they have proper context and hallucinate when they don't. The resources below give them the real SockOdds API. ## Copy-paste context [#copy-paste-context] **We highly recommend you paste the [AI context](#ai-context) below into your prompt or add it to a rules file.** ## AI-friendly docs [#ai-friendly-docs] ### Indexed documentation [#indexed-documentation] [View llms.txt](https://sockodds.com/llms.txt) — URLs and descriptions of each documentation page. AI tools can use this to identify which page to fetch. Length: ~6k tokens ### Full documentation [#full-documentation] [View docs/llms-full.txt](https://sockodds.com/docs/llms-full.txt) — the entire documentation in a single file, for tools with a large context window. [llms-full.txt](https://sockodds.com/llms-full.txt) is the full site knowledge payload (sports, leagues, bookmakers, use cases, FAQ, glossary). Length: ~65k tokens (docs) · ~10k tokens (site payload) ### OpenAPI specification [#openapi-specification] [View OpenAPI spec](https://sockodds.com/openapi.json) — a machine-readable definition with exact schemas for every endpoint. Every page advertises it via ``, so agents can find it without being told the URL. Length: ~20k tokens ### Markdown versions of any page [#markdown-versions-of-any-page] Every page has a Markdown representation — no HTML, no nav. Request it either way: ```bash # Content negotiation curl -H "Accept: text/markdown" https://sockodds.com/docs/basics/quickstart # Or append .md / .mdx to the URL curl https://sockodds.com/docs/basics/quickstart.mdx curl https://sockodds.com/leagues/afl-odds-api.md ``` ## MCP server [#mcp-server] Because SockOdds serves the SportsGameOdds v2 schema, the official [sports-odds-api-mcp](https://www.npmjs.com/package/sports-odds-api-mcp) server works against it: set `SPORTS_GAME_ODDS_BASE_URL` to `https://api.sockodds.com/v2`. Version 2.1.0 exposes two tools: - **`execute`** — runs TypeScript against the SportsGameOdds SDK client (events, odds, teams, players, leagues, markets, usage). It executes in a Deno sandbox on your machine, so [Deno](https://deno.land) must be installed. - **`search_docs`** — searches the SDK method reference bundled with the server. Replace `your-api-key-here` with your key. > Verified against `sports-odds-api-mcp@2.1.0` on 7 September 2026: the SDK client reads `SPORTS_GAME_ODDS_BASE_URL` (not `SPORTS_ODDS_API_BASE_URL`), and the server passes that base URL into its Deno worker and whitelists only that host for network access. On Deno 2.9 the worker fails to start (`NotCapable: requires net access to unix:…deno-http.sock`) — against SportsGameOdds' own default URL as well as ours, so it is a bug in the server, not in the override. Until it is fixed, drive the API from an SDK or paste the AI context below. ### Claude Code [#claude-code] **command** · **json** ```bash claude mcp add sockodds --env SPORTS_ODDS_API_KEY_HEADER="your-api-key-here" --env SPORTS_GAME_ODDS_BASE_URL="https://api.sockodds.com/v2" -- npx -y sports-odds-api-mcp ``` ```json { "mcpServers": { "sockodds": { "command": "npx", "args": [ "-y", "sports-odds-api-mcp@latest" ], "env": { "SPORTS_ODDS_API_KEY_HEADER": "your-api-key-here", "SPORTS_GAME_ODDS_BASE_URL": "https://api.sockodds.com/v2" } } } } ``` ### Cursor [#cursor] **json** ```json { "mcpServers": { "sockodds": { "command": "npx", "args": [ "-y", "sports-odds-api-mcp@latest" ], "env": { "SPORTS_ODDS_API_KEY_HEADER": "your-api-key-here", "SPORTS_GAME_ODDS_BASE_URL": "https://api.sockodds.com/v2" } } } } ``` ### VS Code [#vs-code] **command** · **json** ```bash code --add-mcp '{"name":"sockodds","command":"npx","args":["-y","sports-odds-api-mcp@latest"],"env":{"SPORTS_ODDS_API_KEY_HEADER":"your-api-key-here","SPORTS_GAME_ODDS_BASE_URL":"https://api.sockodds.com/v2"}}' ``` ```json { "name": "sockodds", "command": "npx", "args": [ "-y", "sports-odds-api-mcp@latest" ], "env": { "SPORTS_ODDS_API_KEY_HEADER": "your-api-key-here", "SPORTS_GAME_ODDS_BASE_URL": "https://api.sockodds.com/v2" } } ``` ## AI context [#ai-context] Paste the following into your prompt or rules file: ```md # SockOdds API Reference Australian sports odds data in the SportsGameOdds v2 schema. ## Primary documentation resources [#primary-documentation-resources] If your environment can fetch URLs, use these as sources of truth. **Do not guess or make up information.** | Resource | URL | Use case | | --- | --- | --- | | Documentation index (~6k tokens) | https://sockodds.com/llms.txt | Overview of every documentation page with descriptions | | Full documentation (~65k tokens) | https://sockodds.com/docs/llms-full.txt | Detailed explanations of fields, parameters and examples | | OpenAPI specification (~20k tokens) | https://sockodds.com/openapi.json | Exact request/response schemas and parameter definitions | | Any page as Markdown | append .md to a page URL, or send Accept: text/markdown | Clean prose without HTML — e.g. https://sockodds.com/leagues/afl-odds-api.md | > Note: if you cannot fetch URLs, ask the user to paste the relevant resource rather than guessing. ## Authentication [#authentication] - API key required: https://sockodds.com/signup (free tier, shown once). Never invent an API key. - Header: x-api-key: API_KEY or query: ?apiKey=API_KEY ## Base URL [#base-url] https://api.sockodds.com/v2 (same paths as api.sportsgameodds.com/v2) ## Response format [#response-format] All responses are JSON: { "success": true, "data": [...], "nextCursor": string|null, "notice": string? } Errors: { "success": false, "error": "..." } with 400/401/403/429/500. ## Events endpoint (most common) [#events-endpoint-most-common] GET https://api.sockodds.com/v2/events | Parameter | Example | Description | | --- | --- | --- | | oddsAvailable | true | only events with open markets | | leagueID | AFL,NRL | comma-separated leagues (AFL, AFLW, NRL, RUGBYLEAGUE_NRLW, IPL, EPL, NFL, NBA, MLB, ...) | | oddID | points-home-game-ml-home | comma-separated markets; PLAYER_ID matches every player | | includeOpposingOdds | true | also return the other side | | bookmakerID | sportsbet,tab | limit bookmakers | | includeAltLines | true | alternate lines (large) | | cursor / limit | | pagination (limit default 10, max 100) | ### Event object key fields [#event-object-key-fields] eventID, sportID, leagueID, type, teams.home.teamID, teams.away.teamID, status.startsAt, status.started, status.ended, status.finalized, status.live, info.stale, info.lastUpdatedAt, players., odds., links.bookmakers, links.betslip ## oddID format [#oddid-format] {statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID} points-home-game-ml-home (head to head, home) · points-all-game-ou-over (total over) · disposals-LACHIE_NEALE_1_AFL-game-ou-over (player prop) ## Bookmaker odds structure [#bookmaker-odds-structure] odds..byBookmaker.: { odds: "-108" (American string), decimal: 1.93, available: bool, overUnder|spread: string?, lastUpdatedAt } bookmakerIDs: sportsbet, unibet, tab, playup, tabtouch, betright, neds, picklebet, pointsbet, betdeluxe, betfairexchange, kalshi, polymarket, prophetexchange ## Consensus [#consensus] odds..bookOdds (with vig) and fairOdds (de-vigged, Base plan and above; null with fairOddsAvailable=false when not computable) ## Not offered [#not-offered] No WebSocket streaming (/v2/stream/events is 501). No open/close snapshots. Poll every 2-5 minutes; cache by info.lastUpdatedAt. ``` > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Best Practices and Common Mistakes URL: https://sockodds.com/docs/info/best-practices/ # Best Practices and Common Mistakes [#best-practices-and-common-mistakes] Recommended patterns and mistakes to avoid when working with the SockOdds API. ## Patterns to follow [#patterns-to-follow] ### Set up a server-side process to sync data from the API into your database [#set-up-a-server-side-process-to-sync-data-from-the-api-into-your-database] - The most secure and scalable way to ingest API data — a cron job every 2–5 minutes handles it. - You control how up-to-date your data is, and 10× more app traffic doesn't mean 10× more API calls. ### Calculate your expected usage [#calculate-your-expected-usage] - How many leagues you track, how often you refresh, and how many events per league at a time. - Only events with open markets? Then `oddsAvailable=true` and count those. ### Specify oddID when you only need specific markets [#specify-oddid-when-you-only-need-specific-markets] - Cuts payloads by up to ~100× and improves response times. ### Implement retry logic on 500-level errors [#implement-retry-logic-on-500-level-errors] - Wait a short period, retry once. If it still fails, stop and log. ### Use query params to filter data [#use-query-params-to-filter-data] - Filtering at the API level means less irrelevant data returned, faster responses, fewer objects. ### Use eventID by itself for direct lookups [#use-eventid-by-itself-for-direct-lookups] - With `eventID` or `eventIDs` all other filters are ignored; response-shaping params (`oddID`, `bookmakerID`, `includeAltLines`) still apply. ### Use limit and cursor together [#use-limit-and-cursor-together] - Raise `limit` before you start paging. ### Keep your API key secure [#keep-your-api-key-secure] - Never in frontend code. Never in version control. ### Monitor your usage [#monitor-your-usage] - `/account/usage` shows where you stand against your limits. ### Handle missing fields defensively [#handle-missing-fields-defensively] - Critical fields (`eventID`, `sportID`, `leagueID`) are always present; less critical ones (`colors`, `results`, `players`) may be empty. Use `names.long || names.medium || names.short`. ### Read the honesty flags [#read-the-honesty-flags] - `available`, `info.stale` and `notice` tell you what the feed knows and what your plan withheld. Treat them as data. ### Vary your polling intervals [#vary-your-polling-intervals] - Odds far from kick-off change slowly and many markets aren't offered until 24–48 hours out. Poll less for the far future. ### Learn the oddID structure [#learn-the-oddid-structure] - `{statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID}`. Knowing it makes everything faster. ### Look league keys up, never construct them [#look-league-keys-up-never-construct-them] - `RUGBYLEAGUE_NRLW`, `FR_LIGUE_1`, `UCL` — from `/leagues`. ## Anti-patterns to avoid [#anti-patterns-to-avoid] ### Making API requests from a frontend/browser [#making-api-requests-from-a-frontendbrowser] - Exposes your key. Proxy through a backend, or sync to your database and query that. ### Polling on an interval on the free plan [#polling-on-an-interval-on-the-free-plan] - 10 requests a minute goes quickly; run requests manually and upgrade when you're ready to schedule. ### Polling too frequently [#polling-too-frequently] - The source refreshes every ~2 minutes; faster polling wastes quota. ### Always polling all upcoming games [#always-polling-all-upcoming-games] - Cache longer for far-off games. ### Not considering response codes [#not-considering-response-codes] - A 429 carries `Retry-After`; not waiting burns quota faster. ### Not considering error messages [#not-considering-error-messages] - Every error has `success: false` and an `error` field. Log it. ### Always including includeAltLines=true [#always-including-includealtlinestrue] - Multiplies the payload; ask only when needed. ### Using startsAfter/startsBefore when unneeded [#using-startsafterstartsbefore-when-unneeded] - They widen the scan; combine with as few other filters as possible. ### Filtering results on the client [#filtering-results-on-the-client] - Filter at the API with query parameters instead of downloading everything. ### Using wrong/invalid query parameters [#using-wronginvalid-query-parameters] - See the [reference](https://sockodds.com/docs/reference/) for valid parameters per endpoint. ### Acting on a stale or suspended price [#acting-on-a-stale-or-suspended-price] - Verify on the book via `links.bookmakers` before staking; prices move. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Sports Betting Glossary - Terms and Definitions URL: https://sockodds.com/docs/info/glossary/ # Sports Betting Glossary - Terms and Definitions [#sports-betting-glossary---terms-and-definitions] A guide to betting terminology and how it relates to the SockOdds API. ## Alternate lines (alt lines) [#alternate-lines-alt-lines] Non-standard lines or totals at different prices. In the API, `altLines` keyed by line with `includeAltLines=true`. ## American odds [#american-odds] The `odds` string format: negative (−110) is how much to risk to win 100; positive (+150) is what 100 wins. ## API key [#api-key] The authentication token, passed as `x-api-key` or `apiKey`. ## Arbitrage (arb) [#arbitrage-arb] Betting all outcomes at different bookmakers to guarantee profit from odds discrepancies. See the [arbitrage calculator](https://sockodds.com/docs/examples/arbitrage-calculator/). ## ATS [#ats] Against the spread — a team to cover the line. ## Bad beat [#bad-beat] Losing a bet in an unlikely way after appearing to have won. ## betTypeID [#bettypeid] The type of bet: `ml`, `sp`, `ou`, `yn`, `eo`, `pick`, `topbat`, … See [bet types](https://sockodds.com/docs/data-types/bet-types/). ## Book odds [#book-odds] Consensus across bookmakers including juice: `bookOdds`, `bookSpread`, `bookOverUnder`. ## Bookmaker ID [#bookmaker-id] The identifier for each book (`sportsbet`, `tab`, `pinnacle`). See [bookmakers](https://sockodds.com/docs/data-types/bookmakers/). ## BTTS [#btts] Both teams to score — soccer. ## Closing line [#closing-line] The final odds before an event starts. Not snapshotted by the feed; the last `lastUpdatedAt` price before `startsAt` is the nearest thing. ## CLV [#clv] Closing line value — the difference between the odds you got and the closing line. ## Consensus line [#consensus-line] The median price across bookmakers. See [consensus odds](https://sockodds.com/docs/info/consensus-odds/). ## Cover [#cover] Beating the line. ## Cursor [#cursor] A pagination token (`nextCursor`). See [data batches](https://sockodds.com/docs/guides/data-batches/). ## Decimal odds [#decimal-odds] Total return per $1 staked including stake — what Australian books quote. Every bookmaker entry carries `decimal`. ## DNB [#dnb] Draw no bet. ## Deeplink [#deeplink] A URL to the event or bet on the bookmaker: `links.bookmakers`, `links.betslip`. ## EV [#ev] Expected value — the average return per bet over the long term. ## Fair odds [#fair-odds] Consensus with the vig removed: `fairOdds`, `fairSpread`, `fairOverUnder`. ## Favourite [#favourite] The side expected to win (negative American odds). ## Fractional odds [#fractional-odds] UK format, profit relative to stake (10/11). ## Futures [#futures] Season-long markets; `periodID=season` where carried. ## Handle [#handle] Total money wagered on a market. ## Head to head (H2H) [#head-to-head-h2h] Australian term for the moneyline: `betTypeID=ml`. ## Hedge [#hedge] Betting the opposite side of an existing bet. ## Hook [#hook] The half-point in a line (−7.5). ## Implied probability [#implied-probability] `1 / decimal`. Both sides sum above 100% because of the vig. ## Juice / vig [#juice-vig] The bookmaker's margin. Both sides at 1.91 imply ~104.7%. ## Lane [#lane] A separately priced product of a bookmaker (`dabble__pickem`, `mintbet__sgm_only`). ## League ID [#league-id] The league identifier (`AFL`, `NRL`, `RUGBYLEAGUE_NRLW`). See [leagues](https://sockodds.com/docs/data-types/leagues/). ## Line [#line] Australian term for the spread: `betTypeID=sp`. ## Line movement [#line-movement] Changes to odds or lines over time, visible through `lastUpdatedAt` and retained events. ## Live betting (in-play) [#live-betting-in-play] Bets after an event starts. Filter with `live=true`; prices refresh every ~2 minutes. ## Middle [#middle] Both sides at different lines so both can win. ## Multi [#multi] Australian term for a parlay. ## Moneyline [#moneyline] A bet on the winner; `betTypeID=ml`. ## Notice [#notice] The envelope field present when your plan filtered a response. ## Object [#object] The pricing unit: one top-level item in a response (an event, a league, a team). Not per market, not per bookmaker. ## oddID [#oddid] `{statID}-{statEntityID}-{periodID}-{betTypeID}-{sideID}`. See [odds](https://sockodds.com/docs/data-types/odds/). ## Opening line [#opening-line] The first price posted. Not snapshotted by the feed. ## O/U [#ou] Over/under (total): `betTypeID=ou`. ## Parlay [#parlay] Multiple selections in one bet; all must win. See [the parlay builder](https://sockodds.com/docs/examples/parlay-builder/). ## Period ID [#period-id] `game`, `1h`, `1q`, `1i`, `reg`. See [periods](https://sockodds.com/docs/data-types/periods/). ## Pick'em [#pickem] DFS-style over/under on a player stat — Dabble's pick'em lane. ## Positive EV [#positive-ev] A bet with positive expected return: a book's implied probability below fair. ## Props [#props] Bets on specific outcomes within a game; player props have a playerID in the entity slot. ## Push [#push] A result exactly on the line; stake returned. ## ROI [#roi] Return on investment. ## SGM [#sgm] Same-game multi — correlated legs from one event; priced separately in the SGM lanes. ## Sharp money [#sharp-money] Bets from professionals; sharp books (Pinnacle) and exchanges anchor `fairOdds`. ## sideID [#sideid] The side of a bet: `home`, `away`, `over`, `under`, `yes`, `no`, `draw`. ## Stale [#stale] `info.stale: true` — no source write for 45 minutes; prices are last-known. ## Stat entity ID [#stat-entity-id] Whose stat: `home`, `away`, `all`, or a playerID. See [stat entities](https://sockodds.com/docs/data-types/stat-entity/). ## Stat ID [#stat-id] The statistic (`points`, `disposals`, `tries`). See [stats](https://sockodds.com/docs/data-types/stats/). ## Steam move [#steam-move] Sudden movement across many books at once. ## Total [#total] See O/U. ## Underdog [#underdog] The side expected to lose (positive American odds). > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Error Codes and Troubleshooting URL: https://sockodds.com/docs/info/errors/ # Error Codes and Troubleshooting [#error-codes-and-troubleshooting] All API errors return an applicable HTTP status code along with this JSON body: ```json { "success": false, "error": "Human-readable error description" } ``` ## TLDR [#tldr] | Code | Meaning | Your action | | --- | --- | --- | | 200 | Success | Use the data | | 400 | Bad Request | Fix parameters — check the error text | | 401 | Unauthorized | Check API key header/parameter | | 403 | Forbidden | The key is deactivated — contact support | | 404 | Not Found | Verify the endpoint path | | 429 | Rate Limited | Wait for `Retry-After`, then retry; upgrade if persistent | | 500 | Server Error | Retry once after a delay | | 501 | Not Implemented | `/stream/events` — poll instead | | 503 | Service Unavailable | Wait and retry once | ## Types of errors [#types-of-errors] ### Non-standard response format [#non-standard-response-format] An empty or non-JSON body means a transient network or server issue. Treat it as a 500: retry once after a short delay; contact support if it repeats. ### 400 Bad Request [#400-bad-request] **Meaning:** invalid or missing parameters. Check the [reference](https://sockodds.com/docs/reference/); booleans must be `true`/`false`; dates ISO-8601; don't repeat a parameter; some combinations (`live=true` with `ended=true`) can't go together. ### 401 Unauthorized [#401-unauthorized] **Meaning:** authentication failed or the key is missing. Send it as `x-api-key` (case-insensitive) or `apiKey`; check for whitespace; make sure you copied the key you were shown. ### 403 Forbidden [#403-forbidden] **Meaning:** the key exists but is deactivated. Email support with the `keyID`. ### 404 Not Found [#404-not-found] **Meaning:** the path doesn't exist. Make sure it starts with `/v2/`. ### 429 Too Many Requests [#429-too-many-requests] **Meaning:** you exceeded your per-minute limit. Wait for `Retry-After` seconds; check `/account/usage`. A rejected call is counted for the minute but never billed. ### 500 Internal Server Error [#500-internal-server-error] **Meaning:** something went wrong on our end. Retry once after a few seconds; if it persists contact support. Please don't retry in a tight loop. ### 501 Not Implemented [#501-not-implemented] **Meaning:** you called `/v2/stream/events`. SockOdds does not stream; see the [polling guide](https://sockodds.com/docs/guides/realtime-streaming-api/). ### 503 Service Unavailable [#503-service-unavailable] **Meaning:** temporarily unavailable. Wait and retry once. ## Error handling example [#error-handling-example] **JavaScript** · **Python** · **Java** · **Ruby** · **PHP** ```javascript async function fetchSockOdds(url, options = {}, canRetry = true) { let response, payload, error; try { response = await fetch(url, options); payload = await response.json(); } catch (e) { error = e; } if (payload?.success === true) return payload.data; const status = response?.status; if (status === 429 && canRetry) { await new Promise((r) => setTimeout(r, 1000 * Number(response.headers.get("retry-after") || 5))); return fetchSockOdds(url, options, false); } const isClientError = payload?.success === false && status >= 400 && status < 500; if (canRetry && !isClientError) { await new Promise((r) => setTimeout(r, 2000 + Math.random() * 3000)); return fetchSockOdds(url, options, false); } console.error(`SockOdds request failed: ${status} ${payload?.error || error?.message}`); return null; } ``` ```python import random, time, requests def fetch_sirenodds(url, headers, can_retry=True): try: r = requests.get(url, headers=headers); payload = r.json() except Exception as e: r, payload = None, None if payload and payload.get("success") is True: return payload["data"] status = r.status_code if r is not None else None if status == 429 and can_retry: time.sleep(int(r.headers.get("Retry-After", "5"))); return fetch_sirenodds(url, headers, False) client_error = payload and payload.get("success") is False and status and 400 <= status < 500 if can_retry and not client_error: time.sleep(2 + random.random() * 3); return fetch_sirenodds(url, headers, False) print("SockOdds request failed:", status, (payload or {}).get("error")) return None ``` ```java static JsonElement fetchSockOdds(String url, String key, boolean canRetry) throws Exception { var req = HttpRequest.newBuilder().uri(URI.create(url)).header("x-api-key", key).GET().build(); var res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()); var payload = JsonParser.parseString(res.body()).getAsJsonObject(); if (payload.has("success") && payload.get("success").getAsBoolean()) return payload.get("data"); int status = res.statusCode(); if (status == 429 && canRetry) { Thread.sleep(1000L * Long.parseLong(res.headers().firstValue("retry-after").orElse("5"))); return fetchSockOdds(url, key, false); } boolean clientError = status >= 400 && status < 500; if (canRetry && !clientError) { Thread.sleep(2000 + new Random().nextInt(3000)); return fetchSockOdds(url, key, false); } System.err.println("SockOdds request failed: " + status + " " + payload.get("error")); return null; } ``` ```ruby def fetch_sirenodds(url, key, can_retry: true) uri = URI(url); req = Net::HTTP::Get.new(uri); req["x-api-key"] = key res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } payload = JSON.parse(res.body) rescue nil return payload["data"] if payload&.dig("success") == true status = res.code.to_i if status == 429 && can_retry sleep(res["retry-after"].to_i.nonzero? || 5); return fetch_sirenodds(url, key, can_retry: false) end client_error = payload&.dig("success") == false && status.between?(400, 499) if can_retry && !client_error sleep(2 + rand * 3); return fetch_sirenodds(url, key, can_retry: false) end warn "SockOdds request failed: #{status} #{payload&.dig('error')}"; nil end ``` ```php function fetchSockOdds(string $url, string $key, bool $canRetry = true) { $ch = curl_init($url); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: $key"], CURLOPT_TIMEOUT => 30]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $payload = $body !== false ? json_decode($body, true) : null; if (($payload['success'] ?? false) === true) return $payload['data']; if ($status === 429 && $canRetry) { sleep(5); return fetchSockOdds($url, $key, false); } $clientError = ($payload['success'] ?? null) === false && $status >= 400 && $status < 500; if ($canRetry && !$clientError) { usleep((2 + lcg_value() * 3) * 1000000); return fetchSockOdds($url, $key, false); } error_log("SockOdds request failed: $status " . ($payload['error'] ?? 'Unknown')); return null; } ``` > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Consensus Odds - Fair and Book Calculations URL: https://sockodds.com/docs/info/consensus-odds/ # Consensus Odds - Fair and Book Calculations [#consensus-odds---fair-and-book-calculations] ## "Lines" [#lines] - "Line" here means the spread or over/under value associated with a set of odds. - For bet types without a line (head to head, yes/no) every price is treated as having a line of 0. - Both sides of a bet carry mirrored line values: home +3 ↔ away −3; over 165.5 ↔ under 165.5. ## Fair odds [#fair-odds] ### Key fields [#key-fields] - `fairSpread` or `fairOverUnder`: the fairest line (if applicable) - `fairOdds`: the fair price for the given side at that line - `fairOddsAvailable`: whether sufficient sharp data existed to calculate them ### Summary [#summary] The fair price is derived from the exchange and sharp-book prices in the feed — the Betfair Exchange, Pinnacle, ProphetX, and Kalshi and Polymarket where they price the fixture — with the margin removed so the implied probabilities of the sides sum to one. It is never estimated from soft-book prices alone: where the sharp sources do not price a market, `fairOdds` is `null` and `fairOddsAvailable` is false. On the Lite plan fair fields are always null. ### Calculation process [#calculation-process] 1. **Group odds** for each event + oddID by `bookmakerID` + line. 2. **Keep the sharp sources** (exchanges, Pinnacle, prediction markets) that are currently open for the market. 3. **Choose the fair line**: the line where the sharp sources' two sides are closest to balanced. 4. **Take the consensus** across those sources at that line. 5. **Remove the juice**: convert each side to implied probability, normalise so they sum to one, convert back to American. ## Book odds [#book-odds] ### Key fields [#key-fields] - `bookSpread` or `bookOverUnder`: consensus main line across bookmakers - `bookOdds`: consensus price for that line - `bookOddsAvailable`: whether the side is open at at least one bookmaker ### Summary [#summary] We identify each bookmaker's main line, select the most common main line across all bookmakers, and take the median price across bookmakers for that line. ### Full calculation process [#full-calculation-process] 1. **Group odds** for each event + oddID by `bookmakerID` + line. 2. **Identify the main line** for each book; where a book doesn't distinguish main from alt, the line closest to the other books' main line is used. 3. **Take the latest price** from each group. 4. **Combine**, preferring prices that are open for betting. 5. **Select the consensus book line**: the line most books call their main line; ties go to the line closest to the fair line. 6. **Consensus book odds** = the median price across bookmakers for that line. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Rate Limits by Plan - Requests and Objects URL: https://sockodds.com/docs/info/rate-limiting/ # Rate Limits by Plan - Requests and Objects [#rate-limits-by-plan---requests-and-objects] Based on your plan, your key is limited to a number of requests per minute. Exceed it and you receive a `429` with a `Retry-After` header until the minute resets. ## Request limits [#request-limits] - Lite plan: 10 requests per minute - Base plan: 60 requests per minute - Platform plan: 600 requests per minute ## Object limits [#object-limits] There is no monthly object cap on any plan. Objects served are counted per day and reported in `/account/usage` for your own tracking. Each response counts as a minimum of 1 object. ## How the counter works [#how-the-counter-works] > **The counter is atomic.** The limit is enforced by the increment itself, so 60 simultaneous requests on a 10/min key serve exactly 10 — it cannot be raced open. A rejected request is still counted for the minute (so hammering is never free) but is *never billed*: usage billing is a separate meter charged only after a request is served. Every response carries `x-ratelimit-limit` and `x-ratelimit-remaining`. Windows are fixed epoch minutes. ## Strategies to avoid rate limiting [#strategies-to-avoid-rate-limiting] 1. Avoid frequent calls to endpoints whose data rarely changes (teams, players, stats) — cache them. 2. Poll at the source cadence (~2 minutes), not faster. 3. Use query params to focus on only the data you need. 4. Check `/account/usage`. ## Response filtering notice [#response-filtering-notice] When your key's plan causes data to be filtered from a response (league scope, bookmaker cap, fair odds), the response carries a `notice` field saying what was withheld: ```json { "success": true, "data": [...], "nextCursor": "afl_2026-09-12_brisbane_lions_vs_adelaide_crows", "notice": "Response is missing 3 events and 15 bookmaker odds. Upgrade your API key to access all data from this query." } ``` ## Checking your rate limit usage [#checking-your-rate-limit-usage] ```javascript fetch("https://api.sockodds.com/v2/account/usage", { headers: { "X-Api-Key": "YOUR_TOKEN" } }); ``` ```json { "success": true, "data": [ { "keyID": "5fb8c85833e6dcf5", "customerID": null, "email": "you@example.com", "isActive": true, "tier": "base", "rateLimits": { "per-minute": { "max-requests": 60, "current-requests": 1, "max-entities": null, "current-entities": null }, "per-day": { "max-requests": null, "current-requests": 12, "max-entities": null, "current-entities": 240 } } } ] } ``` > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Migration Guide - V1 to V2 API URL: https://sockodds.com/docs/info/v1-to-v2/ # Migration Guide - V1 to V2 API [#migration-guide---v1-to-v2-api] > SockOdds has only ever served the v2 schema. This page exists for teams arriving from a SportsGameOdds **v1** integration: the changes below are the v1 → v2 differences you must make, and the resulting v2 code runs against SockOdds unchanged. ## Overview of changes [#overview-of-changes] - **Combined /odds and /events endpoints** — everything is on `/v2/events`; there is no `/v2/odds`. - **Deeplinks on events** — `links.bookmakers` and `links.betslip`. - **Bookmaker prices persist when unavailable** — every `byBookmaker` entry carries `available`; filter on it. - **Unique playerIDs** — `PLAYER_NAME_NUMBER_LEAGUE` (`PATRICK_MAHOMES_1_NFL`), the same across teams. - **Many more request options** on the events endpoint. ## Upgrade guide [#upgrade-guide] ### Swap the base URL [#swap-the-base-url] ```diff - https://api.sportsgameodds.com/v1/... + https://api.sockodds.com/v2/... ``` ### Point v1/events and v1/odds at the combined v2/events [#point-v1events-and-v1odds-at-the-combined-v2events] ```diff - /v1/events... - /v1/odds... + /v2/events... ``` ### Odds field renames [#odds-field-renames] - `odds` → `fairOdds` - `spread` → `fairSpread` - `overUnder` → `fairOverUnder` - `isFallbackOdds` → removed (no placeholder even-money prices) ### Status field renames [#status-field-renames] - `hasMarketOdds` / `hasAnyOdds` → `oddsPresent` - `anyOddsAvailable` / `marketOddsAvailable` → `oddsAvailable` - `nextUpdateAt` → removed (use `info.lastUpdatedAt`) ### Add includeAltLines if you need alt lines [#add-includealtlines-if-you-need-alt-lines] Alt lines are off by default in v2. ### Filter unavailable byBookmaker odds [#filter-unavailable-bybookmaker-odds] ```diff + Event.odds..byBookmaker..available ``` ### Player name paths [#player-name-paths] - `Player.firstName` → `Player.names.firstName` on `/players` (event players keep the flat fields) ## New features & non-breaking changes [#new-features-non-breaking-changes] - The API key can be a query param (`?apiKey=`). - New params on `/v2/events`: `type`, `oddsPresent`, `includeOpposingOdds` (alias `includeOpposingOddIDs`), `includeAltLines`, `bookmakerID`, `teamID`, `playerID`, `live`, `started`, `ended`, `cancelled`, `finalized`, `includeOpenCloseOdds` (accepted; no snapshots yet). - `marketName` on every market; `decimal` on every bookmaker entry. - Default/max limits: `/events` 10/100; `/teams` and `/players` 50/250. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Pointing a SportsGameOdds client at SockOdds URL: https://sockodds.com/docs/info/migrating-from-sgo/ # Pointing a SportsGameOdds client at SockOdds [#pointing-a-sportsgameodds-client-at-sockodds] SockOdds is the Australian extension of the SportsGameOdds v2 schema, so an existing SGO integration needs one change: the base URL. 1. Change the base URL to `https://api.sockodds.com/v2` and the key to an SockOdds key. 2. Keep your parsing: `eventID`, `odds[oddID]`, `byBookmaker`, `opposingOddID`, `status`, the envelope — all unchanged. 3. Expect the `leagueID`s SGO doesn't carry (`AFL`, `AFLW`, `NRL`, `RUGBYLEAGUE_NRLW`, `RUGBYLEAGUE_STATE_ORIGIN`, `RUGBYUNION_INTERNATIONALS`, `IPL`) and Australian `bookmakerID`s (`sportsbet`, `unibet`, `tab`, `playup`, `tabtouch`, `betright`, …). 4. Replace any stream subscription with polling every ~2 minutes (`/stream/events` answers 501). 5. Read the extra signals: `decimal` on every book, `info.stale`, `notice`. ## SDK [#sdk] **TypeScript** · **Python** · **Ruby** · **Go** · **Java** ```typescript import SportsGameOdds from "sports-odds-api"; const client = new SportsGameOdds({ apiKeyHeader: "YOUR_API_KEY", baseURL: "https://api.sockodds.com/v2" }); const res = await client.events.get({ leagueID: ["AFL"] }); console.log(res.data); ``` ```python from sports_odds_api import SportsGameOdds client = SportsGameOdds(api_key_header="YOUR_API_KEY", base_url="https://api.sockodds.com/v2") res = client.events.get({ league_id: ["afl"] }) print(res.data) ``` ```ruby require "sports_odds_api" client = SportsOddsAPI::Client.new(api_key_header: "YOUR_API_KEY", base_url: "https://api.sockodds.com/v2") res = client.events.get({ league_id: ["afl"] }) puts res.data ``` ```go client := sportsoddsapi.NewClient(option.WithAPIKeyHeader("YOUR_API_KEY"), option.WithBaseURL("https://api.sockodds.com/v2")) res, err := client.Events.Get({ leagueID: ["AFL"] }) if err != nil { log.Fatal(err) } fmt.Println(res.Data) ``` ```java SportsGameOddsClient client = SportsGameOddsOkHttpClient.builder() .apiKeyHeader("YOUR_API_KEY").baseUrl("https://api.sockodds.com/v2").build(); var res = client.events.get({ leagueID: ["AFL"] }); System.out.println(res.items()); ``` ## Differences at a glance [#differences-at-a-glance] | | SockOdds | SportsGameOdds | | --- | --- | --- | | Base URL | `https://api.sockodds.com/v2` | `https://api.sportsgameodds.com/v2` | | Key prefix | `so_live_…` | — | | Bookmakers | 25, 21 Australian | 85+, US/EU | | Australian codes | AFL, AFLW, NRL, NRLW, State of Origin, rugby union, IPL | Not carried | | Streaming | 501 | WebSocket on All-Star | | Open/close odds | Not snapshotted | `includeOpenCloseOdds` | | Extra fields | `decimal`, `info.stale`, `links.betslip` | — | | Shape | Identical | Identical | Using both feeds for two markets is common: join on `eventID` and `oddID`. > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Live Odds Tracker Example - JavaScript URL: https://sockodds.com/docs/examples/live-odds-tracker/ # Live Odds Tracker Example - JavaScript [#live-odds-tracker-example---javascript] Complete working example that tracks AFL odds every 2.5 minutes and detects line movement across every Australian bookmaker. ## What you'll build [#what-youll-build] A script that: - Fetches AFL odds on a schedule - Tracks line movement across all bookmakers - Alerts when prices change - Shows before/after comparisons **Perfect for:** Detecting sharp money, identifying steam moves, feeding an alert bot ## Prerequisites [#prerequisites] - Node.js 18+ - SockOdds API key ([free](https://sockodds.com/signup/)) - Basic JavaScript ## Complete code [#complete-code] ### Step 1: Setup project [#step-1-setup-project] ```bash mkdir odds-tracker && cd odds-tracker npm init -y ``` ### Step 2: Create tracker.js [#step-2-create-trackerjs] ```javascript // tracker.js — Node 18+ (global fetch) const API_KEY = process.env.SOCKODDS_KEY; const BASE = "https://api.sockodds.com/v2"; const LEAGUE = process.argv[2] || "AFL"; const INTERVAL_MS = 150_000; // the source refreshes every ~2 min const previous = new Map(); // eventID -> event async function fetchOdds() { const url = new URL(BASE + "/events"); url.search = new URLSearchParams({ leagueID: LEAGUE, oddsAvailable: "true", oddID: "points-home-game-ml-home,points-all-game-ou-over", includeOpposingOdds: "true", limit: "100" }); const r = await fetch(url, { headers: { "x-api-key": API_KEY } }); if (!r.ok) throw new Error(`${r.status} ${await r.text()}`); const body = await r.json(); if (body.notice) console.log("notice:", body.notice); return body.data; } function title(e) { return `${e.teams?.away?.names?.medium ?? "?"} @ ${e.teams?.home?.names?.medium ?? "?"}`; } function compare(prev, next) { const moves = []; for (const [oddID, odd] of Object.entries(next.odds)) { for (const [book, q] of Object.entries(odd.byBookmaker)) { const was = prev?.odds?.[oddID]?.byBookmaker?.[book]; if (!was) continue; if (was.decimal !== q.decimal) moves.push({ oddID, book, from: was.decimal, to: q.decimal, pct: ((q.decimal - was.decimal) / was.decimal * 100).toFixed(1) }); if (was.available !== q.available) moves.push({ oddID, book, available: q.available }); } } return moves; } async function tick() { const events = await fetchOdds(); const stamp = new Date().toISOString().slice(11, 19); for (const e of events) { if (e.info?.stale) { console.log(`[${stamp}] ${title(e)} — STALE since ${e.info.lastUpdatedAt}`); continue; } const moves = compare(previous.get(e.eventID), e); for (const m of moves) console.log(`[${stamp}] ${title(e)} ${m.oddID} ${m.book}: ${"available" in m ? (m.available ? "reopened" : "SUSPENDED") : `${m.from} → ${m.to} (${m.pct}%)`}`); previous.set(e.eventID, e); } console.log(`[${stamp}] tracked ${events.length} ${LEAGUE} events`); } tick().then(() => setInterval(() => tick().catch(console.error), INTERVAL_MS)).catch((e) => { console.error(e); process.exit(1); }); ``` ### Step 3: Run it [#step-3-run-it] ```bash SOCKODDS_KEY=so_live_… node tracker.js AFL ``` ## Expected output [#expected-output] ``` [08:30:12] tracked 6 AFL events [08:32:42] Hawthorn @ Fremantle points-home-game-ml-home sportsbet: 1.91 → 1.87 (-2.1%) [08:32:42] Hawthorn @ Fremantle points-all-game-ou-over tab: SUSPENDED [08:32:42] tracked 6 AFL events ``` ## How it works [#how-it-works] ### 1. Fetch odds [#1-fetch-odds] One events call per league, trimmed with `oddID` and `includeOpposingOdds` so the payload stays small enough for the free plan. ### 2. Process the nested bookmaker structure [#2-process-the-nested-bookmaker-structure] `odds[oddID].byBookmaker[book].decimal` is the number to compare; `available` tells you about suspensions. ### 3. Compare with previous odds [#3-compare-with-previous-odds] A `Map` keyed by `eventID` holds the last event seen. ### 4. Detect significant movement [#4-detect-significant-movement] Filter `moves` by `Math.abs(pct) > 3` for steam. ### 5. Respect stale events [#5-respect-stale-events] `info.stale` means the source stopped writing; don't alert on it. ## Enhancements [#enhancements] ### Track multiple leagues [#track-multiple-leagues] ```javascript for (const league of ["AFL", "NRL", "EPL"]) await tickFor(league); ``` ### Add Discord notifications [#add-discord-notifications] ```javascript await fetch(process.env.DISCORD_WEBHOOK, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ content: `${title(e)} ${m.book} ${m.from}→${m.to} ${e.links?.bookmakers?.[m.book] ?? ""}` }) }); ``` ### Store history [#store-history] ```javascript import { appendFileSync } from "node:fs"; appendFileSync("moves.jsonl", JSON.stringify({ t: Date.now(), eventID: e.eventID, ...m }) + "\n"); ``` ## Troubleshooting [#troubleshooting] ### "No AFL events found" [#no-afl-events-found] Out of season, or your plan doesn't include the league — check `notice` and `/leagues`. ### Rate limit errors (429) [#rate-limit-errors-429] The free plan is 10/min; one league per tick is fine, don't poll faster than the source. ### Missing odds data [#missing-odds-data] Some events carry `oddsPresent: true` but `oddsAvailable: false` — every book has suspended. ## Next steps [#next-steps] ### Combine with other examples [#combine-with-other-examples] - [Arbitrage calculator](https://sockodds.com/docs/examples/arbitrage-calculator/) - [Odds comparison dashboard](https://sockodds.com/docs/examples/odds-comparison-dashboard/) - [Player props analyzer](https://sockodds.com/docs/examples/player-props-analyzer/) - [Parlay builder](https://sockodds.com/docs/examples/parlay-builder/) ### Learn more [#learn-more] - [The oddID grammar](https://sockodds.com/docs/data-types/odds/) - [Best practices](https://sockodds.com/docs/info/best-practices/) - [API reference](https://sockodds.com/docs/reference/) > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Arbitrage Calculator Example - Python URL: https://sockodds.com/docs/examples/arbitrage-calculator/ # Arbitrage Calculator Example - Python [#arbitrage-calculator-example---python] Find cross-bookmaker margins by identifying odds discrepancies across every Australian book. ## What you'll build [#what-youll-build] A script that: - Scans all bookmakers for odds discrepancies - Calculates arbitrage opportunities on two-sided markets - Shows optimal stake sizing - Calculates guaranteed profit percentage **Perfect for:** Finding cross-book margins, comparing bookmaker odds, beating the vig ## Prerequisites [#prerequisites] - Python 3.8+ - SockOdds API key ([free](https://sockodds.com/signup/)) - Basic Python ## Complete code [#complete-code] ### Step 1: Setup project [#step-1-setup-project] ```bash mkdir arb-finder && cd arb-finder python -m venv venv && source venv/bin/activate pip install requests ``` ### Step 2: Create arb_calculator.py [#step-2-create-arb_calculatorpy] ```python # arb_calculator.py import os, sys, requests API_KEY = os.environ["SOCKODDS_KEY"] BASE = "https://api.sockodds.com/v2" LEAGUE = sys.argv[1] if len(sys.argv) > 1 else "AFL" MIN_MARGIN = 0.5 # percent def fetch_events(league): events, cursor = [], None while True: params = {"leagueID": league, "oddsAvailable": "true", "limit": 100, **({"cursor": cursor} if cursor else {})} r = requests.get(f"{BASE}/events", params=params, headers={"x-api-key": API_KEY}); r.raise_for_status() body = r.json(); events += body["data"]; cursor = body.get("nextCursor") if not cursor: break return events def best_price(odd): """Best available decimal across books, and which book.""" best = (None, None) for book, q in odd["byBookmaker"].items(): if q.get("available") and q.get("decimal") and (best[0] is None or q["decimal"] > best[0]): best = (q["decimal"], book) return best def find_arbs(event): odds = event["odds"]; seen = set(); out = [] for odd_id, odd in odds.items(): opp = odd.get("opposingOddID") if not opp or opp not in odds or odd_id in seen: continue seen.update({odd_id, opp}) # a two-sided market only arbs when both sides are on the same line a_line, b_line = odd.get("bookOverUnder") or odd.get("bookSpread"), odds[opp].get("bookOverUnder") or odds[opp].get("bookSpread") (pa, ba), (pb, bb) = best_price(odd), best_price(odds[opp]) if not pa or not pb: continue total = 1 / pa + 1 / pb if total < 1: margin = (1 / total - 1) * 100 stake_a, stake_b = (1 / pa) / total, (1 / pb) / total out.append(dict(market=odd["marketName"], side_a=(ba, pa, stake_a), side_b=(bb, pb, stake_b), margin=margin, lines=(a_line, b_line))) return out if __name__ == "__main__": events = fetch_events(LEAGUE) print(f"scanned {len(events)} {LEAGUE} events") for e in events: if e["info"].get("stale"): continue for arb in find_arbs(e): if arb["margin"] < MIN_MARGIN: continue (ba, pa, sa), (bb, pb, sb) = arb["side_a"], arb["side_b"] print(f"\n{e['eventID']} {arb['market']} margin {arb['margin']:.2f}%") print(f" {ba:12} @ {pa:.2f} stake {sa*100:5.1f}% {e['links']['bookmakers'].get(ba, '')}") print(f" {bb:12} @ {pb:.2f} stake {sb*100:5.1f}% {e['links']['bookmakers'].get(bb, '')}") ``` ### Step 3: Run it [#step-3-run-it] ```bash SOCKODDS_KEY=so_live_… python arb_calculator.py AFL ``` ## Expected output [#expected-output] ``` scanned 6 AFL events afl_2026-09-05_collingwood_vs_geelong Total Points margin 1.23% sportsbet @ 2.05 stake 49.4% https://www.sportsbet.com.au/… betfairexchange @ 2.00 stake 50.6% https://www.betfair.com.au/… ``` ## How it works [#how-it-works] ### 1. Fetch events with odds [#1-fetch-events-with-odds] Paged with `cursor`; every market and every book arrives in one object. ### 2. Use decimal directly [#2-use-decimal-directly] No American-to-decimal conversion — every book entry carries `decimal`. ### 3. Pair the sides [#3-pair-the-sides] `opposingOddID` names the other side; each pair is scanned once. ### 4. Find the best price per side [#4-find-the-best-price-per-side] Skip `available: false`. ### 5. Calculate arbitrage [#5-calculate-arbitrage] Total implied probability below 1 → margin `1/total − 1`. ### 6. Calculate optimal stakes [#6-calculate-optimal-stakes] Proportional to `(1/price)/total`. ## Enhancements [#enhancements] ### Minimum profit filter [#minimum-profit-filter] ```python MIN_MARGIN = 1.0 ``` ### Middles [#middles] ```python # with includeAltLines=true, compare line values across books for each side: # Home -5.5 at book A and Away +6.5 at book B is a middle on a 6-point margin ``` ### Real-time monitoring [#real-time-monitoring] ```python import time while True: ...scan... time.sleep(150) ``` ## Troubleshooting [#troubleshooting] ### "No arbitrage opportunities found" [#no-arbitrage-opportunities-found] Normal most of the time. Lower `MIN_MARGIN` to see near-misses, or add `includeAltLines=true`. ### Negative profit calculation [#negative-profit-calculation] Both sides must be on the same line — the script prints the lines so you can check. ### Account limitations [#account-limitations] Books limit winners. This is between you and the book. ## Next steps [#next-steps] ### Combine with other examples [#combine-with-other-examples] - [Live odds tracker](https://sockodds.com/docs/examples/live-odds-tracker/) - [Odds comparison dashboard](https://sockodds.com/docs/examples/odds-comparison-dashboard/) - [Player props analyzer](https://sockodds.com/docs/examples/player-props-analyzer/) - [Parlay builder](https://sockodds.com/docs/examples/parlay-builder/) ### Learn more [#learn-more] - [The oddID grammar](https://sockodds.com/docs/data-types/odds/) - [Best practices](https://sockodds.com/docs/info/best-practices/) - [API reference](https://sockodds.com/docs/reference/) > Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/) --- # Odds Comparison Dashboard Example - React/Next.js URL: https://sockodds.com/docs/examples/odds-comparison-dashboard/ # Odds Comparison Dashboard Example - React/Next.js [#odds-comparison-dashboard-example---reactnextjs] Create a Next.js dashboard that compares odds across every Australian bookmaker and highlights the best line. ## What you'll build [#what-youll-build] A script that: - Displays odds from all bookmakers side by side - Highlights the best price for each market - Auto-refreshes every 2.5 minutes - Links each best price to the bookmaker - Keeps the key on the server **Perfect for:** Odds comparison sites, betting platforms, research tools ## Prerequisites [#prerequisites] - Node.js 18+ - Basic React/Next.js - SockOdds API key ([free](https://sockodds.com/signup/)) ## Complete code [#complete-code] ### Step 1: Create a Next.js project [#step-1-create-a-nextjs-project] ```bash npx create-next-app@latest odds-dashboard --ts --app --tailwind --eslint cd odds-dashboard ``` ### Step 2: Create the API route (app/api/odds/route.ts) [#step-2-create-the-api-route-appapioddsroutets] ```typescript // app/api/odds/route.ts — proxies the feed so the key never reaches the browser import { NextResponse } from "next/server"; export async function GET(req: Request) { const league = new URL(req.url).searchParams.get("league") ?? "AFL"; const url = new URL("https://api.sockodds.com/v2/events"); url.search = new URLSearchParams({ leagueID: league, oddsAvailable: "true", oddID: "points-home-game-ml-home", includeOpposingOdds: "true", limit: "50" }).toString(); const r = await fetch(url, { headers: { "x-api-key": process.env.SOCKODDS_KEY! }, next: { revalidate: 120 } }); if (!r.ok) return NextResponse.json({ error: await r.text() }, { status: r.status }); return NextResponse.json(await r.json()); } ``` ### Step 3: Create the dashboard component (app/Dashboard.tsx) [#step-3-create-the-dashboard-component-appdashboardtsx] ```typescript "use client"; import { useEffect, useState } from "react"; type Book = { odds: string; decimal: number; available: boolean }; type Odd = { oddID: string; marketName: string; bookOdds: string; fairOdds: string | null; byBookmaker: Record }; type Event = { eventID: string; teams: { home: { names: { medium: string } }; away: { names: { medium: string } } }; odds: Record; links: { bookmakers?: Record }; info: { stale: boolean } }; export default function Dashboard({ league }: { league: string }) { const [events, setEvents] = useState([]); const [updated, setUpdated] = useState(""); async function load() { const b = await (await fetch(`/api/odds?league=${league}`)).json(); setEvents(b.data ?? []); setUpdated(new Date().toLocaleTimeString()); } useEffect(() => { load(); const t = setInterval(load, 150_000); return () => clearInterval(t); }, [league]); const books = Array.from(new Set(events.flatMap((e) => Object.values(e.odds).flatMap((o) => Object.keys(o.byBookmaker))))).sort(); return (

{league} head to head · updated {updated}

{books.map((b) => )}{events.flatMap((e) => Object.values(e.odds).map((o) => { const best = Math.max(...Object.values(o.byBookmaker).filter((q) => q.available).map((q) => q.decimal)); return {books.map((b) => { const q = o.byBookmaker[b]; const isBest = q?.available && q.decimal === best; return ; })} ; }))}
MarketFair{b}
{e.teams.away.names.medium} @ {e.teams.home.names.medium} — {o.marketName} {o.fairOdds ?? "—"} {q ? (isBest && e.links.bookmakers?.[b] ? {q.decimal.toFixed(2)} : q.decimal.toFixed(2)) : ""}
); } ``` ### Step 4: Update the page (app/page.tsx) [#step-4-update-the-page-apppagetsx] ```typescript import Dashboard from "./Dashboard"; export default function Page() { return ; } ``` ### Step 5: Add the environment variable [#step-5-add-the-environment-variable] ```bash echo 'SOCKODDS_KEY=so_live_…' > .env.local ``` ### Step 6: Run it [#step-6-run-it] ```bash npm run dev # open http://localhost:3000 ``` ## Expected output [#expected-output] ``` A table: one row per market, one column per bookmaker, the best available decimal highlighted and linked to the book; stale events dimmed; the fair price in grey. ``` ## How it works [#how-it-works] ### 1. Proxy API calls through the backend [#1-proxy-api-calls-through-the-backend] The route handler holds the key; the browser only ever talks to `/api/odds`. ### 2. Process the nested bookmaker structure [#2-process-the-nested-bookmaker-structure] `Object.values(o.byBookmaker)`, skipping `available: false`. ### 3. Find the best odds [#3-find-the-best-odds] `Math.max` over `decimal`. ### 4. Highlight and link [#4-highlight-and-link] The best cell links to `links.bookmakers[book]`. ### 5. Auto-refresh [#5-auto-refresh] `setInterval` at 2.5 minutes, matching the source. ## Enhancements [#enhancements] ### Add totals and lines [#add-totals-and-lines] ```typescript oddID: "points-home-game-ml-home,points-all-game-ou-over,points-home-game-sp-home" ``` ### Show line movement [#show-line-movement] ```typescript // keep the previous body in a ref and compare decimal per book per oddID; render ▲/▼ ``` ### Add filters [#add-filters] ```typescript const [league, setLeague] = useState("AFL"); //