Pagination
Ask for a page size, then follow links.next until it is null.
How a list is paged
Every list route returns one page at a time. You choose the page size with limit, and the response tells you where the next page is.
| Route | Default limit | Largest limit |
|---|---|---|
/v1/leagues, /v1/bookmakers, /v1/markets, /v1/teams, /v1/players, /v1/events, /v1/odds, /v1/results | 25 | 100 |
/v1/odds/history | 250 | 1,000 |
On /v1/odds the limit counts events, and each event carries all of its prices. /v1/sports is short enough to arrive on one page. A limit outside the allowed range is refused with 400.
What a page tells you
| Field | Meaning |
|---|---|
meta.count | How many items this page holds. It can be less than limit, on the last page or when a filter leaves fewer. |
meta.cursor | An opaque cursor for the next page, or null when this is the last page. |
links.next | The full URL of the next page: your request with the cursor added. null on the last page. |
Here is the first page of the NFL's markets, two at a time:
curl -sS "https://api.sockodds.com/v1/markets?league=nfl&limit=2" \
-H "Authorization: Bearer $SOCKODDS_API_KEY"const response = await fetch("https://api.sockodds.com/v1/markets?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/markets?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": [
{
"key": "total_points_odd_even",
"name": "Total points odd/even",
"subject": "event",
"bet": "odd_even",
"stat": "points",
"has_line": false,
"sides": [
"odd",
"even"
],
"sports": [
"american_football"
],
"periods": [
"full",
"half_1",
"half_2",
"quarter_1",
"quarter_2",
"quarter_3",
"quarter_4"
]
},
{
"key": "team_points_odd_even",
"name": "Team points odd/even",
"subject": "team",
"bet": "odd_even",
"stat": "points",
"has_line": false,
"sides": [
"odd",
"even"
],
"sports": [
"american_football"
],
"periods": [
"full"
]
}
],
"meta": {
"count": 2,
"cursor": "eyJuIjoyLCJmIjoiOWY5N2IyMTM5MzZhNjRmOSJ9"
},
"links": {
"next": "https://api.sockodds.com/v1/markets?league=nfl&limit=2&cursor=eyJuIjoyLCJmIjoiOWY5N2IyMTM5MzZhNjRmOSJ9"
}
}Follow links.next unchanged
To read the next page, request links.next exactly as it came back. Keep going until links.next is null.
- Do not build the next URL yourself, and do not decode or edit the cursor. Its format is not part of the contract and can change.
- A cursor belongs to the query that produced it. Change a filter or the limit and the cursor no longer fits: the API answers 400 with
Invalid cursor. Start again from the first page instead. - Send your key only to
https://api.sockodds.com. The programs below keep the path and query oflinks.nextexactly as given and always send them to the API's own address, so the key can never be sent anywhere else.
Read every page
This program reads every NFL market, ten at a time, and says how many pages and markets it read.
const API = "https://api.sockodds.com";
const headers = { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` };
let url = `${API}/v1/markets?league=nfl&limit=10`;
let pages = 0;
let markets = 0;
while (url) {
const response = await fetch(url, { headers });
const body = await response.json();
if (!response.ok) throw new Error(`${body.status} ${body.title}: ${body.detail}`);
pages += 1;
markets += body.meta.count;
console.log(`Page ${pages}: ${body.meta.count} markets`);
// links.next is null on the last page. Keep its path and query exactly as given,
// and send them only to the SockOdds API.
const next = body.links.next ? new URL(body.links.next) : null;
url = next ? API + next.pathname + next.search : null;
}
console.log(`Read ${markets} markets in ${pages} ${pages === 1 ? "page" : "pages"}.`);import json, os, urllib.request
from urllib.parse import urlsplit
API = "https://api.sockodds.com"
headers = {"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]}
url = API + "/v1/markets?league=nfl&limit=10"
pages = 0
markets = 0
while url:
with urllib.request.urlopen(urllib.request.Request(url, headers=headers)) as response:
body = json.load(response)
pages += 1
markets += body["meta"]["count"]
print(f"Page {pages}: {body['meta']['count']} markets")
# links.next is null on the last page. Keep its path and query exactly as given,
# and send them only to the SockOdds API.
next_link = body["links"]["next"]
if next_link:
parts = urlsplit(next_link)
url = API + parts.path + ("?" + parts.query if parts.query else "")
else:
url = None
print(f"Read {markets} markets in {pages} {'page' if pages == 1 else 'pages'}.")Output from a local copy of the API with invented test prices
Page 1: 10 markets
Page 2: 9 markets
Read 19 markets in 2 pages.Tips
- Use the largest
limityou can handle when you want everything: fewer pages means fewer requests against your per-minute limit. See Limits. - Lists of events change as games are added, start and finish. Each page is read when you ask for it, so if the list changes during a long walk an event can appear on two pages or move past you. Key what you store by event
id, and poll again rather than treating one walk as a frozen copy. - History pages work the same way, with their own limits. See History and snapshots.