Odds API · 11 October 2026 · updated 14 October 2026

Odds API Rate Limits: Credits, Objects and Requests Compared

A rate limit caps speed and a quota caps volume. The unit the quota is counted in decides which habits save you money.

Odds API rate limits come in two layers. A rate limit caps how fast you can call (say 50 requests a minute) and returns HTTP 429 when you go over. A usage quota caps how much you can take in a month, and providers count it in different units: credits per call, objects per item returned, or plain requests per hour. The unit matters more than the headline number. In the worked example below, the same change to a polling schedule cuts usage by more than 100 times under one meter and by about 19 times under another.

This guide lays out the three common ways odds APIs meter usage, shows exactly how SockOdds counts, then works through a monthly budget for a realistic polling schedule.

Rate limit versus usage quota

Rate limitUsage quota
What it capsSpeedVolume
Typical windowPer second, minute or hourPer calendar month
What you see when you go overHTTP 429, often with Retry-After429, 402 or overage billing, depending on provider
How to stay underSpace out calls, back off on 429Ask for less of what you don't need

MDN describes 429 Too Many Requests as the status for a client that "has sent too many requests in a given amount of time". The Retry-After header "indicates how long the user agent should wait before making a follow-up request". Read it and sleep for that long, rather than retrying in a loop.

For small apps the rate limit is rarely the problem. Polling a league every two minutes is one request every two minutes. The monthly quota is where people get caught.

Three ways odds APIs count usage

Credits per call, priced by what you ask for

Some APIs charge each call in credits worked out from its parameters, not from how many games come back: typically the number of markets you ask for times the number of regions. Head to head, line and total in one region is 3 credits, whether the call returns 2 games or 20. History calls often cost a multiple of the live price, and some of these APIs charge nothing for a call that returns no events. A variant fixes a credit cost per endpoint instead, from nothing for a catalogue call to a large cost for a bulk export.

What this rewards: fetching every event in one call, and asking for fewer markets and regions.

Objects per item returned

SockOdds counts objects, as Limits sets out. Each record in data is one object, and every bookmaker price inside it is one more. One event with three bookmakers each quoting a two-way market is 1 + 3 x 2 = 7 objects. Catalogue lists (sports, leagues, bookmakers, markets, teams, players) count one object per row.

SockOdds planRequests a minuteObjects a month
Developer (free)102,500
Rookie50100,000
Pro300No monthly object cap

For more than Pro, contact support about Platform access. A few details from the docs that change how you write a client:

  • Every request counts towards the per-minute limit, including ones refused with 400 or 403. Errors are not charged as objects.
  • Over the per-minute limit you get 429 titled "Rate limit reached", with a Retry-After header in seconds.
  • A request for a league outside your plan gets 403 titled "Plan restriction". Bookmakers outside your plan are simply left out of the response.
  • A filter that matches nothing returns 200 with an empty data list, so there are no records to count. It still counts towards the minute.

What this rewards: asking only for what you show. The number of events matters, and so does the number of outcomes inside each one.

Requests per hour, priced by bookmaker

Other APIs keep the meter simple: a flat number of requests an hour on every paid plan, with the price rising with the number of bookmakers you choose. Extra hourly capacity is sometimes sold as an add-on.

What this rewards: choosing your bookmakers carefully. With a generous hourly allowance, call volume is rarely the constraint.

For how named providers meter usage and what they charge, see our comparison pages.

What happens when you run out

Going over the monthly quota is handled differently again:

  • A hard stop. Some APIs answer 402 Payment Required at the quota and never charge for the overflow.
  • Overage billing. Others keep serving and bill the extra, often up to a cap.
  • A visible meter. Some send the credits remaining on every response, so you can see the limit coming.
  • SockOdds: once the Developer or Rookie allowance is used, requests get 429 titled "Monthly allowance reached" until the next calendar month (UTC), or until you change plan. That 429 has no Retry-After, and retrying will not help. Pro has no monthly object cap. Usage so far is on your account page; responses carry no usage headers.

If your app runs unattended, keep your own count of what each response used and slow down before you reach the limit, rather than finding out from an error at 2am on a Saturday.

Worked example: a monthly polling budget

