Tokens, quotes and rate limits
How a call is priced, how filters lower the price, and how to handle a 402 or a 429.
A call is priced from the request
Each account has one token pool: a paid plan's resets every month, and the free plan gives 50,000 tokens for 14 days. A call's price comes from the product and the filters you send, never from the size of the reply, except a historical odds day file, priced per MB. GET /v2/pricing publishes the table, and /pricing on the site explains it with worked examples.
On standard, dutching and raw, each filter narrows the price: every sport, bookmaker, exchange or market you name carries points, and the price is multiplied by the named points over 100, never above 1. On dutching and raw the bookmakers and exchanges you name count together as one filter. The four smaller products cost the same whatever you filter. The result is rounded up, and a call never costs less than 1 token.
On raw, one price covers the whole walk: each page pays its share of the query's price. An updatedSince no more than 120 s back pays 10% of that price.
Quote before a big call
Add quote=true to any data request. It returns the price instead of the odds and costs nothing. A quote checks your key and parameters but not your balance, so a quote can succeed where the real call returns 402 (or 503 if the product isn't ready).
curl --compressed -s -H "Authorization: Bearer $ODDSRELAY_KEY" \
"https://api.oddsrelay.io/v2/odds/standard?region=uk&sports=soccer"e=true"
# {"cost": …, "remaining": …, "rate_card": "…"}What is free
Discovery (/v2/sports, /v2/events, /v2/bookmakers, /v2/regions), /v2/usage, /v2/pricing, the historical odds files list (/v2/historical/files), quotes, 304 replies, replies with no events, and every refusal.
Read the token headers
Every reply to a valid key carries X-Tokens-Cost. X-Tokens-Used, X-Tokens-Remaining, X-Tokens-Limit and X-Tokens-Reset come with it whenever the balance is known; Remaining and Limit read unlimited on an unlimited account.
On a 402
402 insufficient_ means the price is above the balance, and nothing was served or spent. The body carries cost, remaining, resets_ (on the free plan's window or a trial, its end when that comes first), plan, rate_, quote and, except on a custom Enterprise plan, upgrade_. Narrow the filters or change plan. On a paid plan, waiting for resets_ also works; on the free plan's window it does not, because no tokens follow its end. Retrying the same call won't help, and a 402 never carries Retry-After.
On a 429
429 too_ means the account's rate limit, per 10 seconds or per minute, or its cap on requests in flight. 429 too_ is the per-address limit before authentication. 429 too_ is the historical odds download cap: one file download at a time per account. Wait for the Retry-After seconds, then retry.
Our edge also limits each address and the whole service. Its 429s carry Retry-After with an empty body and no code, as does its 502. Handle an empty-bodied 429 the same way, and back off on a 502.
From the blog
- Export betting odds to CSV in PythonPut each back and lay offer on its own row and a spreadsheet can filter by side, venue and depth.
- How to build an odds screen in PythonCeltic arrives under two spellings and Ferencváros under three, so a screen keyed on names splits rows.
- Odds API in Python: an oddsmatcher in 50 linesPython's urllib asks for an uncompressed reply by default, and the standard board answers that with a 400.
Every parameter: API reference.