API reference
16 operations, generated from the contract the API serves.
Base URL: https://api.sockodds.com/v1. Send your key as Authorization: Bearer; keep it on your server, out of browser code and out of URLs. Start with the quickstart if this is your first request.
Responses use { data, meta, links }. Follow links.next unchanged to continue a list. Errors use application/problem+json. Example identities and dates below are illustrative; use the values your own responses return.
Try live requests from the API playground in your account, or get the AI starter context. The OpenAPI contract is the source of this page and of the Postman collection.
GET/sports
Operation listSports · Catalogue
Lists sports.
curl -sS "https://api.sockodds.com/v1/sports" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/sports", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/sports",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"key": "australian_football",
"name": "Australian football",
"participants": "teams"
},
{
"key": "rugby_league",
"name": "Rugby league",
"participants": "teams"
},
{
"key": "rugby_union",
"name": "Rugby union",
"participants": "teams"
},
{
"key": "soccer",
"name": "Soccer",
"participants": "teams"
},
{
"key": "basketball",
"name": "Basketball",
"participants": "teams"
},
{
"key": "american_football",
"name": "American football",
"participants": "teams"
},
{
"key": "baseball",
"name": "Baseball",
"participants": "teams"
},
{
"key": "ice_hockey",
"name": "Ice hockey",
"participants": "teams"
},
{
"key": "tennis",
"name": "Tennis",
"participants": "competitors"
},
{
"key": "mma",
"name": "Mixed martial arts",
"participants": "competitors"
},
{
"key": "cricket",
"name": "Cricket",
"participants": "teams"
},
{
"key": "motorsport",
"name": "Motorsport",
"participants": "competitors"
},
{
"key": "esports",
"name": "Esports",
"participants": "teams"
}
],
"meta": {
"count": 13,
"cursor": null
},
"links": {
"next": null
}
}GET/leagues
Operation listLeagues · Catalogue
Lists leagues.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
sport | query | No | Key (string) | See the OpenAPI schema for allowed values. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/leagues?sport=rugby_league" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/leagues?sport=rugby_league", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/leagues?sport=rugby_league",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"key": "nrl",
"name": "NRL",
"sport": "rugby_league",
"region": "au",
"periods": [
"full",
"regulation",
"half_1",
"half_2"
]
},
{
"key": "nrlw",
"name": "NRL Women's",
"sport": "rugby_league",
"region": "au",
"periods": [
"full",
"regulation",
"half_1",
"half_2"
]
},
{
"key": "rlwc",
"name": "Rugby League World Cup",
"sport": "rugby_league",
"region": "intl",
"periods": [
"full",
"regulation",
"half_1",
"half_2"
]
},
{
"key": "rlwcw",
"name": "Rugby League World Cup Women's",
"sport": "rugby_league",
"region": "intl",
"periods": [
"full",
"regulation",
"half_1",
"half_2"
]
}
],
"meta": {
"count": 4,
"cursor": null
},
"links": {
"next": null
}
}GET/bookmakers
Operation listBookmakers · Catalogue
Lists bookmakers.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
regions | query | No | string | Comma-separated bookmaker regions, at most 20. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/bookmakers?regions=au&limit=3" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/bookmakers?regions=au&limit=3", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/bookmakers?regions=au&limit=3",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"key": "bearbet",
"name": "BearBet",
"kind": "sportsbook",
"regions": [
"au"
]
},
{
"key": "bet365",
"name": "bet365",
"kind": "sportsbook",
"regions": [
"au"
]
},
{
"key": "betblitz",
"name": "BetBlitz",
"kind": "sportsbook",
"regions": [
"au"
]
}
],
"meta": {
"count": 3,
"cursor": "eyJuIjozLCJmIjoiZTUzY2IxODA0YzI4NTg2ZCJ9"
},
"links": {
"next": "https://api.sockodds.com/v1/bookmakers?regions=au&limit=3&cursor=eyJuIjozLCJmIjoiZTUzY2IxODA0YzI4NTg2ZCJ9"
}
}GET/markets
Operation listMarkets · Catalogue
Markets and the sides each offers. With league, only the markets offered in that league's sport, and only the role sides that sport uses (winner and handicap are home and away in team sports, competitor_1 and competitor_2 in tennis; winner is also offered in UFC).
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/markets?league=rlwc&limit=2" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/markets?league=rlwc&limit=2", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/markets?league=rlwc&limit=2",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"key": "winner",
"name": "Winner",
"subject": "event",
"bet": "winner",
"stat": null,
"has_line": false,
"sides": [
"home",
"away"
],
"sports": [
"australian_football",
"rugby_league",
"rugby_union",
"basketball",
"american_football",
"baseball",
"ice_hockey",
"tennis",
"mma",
"cricket",
"esports"
],
"periods": [
"full",
"half_1",
"half_2",
"quarter_1",
"quarter_2",
"quarter_3",
"quarter_4",
"innings_1_to_5",
"half_2_ot",
"period_1",
"period_2",
"period_3",
"set_1",
"set_2",
"set_3",
"set_4",
"set_5",
"map_1",
"map_2",
"map_3",
"map_4",
"map_5"
]
},
{
"key": "result",
"name": "Result (with the draw)",
"subject": "event",
"bet": "result",
"stat": null,
"has_line": false,
"sides": [
"home",
"draw",
"away"
],
"sports": [
"american_football",
"soccer",
"rugby_league",
"rugby_union",
"ice_hockey",
"cricket",
"baseball"
],
"periods": [
"full",
"regulation",
"half_1",
"half_2",
"period_1",
"period_2",
"period_3",
"quarter_1",
"quarter_2",
"quarter_3",
"quarter_4",
"inning_1",
"innings_1_to_5"
]
}
],
"meta": {
"count": 2,
"cursor": "eyJuIjoyLCJmIjoiNDYxMmFlY2Y5ZGMzY2Y5NCJ9"
},
"links": {
"next": "https://api.sockodds.com/v1/markets?league=rlwc&limit=2&cursor=eyJuIjoyLCJmIjoiNDYxMmFlY2Y5ZGMzY2Y5NCJ9"
}
}GET/teams
Operation listTeams · Identity
Lists teams.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/teams?league=rlwc&limit=2" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/teams?league=rlwc&limit=2", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/teams?league=rlwc&limit=2",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"key": "australia_rugby_league",
"name": "Australia",
"sport": "rugby_league",
"leagues": [
"rlwc"
]
},
{
"key": "new_zealand_rugby_league",
"name": "New Zealand",
"sport": "rugby_league",
"leagues": [
"rlwc"
]
}
],
"meta": {
"count": 2,
"cursor": null
},
"links": {
"next": null
}
}GET/players
Operation listPlayers · Identity
Lists players.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
team | query | No | Key (string) | See the OpenAPI schema for allowed values. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/players?league=rlwc&team=australia_rugby_league&limit=2" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/players?league=rlwc&team=australia_rugby_league&limit=2", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/players?league=rlwc&team=australia_rugby_league&limit=2",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"id": "pl_000ckk0qrcd5",
"name": "Blayke Brailey",
"sport": "rugby_league",
"team": {
"key": "australia_rugby_league",
"name": "Australia"
}
},
{
"id": "pl_000ckk0v7atp",
"name": "Bradman Best",
"sport": "rugby_league",
"team": {
"key": "australia_rugby_league",
"name": "Australia"
}
}
],
"meta": {
"count": 2,
"cursor": null
},
"links": {
"next": null
}
}GET/events
Operation listEvents · Identity
Lists events.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
starts_after | query | No | string (date-time) | UTC date and time. With neither starts_after nor starts_before, the list starts 48 hours ago (live and upcoming games plus the last two days), and international cricket Tests (league cricket) are listed for six days from their start; name a window to list older games. |
starts_before | query | No | string (date-time) | UTC date and time. Give starts_after, starts_before or both to list games outside the default window (from 48 hours ago; six days for cricket Tests). |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/events?league=rlwc" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/events?league=rlwc", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/events?league=rlwc",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"id": "ev_000ckk0zrx33",
"name": "Australia v New Zealand",
"league": {
"key": "rlwc",
"name": "Rugby League World Cup"
},
"scheduled_start": "2026-10-15T09:05:00.000Z",
"status": "scheduled",
"participants": [
{
"role": "away",
"identity": {
"key": "new_zealand_rugby_league",
"name": "New Zealand"
}
},
{
"role": "home",
"identity": {
"key": "australia_rugby_league",
"name": "Australia"
}
}
]
}
],
"meta": {
"count": 1,
"cursor": null
},
"links": {
"next": null
}
}GET/events/lookup
Operation lookupEvents · Identity
Find our event from an official event id: ESPN's event id, the AFL's match id (CD_M and digits) for AFLW, or the NRL's match id (digits) for the Rugby League World Cup. Send ref for one event, or refs for up to 100 at once. With ref, the response is the event, or 404 when no event carries that id. With refs, the response lists the ids that matched, in the order sent, each with its event; ids that match nothing are left out. A merged event returns the event it was merged into. Only official sources can be looked up, never a bookmaker's listing id. Each event returned counts as one object towards your allowance, like an event read.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
source | query | Yes | OfficialSource (one of espn, afl, nrl) | Who issued the id: espn, afl for AFLW match ids, or nrl for Rugby League World Cup match ids. |
ref | query | No | OfficialRef (string) | One official event id. Use this or refs, not both. |
refs | query | No | string | Comma-separated official event ids, at most 100. Use this or ref, not both. |
curl -sS "https://api.sockodds.com/v1/events/lookup?source=nrl&ref=20261310110" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/events/lookup?source=nrl&ref=20261310110", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/events/lookup?source=nrl&ref=20261310110",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": {
"id": "ev_000ckk0zrx33",
"name": "Australia v New Zealand",
"league": {
"key": "rlwc",
"name": "Rugby League World Cup"
},
"scheduled_start": "2026-10-15T09:05:00.000Z",
"status": "scheduled",
"participants": [
{
"role": "away",
"identity": {
"key": "new_zealand_rugby_league",
"name": "New Zealand"
}
},
{
"role": "home",
"identity": {
"key": "australia_rugby_league",
"name": "Australia"
}
}
]
},
"meta": {
"count": 1,
"cursor": null
},
"links": {
"next": null
}
}GET/events/{event_id}
Operation getEvent · Identity
Returns event.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
event_id | path | Yes | EventId (string) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/events/ev_000ckk0zrx33" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/events/ev_000ckk0zrx33", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/events/ev_000ckk0zrx33",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": {
"id": "ev_000ckk0zrx33",
"name": "Australia v New Zealand",
"league": {
"key": "rlwc",
"name": "Rugby League World Cup"
},
"scheduled_start": "2026-10-15T09:05:00.000Z",
"status": "scheduled",
"participants": [
{
"role": "away",
"identity": {
"key": "new_zealand_rugby_league",
"name": "New Zealand"
}
},
{
"role": "home",
"identity": {
"key": "australia_rugby_league",
"name": "Australia"
}
}
]
},
"meta": {
"count": 1,
"cursor": null
},
"links": {
"next": null
}
}GET/events/{event_id}/odds
Operation getEventOdds · Prices
Current prices for one event, grouped by market: each market and outcome appears once, and each outcome lists every bookmaker's price in prices, best price first. Markets come in catalogue order, then period, subject and line; outcomes in the market's side order. Use group_by=bookmaker for one block per bookmaker instead (the shape v1 launched with). Each price counts as one object towards your allowance, plus one for the event, in either shape.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
event_id | path | Yes | EventId (string) | See the OpenAPI schema for allowed values. |
markets | query | No | string | Comma-separated market keys, at most 20. |
regions | query | No | string | Comma-separated bookmaker regions, at most 20. |
bookmakers | query | No | string | Comma-separated bookmaker keys, at most 20. |
alternates | query | No | boolean (default false) | See the OpenAPI schema for allowed values. |
group_by | query | No | string (one of market, bookmaker; default market) | How prices are grouped. market (the default) lists each market and outcome once, with every bookmaker's price under the outcome in prices, best price first. bookmaker lists each bookmaker with its own copy of the markets, the shape v1 launched with. |
non_participant | query | No | string | Comma-separated non-participation rules (void, loses, under, fair_price). Keeps only player-market prices whose bookmaker settles a player who takes no part by one of these rules; prices on every other market are unaffected. For example non_participant=void keeps only player prices that are refunded when the player does not play. |
snapshots | query | No | string (one of opening, closing, opening,closing) | Pro and Internal only. Adds opening, closing or both, as requested, to each price, from that bookmaker's own quotes observed strictly before a cutoff (a quote stamped exactly at the cutoff does not count). For every sport but MMA the cutoff is the event's scheduled_start, so in-play quotes never count. For an MMA bout, whose scheduled_start is its card segment's start, the cutoff once the bout has been observed live or final (first at F) is the last poll that still showed it scheduled before F, but never earlier than the earlier of scheduled_start and F; a bout never observed live or final uses scheduled_start. |
curl -sS "https://api.sockodds.com/v1/events/ev_000ckk0zrx33/odds?markets=winner&bookmakers=sportsbet" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/events/ev_000ckk0zrx33/odds?markets=winner&bookmakers=sportsbet", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/events/ev_000ckk0zrx33/odds?markets=winner&bookmakers=sportsbet",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": {
"id": "ev_000ckk0zrx33",
"name": "Australia v New Zealand",
"league": {
"key": "rlwc",
"name": "Rugby League World Cup"
},
"scheduled_start": "2026-10-15T09:05:00.000Z",
"status": "scheduled",
"participants": [
{
"role": "away",
"identity": {
"key": "new_zealand_rugby_league",
"name": "New Zealand"
}
},
{
"role": "home",
"identity": {
"key": "australia_rugby_league",
"name": "Australia"
}
}
],
"markets": [
{
"id": "mk_pz0msgze2rqwwktb",
"key": "winner",
"name": "Winner",
"period": "full",
"subject": null,
"alternate": false,
"outcomes": [
{
"id": "oc_nncgcssmzjhyt5pe",
"name": "Australia",
"side": "home",
"line": null,
"prices": [
{
"bookmaker": "sportsbet",
"decimal_odds": 1.72,
"implied_probability": 0.581395,
"available": true,
"observed_at": "2026-10-15T07:58:00.000Z"
}
]
},
{
"id": "oc_cwsr1gda0qcgf03j",
"name": "New Zealand",
"side": "away",
"line": null,
"prices": [
{
"bookmaker": "sportsbet",
"decimal_odds": 2.12,
"implied_probability": 0.471698,
"available": true,
"observed_at": "2026-10-15T07:58:00.000Z"
}
]
}
]
}
]
},
"meta": {
"count": 1,
"cursor": null
},
"links": {
"next": null
}
}GET/odds
Operation listOdds · Prices
Current prices for a page of events (25 by default, up to 100), grouped by market as on /events/{event_id}/odds unless group_by=bookmaker. Pages follow events; a cursor works only with the filters and grouping it was issued for, so follow links.next. Filters apply to prices before grouping: an outcome or market with no matching price is left out, and an event with none has an empty markets list. Each price counts as one object towards your allowance, plus one per event.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
markets | query | No | string | Comma-separated market keys, at most 20. |
regions | query | No | string | Comma-separated bookmaker regions, at most 20. |
bookmakers | query | No | string | Comma-separated bookmaker keys, at most 20. |
starts_after | query | No | string (date-time) | UTC date and time. With neither starts_after nor starts_before, the list starts 48 hours ago (live and upcoming games plus the last two days), and international cricket Tests (league cricket) are listed for six days from their start; name a window to list older games. |
starts_before | query | No | string (date-time) | UTC date and time. Give starts_after, starts_before or both to list games outside the default window (from 48 hours ago; six days for cricket Tests). |
alternates | query | No | boolean (default false) | See the OpenAPI schema for allowed values. |
group_by | query | No | string (one of market, bookmaker; default market) | How prices are grouped. market (the default) lists each market and outcome once, with every bookmaker's price under the outcome in prices, best price first. bookmaker lists each bookmaker with its own copy of the markets, the shape v1 launched with. |
non_participant | query | No | string | Comma-separated non-participation rules (void, loses, under, fair_price). Keeps only player-market prices whose bookmaker settles a player who takes no part by one of these rules; prices on every other market are unaffected. For example non_participant=void keeps only player prices that are refunded when the player does not play. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/odds?league=rlwc&markets=winner&bookmakers=sportsbet&limit=1&starts_after=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch(`https://api.sockodds.com/v1/odds?league=rlwc&markets=winner&bookmakers=sportsbet&limit=1&starts_after=${new Date().toISOString().slice(0, 19)}Z`, {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
from datetime import datetime, timezone
request = urllib.request.Request(
f"https://api.sockodds.com/v1/odds?league=rlwc&markets=winner&bookmakers=sportsbet&limit=1&starts_after={datetime.now(timezone.utc):%Y-%m-%dT%H:%M:%SZ}",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"id": "ev_000ckk0zrx33",
"name": "Australia v New Zealand",
"league": {
"key": "rlwc",
"name": "Rugby League World Cup"
},
"scheduled_start": "2026-10-15T09:05:00.000Z",
"status": "scheduled",
"participants": [
{
"role": "away",
"identity": {
"key": "new_zealand_rugby_league",
"name": "New Zealand"
}
},
{
"role": "home",
"identity": {
"key": "australia_rugby_league",
"name": "Australia"
}
}
],
"markets": [
{
"id": "mk_pz0msgze2rqwwktb",
"key": "winner",
"name": "Winner",
"period": "full",
"subject": null,
"alternate": false,
"outcomes": [
{
"id": "oc_nncgcssmzjhyt5pe",
"name": "Australia",
"side": "home",
"line": null,
"prices": [
{
"bookmaker": "sportsbet",
"decimal_odds": 1.72,
"implied_probability": 0.581395,
"available": true,
"observed_at": "2026-10-15T07:58:00.000Z"
}
]
},
{
"id": "oc_cwsr1gda0qcgf03j",
"name": "New Zealand",
"side": "away",
"line": null,
"prices": [
{
"bookmaker": "sportsbet",
"decimal_odds": 2.12,
"implied_probability": 0.471698,
"available": true,
"observed_at": "2026-10-15T07:58:00.000Z"
}
]
}
]
}
]
}
],
"meta": {
"count": 1,
"cursor": null
},
"links": {
"next": null
}
}WebSocket/stream/odds
Operation streamOdds · Prices
Pro and Internal only. Upgrade to a WebSocket with your bearer key to receive the current prices for one event, or for up to 20 events in one league, every 15 seconds. Each message is a JSON object whose type is snapshot (the same body as the matching odds route, grouped by market unless group_by=bookmaker), heartbeat (nothing changed) or error (a problem object, after which the connection closes). A refresh counts towards your allowance like an ordinary odds request only when it sends a new snapshot; a heartbeat (nothing changed) is free. Every refresh still counts towards your per minute request limit. Up to two streams per key. A stream lasts at most one hour; reconnect for a fresh one. The stream is read only; a message from the client closes it. Close codes are 1000 (lifetime reached), 1008 (key, plan or limit change), 1009 (snapshot too large, narrow the filters), 1011 (refresh failed) and 1013 (client reading too slowly).
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
event_id | query | No | EventId (string) | One event. Use this or league, not both. |
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
markets | query | No | string | Comma-separated market keys, at most 20. |
regions | query | No | string | Comma-separated bookmaker regions, at most 20. |
bookmakers | query | No | string | Comma-separated bookmaker keys, at most 20. |
alternates | query | No | boolean (default false) | See the OpenAPI schema for allowed values. |
group_by | query | No | string (one of market, bookmaker; default market) | How prices are grouped. market (the default) lists each market and outcome once, with every bookmaker's price under the outcome in prices, best price first. bookmaker lists each bookmaker with its own copy of the markets, the shape v1 launched with. |
non_participant | query | No | string | Comma-separated non-participation rules (void, loses, under, fair_price). Keeps only player-market prices whose bookmaker settles a player who takes no part by one of these rules; prices on every other market are unaffected. For example non_participant=void keeps only player prices that are refunded when the player does not play. |
limit | query | No | integer (minimum 1; maximum 20; default 10) | With league only. Events per snapshot, 1 to 20. Defaults to 10. |
Stream limits
| Limit | Value |
|---|---|
| Refresh interval | 15 seconds |
| Longest connection | 3,600 seconds |
| Largest snapshot | 8,000,000 bytes |
| Largest unread backlog | 1,000,000 bytes |
| Open streams per key | 2 streams |
| Events per league snapshot | 20 events |
Close codes
| Code | Meaning |
|---|---|
1000 | lifetime reached |
1008 | key, plan or limit change |
1009 | snapshot too large, narrow the filters |
1011 | refresh failed |
1013 | client reading too slowly |
const socket = new WebSocket("wss://api.sockodds.com/v1/stream/odds?event_id=ev_000ckk0zrx33", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
socket.addEventListener("message", (message) => {
const frame = JSON.parse(message.data);
if (frame.type === "heartbeat") return; // nothing changed since the last snapshot
if (frame.type === "error") throw new Error(frame.problem.detail);
// A snapshot is the odds route's body: one event for event_id, a list for league.
// Prices are grouped by market, best first, so prices[0] is each outcome's best price.
const events = Array.isArray(frame.data) ? frame.data : [frame.data];
for (const event of events) {
const market = event.markets[0];
if (!market) continue;
const best = market.outcomes.map((o) => `${o.name} ${o.prices[0].decimal_odds} (${o.prices[0].bookmaker})`);
console.log(`${event.name}, ${market.name}: ${best.join(", ")}`);
}
socket.close();
});Output from a local copy of the API with invented test prices
Australia v New Zealand, Winner: Australia 1.81 (playup), New Zealand 2.23 (playup)GET/odds/history
Operation listOddsHistory · Prices
Pro and Internal only. Own observations recorded under the new identities only. No observation predates 22 December 2025 UTC. Changes older than the live store keeps are read from the daily history archive and returned in the same shape and order, so a window reads the same whichever store holds it. Changes are ordered by observation time, then by when they were recorded. A window may not exceed 24 hours. While older history is busy the API answers 503 with a short Retry-After.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
event_id | query | Yes | EventId (string) | See the OpenAPI schema for allowed values. |
from | query | Yes | string (date-time) | See the OpenAPI schema for allowed values. |
to | query | Yes | string (date-time) | See the OpenAPI schema for allowed values. |
markets | query | No | string | Comma-separated market keys, at most 20. |
bookmakers | query | No | string | Comma-separated bookmaker keys, at most 20. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 1000; default 250) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/odds/history?event_id=ev_000ckk0zrx33&from=2026-10-15T00:00:00Z&to=2026-10-15T08:00:00Z&bookmakers=sportsbet&limit=4" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/odds/history?event_id=ev_000ckk0zrx33&from=2026-10-15T00:00:00Z&to=2026-10-15T08:00:00Z&bookmakers=sportsbet&limit=4", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/odds/history?event_id=ev_000ckk0zrx33&from=2026-10-15T00:00:00Z&to=2026-10-15T08:00:00Z&bookmakers=sportsbet&limit=4",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"event": "ev_000ckk0zrx33",
"market": {
"id": "mk_pz0msgze2rqwwktb",
"key": "winner",
"name": "Winner",
"period": "full",
"subject": null
},
"outcome": {
"id": "oc_nncgcssmzjhyt5pe",
"name": "Australia",
"side": "home"
},
"bookmaker": "sportsbet",
"observed_at": "2026-10-15T01:58:00.000Z",
"kind": "opened",
"decimal_odds": 1.79,
"implied_probability": 0.558659,
"line": null
},
{
"event": "ev_000ckk0zrx33",
"market": {
"id": "mk_pz0msgze2rqwwktb",
"key": "winner",
"name": "Winner",
"period": "full",
"subject": null
},
"outcome": {
"id": "oc_cwsr1gda0qcgf03j",
"name": "New Zealand",
"side": "away"
},
"bookmaker": "sportsbet",
"observed_at": "2026-10-15T01:58:00.000Z",
"kind": "opened",
"decimal_odds": 2.2,
"implied_probability": 0.454545,
"line": null
},
{
"event": "ev_000ckk0zrx33",
"market": {
"id": "mk_5j02m4w6004weeze",
"key": "handicap",
"name": "Handicap",
"period": "full",
"subject": null
},
"outcome": {
"id": "oc_j6paqe6cbz2fy0f8",
"name": "Australia",
"side": "home"
},
"bookmaker": "sportsbet",
"observed_at": "2026-10-15T01:58:00.000Z",
"kind": "opened",
"decimal_odds": 1.98,
"implied_probability": 0.505051,
"line": -6.5
},
{
"event": "ev_000ckk0zrx33",
"market": {
"id": "mk_5j02m4w6004weeze",
"key": "handicap",
"name": "Handicap",
"period": "full",
"subject": null
},
"outcome": {
"id": "oc_74ypb0kmrb0cbefw",
"name": "New Zealand",
"side": "away"
},
"bookmaker": "sportsbet",
"observed_at": "2026-10-15T01:58:00.000Z",
"kind": "opened",
"decimal_odds": 1.98,
"implied_probability": 0.505051,
"line": 6.5
}
],
"meta": {
"count": 4,
"cursor": "eyJhdCI6IjIwMjYtMTAtMTVUMDE6NTg6MDAuMDAwWiIsImlkIjoiNTAzIiwicmV2aXNpb24iOiIxMDA1IiwiZiI6IjJkMTc0OGRkZDY4Y2UzMDEifQ"
},
"links": {
"next": "https://api.sockodds.com/v1/odds/history?event_id=ev_000ckk0zrx33&from=2026-10-15T00%3A00%3A00Z&to=2026-10-15T08%3A00%3A00Z&bookmakers=sportsbet&limit=4&cursor=eyJhdCI6IjIwMjYtMTAtMTVUMDE6NTg6MDAuMDAwWiIsImlkIjoiNTAzIiwicmV2aXNpb24iOiIxMDA1IiwiZiI6IjJkMTc0OGRkZDY4Y2UzMDEifQ"
}
}GET/history/files
Operation listHistoryFiles · History
Pro and Internal only. Whole days of history as Parquet files, one file per league and UTC day, from 22 December 2025 up to two days ago. Each file holds the rows /v1/odds/history returns for that league and day, sorted by event and observation time. Own observations only. Listing bills one object per file listed.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/history/files?league=nfl&limit=2" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/history/files?league=nfl&limit=2", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/history/files?league=nfl&limit=2",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"league": {
"key": "nfl",
"name": "NFL"
},
"day": "2026-10-12",
"rows": 96,
"bytes": 3832,
"sha256": "efea29cc589a3a93bd5bead1d7de02a09be13574441721cebdb0498c8514ecd2",
"observed_from": "2026-10-12T00:00:00.000000Z",
"observed_to": "2026-10-12T06:00:00.000000Z",
"url": "https://api.sockodds.com/v1/history/files/nfl/2026-10-12"
}
],
"meta": {
"count": 1,
"cursor": null
},
"links": {
"next": null
}
}GET/history/files/{league}/{day}
Operation getHistoryFile · History
Pro and Internal only. Downloads one day of history as a zstd Parquet file. Columns: event_id, market_id, market_key, market_name, period, subject_key, subject_id, subject_name, outcome_id, outcome_name, side, line, bookmaker, observed_at (UTC, milliseconds), kind, decimal_odds, implied_probability. The ETag is the file's SHA-256. At most two downloads at a time per key. Each download bills one object. A file that names a player whose identity came under review after it was built answers 503 until it is rebuilt overnight.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | path | Yes | Key (string) | See the OpenAPI schema for allowed values. |
day | path | Yes | string (date) | UTC day as YYYY-MM-DD. |
curl -sS -o nfl-2026-10-12.parquet -w "%{http_code} %{content_type} %{size_download}\n" "https://api.sockodds.com/v1/history/files/nfl/2026-10-12" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.sockodds.com/v1/history/files/nfl/2026-10-12", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
const body = Buffer.from(await response.arrayBuffer());
await writeFile("nfl-2026-10-12.parquet", body);
console.log(`${response.status} ${response.headers.get("content-type")} ${body.length}`);import os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/history/files/nfl/2026-10-12",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
body = response.read()
with open("nfl-2026-10-12.parquet", "wb") as out:
out.write(body)
print(response.status, response.headers["Content-Type"], len(body))Output from a local copy of the API with invented test prices
200 application/vnd.apache.parquet 3832GET/results
Operation listResults · Results
Settled outcomes for finished events, graded from official result sources. Each outcome's verdict is won, lost, void (stake returned), dead_heat, half_won or half_lost. half_won / half_lost settle an Asian quarter line (x.25 / x.75) whose two half stakes split: one half won (or lost) at the quoted price and the other half refunded. A dead heat pays dead_heat_factor of the stake at the quoted price and loses the rest: a drawn AFL or AFLW head-to-head, a drawn AFLW quarter or half winner, a tied NBL quarter or half winner, and a Rugby League World Cup head-to-head still level after golden point, is a dead heat with a factor of 0.5. Outcomes that official evidence cannot settle are left out, never guessed. Each market settles on its own once official evidence settles all of its outcomes, so a result can be published while other markets on the event are still pending: settlement is partial and pending_markets counts them, and later results for the same event add the newly settled outcomes without changing any verdict already published.
| Parameter | In | Required | Type | Meaning |
|---|---|---|---|---|
league | query | No | Key (string) | See the OpenAPI schema for allowed values. |
event_id | query | No | EventId (string) | Restrict results to one independent event identity. |
starts_after | query | No | string (date-time) | See the OpenAPI schema for allowed values. |
starts_before | query | No | string (date-time) | See the OpenAPI schema for allowed values. |
cursor | query | No | string | See the OpenAPI schema for allowed values. |
limit | query | No | integer (minimum 1; maximum 100; default 25) | See the OpenAPI schema for allowed values. |
curl -sS "https://api.sockodds.com/v1/results?league=nfl&limit=1" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/results?league=nfl&limit=1", {
headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));import json, os, urllib.request
request = urllib.request.Request(
"https://api.sockodds.com/v1/results?league=nfl&limit=1",
headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))Response from a local copy of the API with invented test prices
{
"data": [
{
"event": {
"id": "ev_000ckk0h3n6r",
"name": "Arizona Cardinals v Atlanta Falcons",
"league": {
"key": "nfl",
"name": "NFL"
},
"scheduled_start": "2026-10-12T08:00:00.000Z",
"status": "final",
"participants": [
{
"role": "away",
"identity": {
"key": "atlanta_falcons",
"name": "Atlanta Falcons"
}
},
{
"role": "home",
"identity": {
"key": "arizona_cardinals",
"name": "Arizona Cardinals"
}
}
]
},
"status": "final",
"settlement": "partial",
"pending_markets": 1,
"settled_at": "2026-10-12T12:00:00.000Z",
"outcomes": [
{
"id": "oc_1xh21t2rzp7ss665",
"name": "Atlanta Falcons",
"verdict": "lost",
"market": {
"id": "mk_s4fv79z47rbkwev7",
"key": "winner",
"name": "Winner",
"period": "full",
"subject": null
},
"side": "away",
"line": null
},
{
"id": "oc_53f4yzaqeyex1xhg",
"name": "Arizona Cardinals",
"verdict": "won",
"market": {
"id": "mk_s4fv79z47rbkwev7",
"key": "winner",
"name": "Winner",
"period": "full",
"subject": null
},
"side": "home",
"line": null
},
{
"id": "oc_7txwsxyatthp1bqb",
"name": "Arizona Cardinals",
"verdict": "won",
"market": {
"id": "mk_5tz5q3wkht6h0pme",
"key": "handicap",
"name": "Handicap",
"period": "full",
"subject": null
},
"side": "home",
"line": -3.5
},
{
"id": "oc_yxchy92ev1h1abxs",
"name": "Atlanta Falcons",
"verdict": "lost",
"market": {
"id": "mk_5tz5q3wkht6h0pme",
"key": "handicap",
"name": "Handicap",
"period": "full",
"subject": null
},
"side": "away",
"line": 3.5
}
]
}
],
"meta": {
"count": 1,
"cursor": null
},
"links": {
"next": null
}
}Objects
Every object the API returns, generated from the contract. Required fields are always present; a field typed with null may be null.
Meta
| Field | Required | Type | Meaning |
|---|---|---|---|
count | Yes | integer (minimum 0) | |
cursor | Yes | string or null |
Links
| Field | Required | Type | Meaning |
|---|---|---|---|
next | Yes | string or null (uri-reference) |
NamedKey
| Field | Required | Type | Meaning |
|---|---|---|---|
key | Yes | Key (string) | |
name | Yes | string |
League
| Field | Required | Type | Meaning |
|---|---|---|---|
key | Yes | Key (string) | |
name | Yes | string | |
sport | No | Key (string) | |
region | No | Key (string) |
Bookmaker
| Field | Required | Type | Meaning |
|---|---|---|---|
key | Yes | Key (string) | |
name | Yes | string | |
regions | No | array of Key (string) | |
kind | No | string (one of sportsbook, exchange, prediction_market) |
Sport
| Field | Required | Type | Meaning |
|---|---|---|---|
key | Yes | Key (string) | |
name | Yes | string | |
participants | Yes | string (one of teams, competitors) | Who contests an event. Team sports use the participant roles home and away; individual sports (tennis |
MarketDescriptor
| Field | Required | Type | Meaning |
|---|---|---|---|
key | Yes | Key (string) | |
name | Yes | string | |
subject | No | string (one of event, team, player) | |
periods | No | array of Key (string) | |
ungraded_periods | No | array of Key (string) | Periods this market is quoted for but not yet graded, because no results source supplies the statistic yet. Results for quotes in these periods stay pending. Listed where audited; its absence is not a guarantee that a period grades. It is per market across all its sports, so a period is listed if it is ungraded in any of them. |
sports | No | array of Key (string) | |
non_participant | No | array of NonParticipant (one of void, loses, under, fair_price) | Player markets only. The non-participation rules a price on this market may carry (see the non_participant of each price in the odds routes). Every player-market price carries one. |
sides | No | array of Key (string) or string (one of participants, scores) | Side keys, or participants when each outcome is a player runner, or scores for exact-score outcomes. A market offers home and away only in team sports and competitor_1 and competitor_2 only in tennis and UFC. |
Team
| Field | Required | Type | Meaning |
|---|---|---|---|
key | Yes | Key (string) | |
name | Yes | string | |
sport | Yes | Key (string) | |
leagues | No | array of Key (string) |
Player
| Field | Required | Type | Meaning |
|---|---|---|---|
id | Yes | PlayerId (string) | |
name | Yes | string | |
sport | Yes | Key (string) | |
team | Yes | NamedKey or null |
Participant
| Field | Required | Type | Meaning |
|---|---|---|---|
role | Yes | string (one of home, away, competitor_1, competitor_2) | home and away in team sports. In tennis and UFC |
identity | Yes | NamedKey or object |
Event
| Field | Required | Type | Meaning |
|---|---|---|---|
id | Yes | EventId (string) | |
name | Yes | string | |
league | Yes | NamedKey | |
scheduled_start | Yes | string (date-time) | |
cricket_format | No | string (one of odi, test) | Official format |
status | Yes | string (one of scheduled, live, final, postponed, cancelled) | |
participants | Yes | array of Participant |
EventLookup
| Field | Required | Type | Meaning |
|---|---|---|---|
source | Yes | OfficialSource (one of espn, afl, nrl) | |
ref | Yes | OfficialRef (string) | |
event | Yes | Event |
ExchangeContract
Native buy contract underlying the enclosing rounded decimal odds. Available is a native quantity; price is the fraction of payout per share/contract. Buy cost equals price times available times payout_per_contract before fees; omitted payout means 1. Novig pays USD 0.01 per contract, Fanatics Markets USD 1. An opposite contract is not a lay.
| Field | Required | Type | Meaning |
|---|---|---|---|
selection_ref | Yes | string | |
side | Yes | string | |
unit | Yes | string (one of contracts, shares) | |
price | Yes | number (greater than 0; less than 1) | |
available | No | number (minimum 0) | |
payout_per_contract | No | number (one of 0.01, 1) | Native monetary payout in liquidity.currency per contract; omitted means 1 for existing feeds. |
ExchangeLiquidity
Verified native exchange depth for this outcome, line and quoted back price, at the enclosing price's observed_at. Available amounts are resting stake, not guaranteed account limits or lay liability. Absent when not reported or the current price is unavailable.
| Field | Required | Type | Meaning |
|---|---|---|---|
market_ref | No | string | Native exchange market reference; joins the market-level exchanges entry. |
offer_ref | No | string | BetOpenly offer identifier when the feed supplies no native market ID. |
contract | No | ExchangeContract | |
currency | Yes | string or null (pattern ^[A-Z]{3}$) | Capture currency when verified; null means unknown. Never infer currency from bookmaker region. |
available_to_back | No | number (minimum 0) | Unrounded amount available at the enclosing decimal_odds. |
lay | No | object | |
lay.decimal_odds | Yes | number (greater than 1) | Best lay price for this same selection and line. |
lay.available | No | number (minimum 0) | Resting stake available at this lay price |
ExchangeMarket
One native exchange market's reported activity. Missing matched_volume means unreported, never zero. Kalshi and Novig traded_contracts is a count, not money. Never sum runner volumes. A native market can span canonical lines; the same market_ref total may therefore appear under multiple lines and must not be added together. Current observations only; no liquidity history or opening/closing volume is promised.
| Field | Required | Type | Meaning |
|---|---|---|---|
bookmaker | Yes | Key (string) | |
market_ref | Yes | string | |
matched_volume | No | number (minimum 0) | Verified monetary volume from the native market |
traded_contracts | No | number (minimum 0) | Kalshi volume_fp or Novig native market volume in contracts. This is not matched money. |
currency | Yes | string or null (pattern ^[A-Z]{3}$) | |
observed_at | Yes | string (date-time) |
Outcome
| Field | Required | Type | Meaning |
|---|---|---|---|
liquidity | No | ExchangeLiquidity | |
id | Yes | OutcomeId (string) | |
name | Yes | string | |
side | Yes | string | A catalogue side key or player runner ID. Team sports name participants home and away; tennis and UFC name them competitor_1 and competitor_2 |
line | Yes | number or null | The outcome's own line. On a handicap each outcome carries its own figure: home -6.5 with away +6.5, and in tennis competitor_1 -2.5 with competitor_2 +2.5 (games). Null when the market has no line. |
decimal_odds | Yes | number or null (greater than 1) | |
implied_probability | Yes | number or null (greater than 0; maximum 1) | |
available | No | boolean | Whether the current quote is available and fresh. Stale or withdrawn quotes appear only when snapshots are requested and carry null current prices. |
alternate | Yes | boolean | |
non_participant | No | NonParticipant (one of void, loses, under, fair_price) | Player markets only, and only when the rule is not void: this shape stays byte for byte the launch body, so an absent value on a player market means void. Grouped by market, every player price carries it. |
bookmaker | Yes | Key (string) | |
observed_at | Yes | string (date-time) | |
opening | No | PriceSnapshot or null | The first quote before the snapshots cutoff, skipping withdrawals. Null when there is none. |
closing | No | PriceSnapshot or null | The last quote before the snapshots cutoff. Null when there is none. For every sport but MMA it is also null when that last row is a withdrawal (the book pulled the price before scheduled_start); it never falls back to an earlier price. For an MMA bout withdrawals are skipped, so closing is the last price offered before the cutoff. |
PriceSnapshot
| Field | Required | Type | Meaning |
|---|---|---|---|
decimal_odds | Yes | number (greater than 1) | |
implied_probability | Yes | number (greater than 0; maximum 1) | |
observed_at | Yes | string (date-time) |
QuotedMarket
| Field | Required | Type | Meaning |
|---|---|---|---|
exchanges | No | array of ExchangeMarket | |
id | Yes | MarketId (string) | |
key | Yes | Key (string) | |
name | Yes | string | |
subject | Yes | null or NamedKey or object | |
period | Yes | Key (string) | |
outcomes | Yes | array of Outcome |
Price
One bookmaker's current price for an outcome.
| Field | Required | Type | Meaning |
|---|---|---|---|
liquidity | No | ExchangeLiquidity | |
bookmaker | Yes | Key (string) | |
decimal_odds | Yes | number or null (greater than 1) | |
implied_probability | Yes | number or null (greater than 0; maximum 1) | |
available | Yes | boolean | Whether the quote is available and fresh. Stale or withdrawn quotes appear only when snapshots are requested and carry null current prices. |
opening | No | PriceSnapshot or null | The first quote before the snapshots cutoff, skipping withdrawals. Null when there is none. |
closing | No | PriceSnapshot or null | The last quote before the snapshots cutoff. Null when there is none. For every sport but MMA it is also null when that last row is a withdrawal (the book pulled the price before scheduled_start); it never falls back to an earlier price. For an MMA bout withdrawals are skipped, so closing is the last price offered before the cutoff. |
non_participant | No | NonParticipant (one of void, loses, under, fair_price) | |
observed_at | Yes | string (date-time) |
PricedOutcome
| Field | Required | Type | Meaning |
|---|---|---|---|
id | Yes | OutcomeId (string) | |
name | Yes | string | |
side | Yes | string | A catalogue side key or player runner ID |
line | Yes | number or null | The outcome's own line: home -6.5 with away +6.5. Null when the market has no line. |
prices | Yes | array of Price | Every bookmaker price for this outcome, best first: highest decimal odds, then bookmaker key. prices[0] is the best price among the bookmakers asked for. A price with no current odds (snapshots only) comes last. |
PricedMarket
| Field | Required | Type | Meaning |
|---|---|---|---|
exchanges | No | array of ExchangeMarket | |
id | Yes | MarketId (string) | |
key | Yes | Key (string) | |
name | Yes | string | |
period | Yes | Key (string) | |
subject | Yes | null or NamedKey or object | |
alternate | Yes | boolean | True when this line is not the market's main line. Alternate lines appear only with alternates=true. |
outcomes | Yes | array of PricedOutcome | In the market's side order (home |
QuotedBookmaker
| Field | Required | Type | Meaning |
|---|---|---|---|
key | Yes | Key (string) | |
name | Yes | string | |
markets | Yes | array of QuotedMarket |
OddsEvent
An event with its prices grouped by market (the default). Markets come in catalogue order, then period, subject and line.
| Field | Required | Type | Meaning |
|---|---|---|---|
id | Yes | EventId (string) | |
name | Yes | string | |
league | Yes | NamedKey | |
scheduled_start | Yes | string (date-time) | |
cricket_format | No | string (one of odi, test) | Official format |
status | Yes | string (one of scheduled, live, final, postponed, cancelled) | |
participants | Yes | array of Participant | |
markets | Yes | array of PricedMarket |
OddsEventByBookmaker
An event with its prices grouped by bookmaker (group_by=bookmaker), the shape v1 launched with.
| Field | Required | Type | Meaning |
|---|---|---|---|
id | Yes | EventId (string) | |
name | Yes | string | |
league | Yes | NamedKey | |
scheduled_start | Yes | string (date-time) | |
cricket_format | No | string (one of odi, test) | Official format |
status | Yes | string (one of scheduled, live, final, postponed, cancelled) | |
participants | Yes | array of Participant | |
bookmakers | Yes | array of QuotedBookmaker |
RecordedMarket
| Field | Required | Type | Meaning |
|---|---|---|---|
id | Yes | MarketId (string) | |
key | Yes | Key (string) | |
name | Yes | string | |
period | Yes | Key (string) | |
subject | Yes | NamedKey or object or null |
HistoryChange
| Field | Required | Type | Meaning |
|---|---|---|---|
event | Yes | EventId (string) | |
market | Yes | RecordedMarket | |
outcome | Yes | object | |
outcome.id | Yes | OutcomeId (string) | |
outcome.name | Yes | string | |
outcome.side | Yes | string | |
bookmaker | Yes | Key (string) | |
observed_at | Yes | string (date-time) | |
kind | Yes | string (one of opened, changed, withdrawn) | |
decimal_odds | Yes | number or null | |
implied_probability | No | number or null | |
line | No | number or null |
HistoryFile
| Field | Required | Type | Meaning |
|---|---|---|---|
league | Yes | NamedKey | |
day | Yes | string (date) | |
rows | Yes | integer (minimum 1) | |
bytes | Yes | integer (minimum 1) | |
sha256 | Yes | string (pattern ^[0-9a-f]{64}$) | |
observed_from | Yes | string (date-time) | |
observed_to | Yes | string (date-time) | |
url | Yes | string (uri) |
Result
| Field | Required | Type | Meaning |
|---|---|---|---|
event | Yes | Event | |
status | Yes | string (one of final, corrected) | |
settlement | Yes | string (one of complete, partial) | complete when every quoted market on the event is settled; partial while any market is still pending. |
pending_markets | Yes | integer (minimum 0) | How many quoted markets on the event are still pending. A market is pending until official evidence settles every one of its outcomes. |
settled_at | Yes | string (date-time) | |
outcomes | Yes | array of object | |
outcomes[].id | Yes | OutcomeId (string) | |
outcomes[].name | Yes | string | |
outcomes[].verdict | Yes | string (one of won, lost, void, dead_heat, half_won, half_lost) | half_won / half_lost: an Asian quarter line (x.25 / x.75) whose two half stakes split: one half won (or lost) at the quoted price and the other half refunded. |
outcomes[].dead_heat_factor | When verdict is dead_heat | number (greater than 0; less than 1) | Dead heats only: the share of the stake paid at the quoted price. The rest of the stake is lost. |
outcomes[].market | Yes | RecordedMarket | |
outcomes[].side | Yes | string | |
outcomes[].line | Yes | number or null |
Problem
| Field | Required | Type | Meaning |
|---|---|---|---|
type | Yes | string (uri-reference) | |
title | Yes | string | |
status | Yes | integer | |
detail | Yes | string | |
instance | Yes | string (uri-reference) |
Identifier formats
| Name | Format |
|---|---|
Key | string (pattern ^[a-z][a-z0-9_]{1,47}$) |
EventId | string (pattern ^ev_[0-9a-hjkmnp-tv-z]{12}$) |
PlayerId | string (pattern ^pl_[0-9a-hjkmnp-tv-z]{12}$) |
MarketId | string (pattern ^mk_[0-9a-hjkmnp-tv-z]{16}$) |
OutcomeId | string (pattern ^oc_[0-9a-hjkmnp-tv-z]{16}$) |
OfficialSource | string (one of espn, afl, nrl) |
OfficialRef | string (pattern ^[A-Za-z0-9][A-Za-z0-9:~._-]{0,63}$) |
NonParticipant | string (one of void, loses, under, fair_price) |