Say you track 9 matches a week, 4 weeks a month (36 matches), with head to head, line and total markets from nine Australian bookmakers. Assume every bookmaker quotes all three markets, so each event carries 9 x 3 x 2 = 54 bookmaker prices: 55 objects, or 19 with the head to head only. To keep the sums simple, assume each league-wide call returns all 9 matches. Compare three schedules over a 30-day month.

Flat: poll every event every 2 minutes, around the clock, all three markets. That is 720 calls a day, or 21,600 a month.

By time: poll every event every 30 minutes as a baseline (48 calls a day, 1,440 a month), then poll each match on its own every 2 minutes for the 3 hours before it starts (90 calls per match, 3,240 a month). That is 4,680 calls.

By time and size: poll every event every 6 hours for the head to head only (4 calls a day, 120 a month), then poll each match on its own every 2 minutes for its last hour, all three markets (30 calls per match, 1,080 a month). That is 1,200 calls.

Why 2 minutes in the fast window? SockOdds does not promise a refresh interval, so log observed_at on the prices you use and see how often they actually change. Two minutes is an example, not a rule.

ScheduleCalls a monthEvents returnedObjectsCredits (markets x 1 region)
Flat21,600194,40010,692,00064,800
By time4,68016,200891,00014,040
By time and size1,2002,16079,9203,360
Flat divided by time and size18x90x134x19x

The working, so you can swap in your own numbers:

  1. Flat: 21,600 calls x 9 events = 194,400 events, x 55 = 10,692,000 objects; 21,600 x 3 = 64,800 credits.
  2. By time: (1,440 x 9) + (3,240 x 1) = 16,200 events, x 55 = 891,000 objects; 4,680 x 3 = 14,040 credits.
  3. By time and size: (120 x 9 x 19) + (1,080 x 1 x 55) = 20,520 + 59,400 = 79,920 objects; (120 x 1) + (1,080 x 3) = 3,360 credits.

What the table shows:

  • Under object metering, the size of each call counts as much as the number. The fast window asks for one event, and the baseline asks for one market. Those two changes are most of the 134x gap.
  • Under per-call credits, the fast window should ask for everything. Narrowing to one event saves nothing, so you may as well take all 9 in the same 3 credits. What saves credits is calling less often and asking for fewer markets and regions.
  • Ask for a window. Without starts_after or starts_before, a SockOdds list starts 48 hours ago and includes games that have finished. Each of those is still an object, even with no prices left on it.
  • The rate limit never comes into it. Even with three matches starting together, the last schedule peaks at under two calls a minute, well under even the Developer plan's 10.

The flat and by-time schedules need far more than Rookie's 100,000 objects, which means Pro. The by-time-and-size schedule fits inside Rookie. Same app, same data on screen at kickoff, different plan.

Count what each call used

SockOdds sends no usage headers, so the simplest meter is your own. This program counts the objects two requests for the same NFL games use, projects each across a month of polling every 2 minutes, and retries politely if it is ever told to slow down:

