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.

RouteDefault limitLargest limit
/v1/leagues, /v1/bookmakers, /v1/markets, /v1/teams, /v1/players, /v1/events, /v1/odds, /v1/results25100
/v1/odds/history2501,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

FieldMeaning
meta.countHow many items this page holds. It can be less than limit, on the last page or when a filter leaves fewer.
meta.cursorAn opaque cursor for the next page, or null when this is the last page.
links.nextThe 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"
  }
}

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 of links.next exactly 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 limit you 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.
Need help?FAQ · Email support

Search across the SockOdds documentation and catalogue.