const headers = { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` };
const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));

// Retry only "Rate limit reached", after the Retry-After seconds. "Monthly allowance reached" will not clear.
async function get(url) {
  for (let attempt = 1; ; attempt += 1) {
    const response = await fetch(url, { headers });
    const body = await response.json();
    if (response.status !== 429 || body.title !== "Rate limit reached" || attempt === 3) return body;
    await sleep(Number(response.headers.get("retry-after")) || 1);
  }
}

// One object per event in data, plus one per bookmaker price inside it.
function objects(body) {
  let count = 0;
  for (const event of body.data) {
    count += 1;
    for (const market of event.markets) {
      for (const outcome of market.outcomes) count += outcome.prices.length;
    }
  }
  return count;
}

const base = "https://api.sockodds.com/v1/odds?league=nfl&limit=5";
for (const [label, query] of [
  ["All markets, all bookmakers", ""],
  ["Head to head, Sportsbet only", "&markets=winner&bookmakers=sportsbet"],
]) {
  const body = await get(base + query);
  const used = objects(body);
  const month = used * 21600; // every 2 minutes for 30 days
  console.log(`${label}: ${body.data.length} event(s), ${used} objects a call, ${month.toLocaleString("en-AU")} a month`);
}
import json, os, time, urllib.error, urllib.request

headers = {"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]}


# Retry only "Rate limit reached", after the Retry-After seconds. "Monthly allowance reached" will not clear.
def get(url):
    for attempt in range(1, 4):
        try:
            with urllib.request.urlopen(urllib.request.Request(url, headers=headers)) as response:
                return json.load(response)
        except urllib.error.HTTPError as error:
            body = json.load(error)
            if error.code != 429 or body.get("title") != "Rate limit reached" or attempt == 3:
                return body
            time.sleep(int(error.headers.get("Retry-After") or 1))


# One object per event in data, plus one per bookmaker price inside it.
def objects(body):
    count = 0
    for event in body["data"]:
        count += 1
        for market in event["markets"]:
            for outcome in market["outcomes"]:
                count += len(outcome["prices"])
    return count


base = "https://api.sockodds.com/v1/odds?league=nfl&limit=5"
for label, query in [
    ("All markets, all bookmakers", ""),
    ("Head to head, Sportsbet only", "&markets=winner&bookmakers=sportsbet"),
]:
    body = get(base + query)
    used = objects(body)
    month = used * 21600  # every 2 minutes for 30 days
    print(f"{label}: {len(body['data'])} event(s), {used} objects a call, {month:,} a month")

Output from a local copy of the API with invented test prices

All markets, all bookmakers: 1 event(s), 49 objects a call, 1,058,400 a month
Head to head, Sportsbet only: 1 event(s), 3 objects a call, 64,800 a month

The response comes from a local copy of the API with invented prices. Compare your running total with the usage on your account page now and then, and alert yourself at 80% of the month, not 100%.

Habits that keep any odds API client inside its limits

  1. Keep your own meter. Count what each response used, by whatever unit your provider bills, and alert at 80% of the month.
  2. Back off on 429. Honour Retry-After, add a little random jitter, and don't let several workers retry at the same instant. Don't retry a 429 that says the month is used up.
  3. Page properly. Ask for the page size you need (limit on /v1/odds goes up to 100 events) and follow links.next unchanged until it is null, as the pagination guide describes.
  4. Cache between polls. If two parts of your app want the same league, fetch once and share it. Read the catalogue (leagues, bookmakers, markets, teams) once a day, not on every request.
  5. Price history separately. On SockOdds, recorded history is a Pro feature: /v1/odds/history returns changes only, in windows of up to 24 hours, and daily files are also available. Pull a backfill once and store it rather than re-requesting it. See History and snapshots.

Questions to ask before you pick a plan

  • What is the unit: credits, objects, requests, or something else?
  • Does an empty response cost anything?
  • Do failed or rate-limited calls count?
  • What happens at the limit: a hard stop, a 402, or overage billing?
  • How do you see your usage, and when does the month reset?
  • Is history priced differently from live odds?

Questions people ask

What is the difference between a rate limit and a usage quota?

A rate limit caps how fast you can call, such as 50 requests a minute, and resets every minute or hour. A usage quota caps how much you can take in a month, counted in credits, objects or requests, and resets once a month.

Does an empty odds API response count against my quota?

It depends on the unit. Some credit-based APIs charge nothing when no events come back. On SockOdds, usage is counted per record returned, so an empty data list has nothing to count, though the request still counts towards your per-minute limit.

What happens when I hit an odds API rate limit?

Most providers answer with HTTP 429 Too Many Requests. SockOdds sends Retry-After with a 429 titled "Rate limit reached". A 429 titled "Monthly allowance reached" means the month's objects are used, and waiting a few seconds will not clear it.

Why does historical odds data use more quota?

Providers price history differently, and some charge a multiple of the live price. On SockOdds, history is part of the Pro plan, which has no monthly object cap, and /v1/odds/history returns only the changes in a window of up to 24 hours.

How do I check how much of my odds API quota I have used?

Some providers send usage headers on every response. SockOdds shows usage on your account page, and you can count objects yourself from each response: one per record, plus one per bookmaker price.

Sources

Next

This guide is for developers building data products. It is not betting advice and does not suggest any bet will win. 18+ only. Please gamble responsibly.

SockOdds provides data only. It does not offer tips or predictions on any game.

Search across the SockOdds documentation and catalogue.