openapi: 3.2.0 info: title: OddsRelay Odds API version: '2026-07-08' x-verified: '2026-10-08' description: 'The OddsRelay odds API: matched boards, the raw board, discovery, coverage and status, as served at `https://api.oddsrelay.io`.' servers: - url: https://api.oddsrelay.io security: - bearerAuth: [] - apiKeyHeader: [] tags: - name: Odds description: Matched boards, raw, one event. paths: /v2/odds/{type}: get: operationId: getMatchedBoard summary: A matched board description: 'The six matched boards: each event''s bookmaker back prices beside the exchanges'' lay prices (`dutching`: back legs that cover every outcome, grouped by `dutch_id`; `each-way` and `extra-place`: each-way terms against win and place lays). **Checks, in this order** (each refusal is free): the per-address shield, the key, the account''s flood cap, a suspended account, a browser key''s website, the API version; then `type` (404 `unknown_type`), `region`, the region in the key (403 `region_not_in_scope`), the product in the key (403 `not_in_key_scope`), the product in the region (403 `product_not_in_region`: a region with no exchange serves only dutching and raw); then the parameters: each one this route reads, once; `format`, `oddsFormat` and `dateFormat` values; on `standard` and `dutching` decimal ISO only and gzip accepted (400 `add Accept-Encoding: gzip; with curl use --compressed`); then the filters (400 unknown values, 403 `bookmaker_not_in_region`, then your account''s scope: 403 `sport_not_in_plan`, `market_not_in_plan`, `bookmaker_not_in_plan`, `exchange_not_in_plan`; and your plan''s bookmaker list), then `quote=true`, the price (402), and the board (503 while it is not ready). **Filters** cut the whole board''s events in order: sport and kick-off, then markets, then back offers by bookmaker and lay offers by exchange; an outcome left with no back or no lay goes, then empty markets and events. `dutching` keeps a dutch only when every leg is a named venue. A filtered reply is priced on its filters (`/v2/pricing`); the kick-off window is never priced. **Freshness**: `meta.processed_at` and `X-Processed-At` are the board time; `meta.last_seen` is each venue and sport''s last read. The API drops no row for age; a venue not read within its own window (at most two minutes) is off the board until its next read. **Conditional requests**: send the `ETag` back as `If-None-Match`; an unchanged body is a 304 with no body, free. A reply with no events is free.' tags: - Odds parameters: - $ref: '#/components/parameters/type' - $ref: '#/components/parameters/region' - $ref: '#/components/parameters/format' - $ref: '#/components/parameters/oddsFormat' - $ref: '#/components/parameters/dateFormat' - $ref: '#/components/parameters/quote' - $ref: '#/components/parameters/sports' - $ref: '#/components/parameters/bookmakers' - $ref: '#/components/parameters/exchanges' - $ref: '#/components/parameters/markets' - $ref: '#/components/parameters/commenceTimeFrom' - $ref: '#/components/parameters/commenceTimeTo' - $ref: '#/components/parameters/acceptEncoding' responses: '200': description: The board (envelope, or the `data` array under `format=bare`), or under `quote=true` the price. Gzip unless refused. headers: X-OddsRelay-Api-Version: description: 'Always sent: #/components/headers/X-OddsRelay-Api-Version' required: true schema: type: string Cache-Control: description: 'Always sent: #/components/headers/Cache-Control' required: true schema: type: string Vary: description: 'Always sent: #/components/headers/Vary' required: true schema: type: string X-RateLimit-Limit: description: 'Always sent: #/components/headers/X-RateLimit-Limit' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Remaining: description: 'Always sent: #/components/headers/X-RateLimit-Remaining' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Reset: description: 'Always sent: #/components/headers/X-RateLimit-Reset' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Cost: description: 'Always sent: #/components/headers/X-Tokens-Cost' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' ETag: description: 'Sent on every 200 but a `quote=true` price: #/components/headers/ETag' x-required-unless-quote: true schema: type: string X-OddsRelay-Version: $ref: '#/components/headers/X-OddsRelay-Version' X-Processed-At: description: 'Sent on every 200 but a `quote=true` price: #/components/headers/X-Processed-At' x-required-unless-quote: true schema: type: string X-OddsRelay-Request-Id: description: 'Always sent: #/components/headers/X-OddsRelay-Request-Id' required: true schema: type: string X-Count: $ref: '#/components/headers/X-Count' Content-Encoding: $ref: '#/components/headers/Content-Encoding' content: application/json: schema: oneOf: - $ref: '#/components/schemas/MatchedEnvelope' - type: array items: $ref: '#/components/schemas/MatchedEvent' - $ref: '#/components/schemas/Quote' examples: standard_filtered: summary: GET /v2/odds/standard?sports=tennis&bookmakers=william_hill&exchanges=betfair_exchange&markets=h2h value: meta: feed_type: standard region: uk odds_format: decimal processed_at: '2026-09-17T02:20:57.271Z' last_seen: betfair_exchange: tennis: '2026-09-17T02:20:56Z' william_hill: tennis: '2026-09-17T02:20:55Z' count: 26 version: v2 next_cursor: null data: - event_id: or_evt_917dd44bce05 sport_key: tennis_itf_w35_shenyang_chn sport_title: ITF W35 Shenyang CHN commence_time: '2026-09-17T03:00:00Z' home_team: Sijia Wei away_team: Zhuoxuan Bai markets: - key: h2h outcomes: - name: Sijia Wei back: - bookmaker: william_hill price: 2.9 link: https://sports.williamhill.com/betting/en-gb/tennis/OB_EV41212781/sijia-wei-vs-zhuoxuan-bai lay: - exchange: betfair_exchange price: 3.45 available: 175.0 link: https://www.betfair.com/exchange/plus/tennis/event/36078894 description: Trimmed from a live reply. each_way: summary: GET /v2/odds/each-way value: meta: feed_type: each-way region: uk odds_format: decimal processed_at: '2026-09-17T02:20:28.456Z' last_seen: 7bet: horse_racing: '2026-09-17T02:20:27Z' soccer: '2026-09-17T02:20:21Z' bet600: horse_racing: '2026-09-17T02:20:26Z' soccer: '2026-09-17T02:20:04Z' count: 39 version: v2 next_cursor: null data: - event_id: or_evt_3f4db6a34819 sport_key: horse_racing sport_title: Horse Racing commence_time: '2026-09-17T03:40:00Z' venue: Illawarra Grange markets: - key: each_way outcomes: - name: Albert Palais back: - bookmaker: kwiff price: 2.7 places: 3 place_fraction: 0.2 link: https://kwiff.com/sports/horse-racing lay: win: - exchange: betfair_exchange price: 3.25 available: 21.71 link: https://www.betfair.com/exchange/plus/horse-racing/market/1.262464867 place: - exchange: betfair_exchange price: 2.16 available: 39.42 places: 3 link: https://www.betfair.com/exchange/plus/horse-racing/market/1.262464868 description: Trimmed from a live reply. dutching_filtered: summary: GET /v2/odds/dutching?sports=tennis&bookmakers=william_hill,betfair_exchange&markets=h2h value: meta: feed_type: dutching region: uk odds_format: decimal processed_at: '2026-09-17T02:20:55.040Z' last_seen: betfair_exchange: tennis: '2026-09-17T02:20:56Z' william_hill: tennis: '2026-09-17T02:20:55Z' count: 2 version: v2 next_cursor: null data: - event_id: or_evt_8fe55f3bdad5 sport_key: tennis_atp_challenger_tiburon_usa_men_singles sport_title: ATP Challenger Tiburon, USA, Men Singles commence_time: '2026-09-17T23:10:00Z' home_team: Michael Zheng away_team: Trevor Svajda markets: - key: h2h outcomes: - name: Michael Zheng back: - bookmaker: william_hill price: 1.33 link: https://sports.williamhill.com/betting/en-gb/tennis/OB_EV41211653/michael-zheng-vs-trevor-svajda dutch_id: b8d7916c - name: Trevor Svajda back: - bookmaker: betfair_exchange price: 3.5 link: https://www.betfair.com/exchange/plus/tennis/event/36077864 dutch_id: b8d7916c description: Trimmed from a live reply. bog_bare: summary: GET /v2/odds/bog?format=bare value: - event_id: or_evt_58ab94d71497 sport_key: horse_racing sport_title: Horse Racing commence_time: '2026-09-17T12:30:00Z' venue: Ayr markets: - key: win outcomes: - name: Bassrah back: - bookmaker: ladbrokes price: 4.0 link: https://www.ladbrokes.com/en/sports/event/258542357 - bookmaker: betfred price: 4.0 link: https://www.betfred.com/sports/horse-racing/event/36917738 lay: - exchange: betfair_exchange price: 4.3 available: 17.26 link: https://www.betfair.com/exchange/plus/horse-racing/market/1.262474543 - exchange: smarkets price: 4.4 available: 75.93 link: https://smarkets.com/sport/horse-racing/ayr/2026/09/17/13-30/45322266/ description: Trimmed from a live reply. quote: summary: GET /v2/odds/standard?quote=true value: cost: 0 remaining: unlimited rate_card: 2026-09 description: Numbers are placeholders. '304': description: 'Not modified: no body, free.' headers: X-OddsRelay-Api-Version: description: 'Always sent: #/components/headers/X-OddsRelay-Api-Version' required: true schema: type: string Cache-Control: description: 'Always sent: #/components/headers/Cache-Control' required: true schema: type: string Vary: description: 'Always sent: #/components/headers/Vary' required: true schema: type: string X-RateLimit-Limit: description: 'Always sent: #/components/headers/X-RateLimit-Limit' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Remaining: description: 'Always sent: #/components/headers/X-RateLimit-Remaining' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Reset: description: 'Always sent: #/components/headers/X-RateLimit-Reset' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Cost: description: 'Always sent: #/components/headers/X-Tokens-Cost' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' ETag: description: 'Always sent: #/components/headers/ETag' required: true schema: type: string X-OddsRelay-Version: $ref: '#/components/headers/X-OddsRelay-Version' X-Processed-At: description: 'Always sent: #/components/headers/X-Processed-At' required: true schema: type: string X-Count: $ref: '#/components/headers/X-Count' '400': $ref: '#/components/responses/BadRequest' description: Unknown or repeated parameter; a bad `format`, `oddsFormat` or `dateFormat` value; on standard and dutching american odds, unix dates or gzip refused; a filter over its cap; a bad instant or window; a region list, repeat or uppercase code; `region_required`; `unknown_sport`, `unknown_market`, `unknown_bookmaker`, `unknown_exchange` (with `valid`). '401': $ref: '#/components/responses/Unauthorized' description: '`missing_api_key`, `invalid_api_key` (also a closed account), `revoked_api_key`, `expired_api_key`, `paused_api_key`.' '402': $ref: '#/components/responses/PaymentRequired' description: '`insufficient_tokens`, before anything is built (a raw page''s price is known after its page is cut), or after it when a concurrent spend emptied the balance: the body is not sent.' '403': $ref: '#/components/responses/Forbidden' description: '`account_suspended`, `origin_not_allowed`, `schema_not_in_scope`, `region_not_in_scope`, `not_in_key_scope`, `product_not_in_region`, `bookmaker_not_in_region`, `bookmaker_not_in_plan`, `sport_not_in_plan`, `market_not_in_plan`, `exchange_not_in_plan`.' '404': $ref: '#/components/responses/NotFound' description: '`unknown_type`, `unknown_region`.' '429': $ref: '#/components/responses/TooManyRequests' description: '`too_many_from_address`, then after authentication `too_many_requests`; the front door''s own caps answer an empty-bodied 429.' '500': $ref: '#/components/responses/InternalError' description: '`internal_error`.' '503': $ref: '#/components/responses/ServiceUnavailable' description: '`temporarily_unavailable`.' '502': $ref: '#/components/responses/FrontDoorError' /v2/odds/raw: get: operationId: getRawBoard summary: The raw board, paged description: 'Every venue''s prices per event, one page at a time: bookmakers as `{name, price}`, exchanges quoting a lay side as `{name, back_price, lay_price, available}`. Served gzip only: a request refusing gzip is 400 `add Accept-Encoding: gzip; with curl use --compressed`. **Checks, in this order**: the per-address shield, the key, the flood cap, a suspended account, a browser key''s website, the API version; `region`, the region and `raw` in the key; the parameters (each one this route reads, once) and their values; the filters (400 unknown values, 403 `bookmaker_not_in_region`, 403 `bookmaker_not_in_plan`, then your account''s scope: 403 `sport_not_in_plan`, `market_not_in_plan`, `bookmaker_not_in_plan`); `includeLinks` and `includeLiquidity`; `limit`; `cursor`; gzip; the raw board (503 `raw_not_enabled`); the page (503 while it cannot be cut); `quote=true`; the price (402). **Paging**: pages are ordered by (kick-off, event id) and a cursor resumes strictly after the last event it saw, so an event that leaves the board between pages never restarts the walk. The accepted limit: an event whose kick-off moves across the cursor during a walk can appear twice or be missed. Dedupe by event `id`, and take a missed event on the next walk. A cursor expires 300 s after it was issued. **Following changes**: walk with `updatedSince` set to the start of your previous walk; `last_update` is when a venue''s market last moved (see the schema for what it is not). **Price**: a page pays its share of the query''s price (its prices over all the query''s events'' prices, at least 1; `eventIds` and `requireBookmakers` narrow the page, never the query), so a whole walk pays the query''s price (narrowed by `eventIds` or `requireBookmakers`, only its kept events'' share of it); a recent `updatedSince` pays a reduced share. `quote=true` prices this page.' tags: - Odds parameters: - $ref: '#/components/parameters/region' - $ref: '#/components/parameters/format' - $ref: '#/components/parameters/oddsFormat' - $ref: '#/components/parameters/dateFormat' - $ref: '#/components/parameters/rawSports' - $ref: '#/components/parameters/rawSport' - $ref: '#/components/parameters/leagues' - $ref: '#/components/parameters/rawBookmakers' - $ref: '#/components/parameters/rawRequireBookmakers' - $ref: '#/components/parameters/rawMarkets' - $ref: '#/components/parameters/rawEventIds' - $ref: '#/components/parameters/commenceTimeFrom' - $ref: '#/components/parameters/commenceTimeTo' - $ref: '#/components/parameters/updatedSince' - $ref: '#/components/parameters/exchangesOnly' - $ref: '#/components/parameters/includeLinks' - $ref: '#/components/parameters/includeLiquidity' - $ref: '#/components/parameters/cursor' - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/quote' - $ref: '#/components/parameters/rawAcceptEncoding' responses: '200': description: One page (envelope, or the `data` array under `format=bare`), or under `quote=true` its price. Gzip. headers: X-OddsRelay-Api-Version: description: 'Always sent: #/components/headers/X-OddsRelay-Api-Version' required: true schema: type: string Cache-Control: description: 'Always sent: #/components/headers/Cache-Control' required: true schema: type: string Vary: description: 'Always sent: #/components/headers/Vary' required: true schema: type: string X-RateLimit-Limit: description: 'Always sent: #/components/headers/X-RateLimit-Limit' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Remaining: description: 'Always sent: #/components/headers/X-RateLimit-Remaining' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Reset: description: 'Always sent: #/components/headers/X-RateLimit-Reset' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Cost: description: 'Always sent: #/components/headers/X-Tokens-Cost' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' ETag: description: 'Sent on every 200 but a `quote=true` price: #/components/headers/ETag' x-required-unless-quote: true schema: type: string X-OddsRelay-Version: $ref: '#/components/headers/X-OddsRelay-Version' X-Processed-At: description: 'Sent on every 200 but a `quote=true` price: #/components/headers/X-Processed-At' x-required-unless-quote: true schema: type: string X-OddsRelay-Request-Id: description: 'Always sent: #/components/headers/X-OddsRelay-Request-Id' required: true schema: type: string X-Count: $ref: '#/components/headers/X-Count' X-Next-Cursor: $ref: '#/components/headers/X-Next-Cursor' X-Feed-Type: $ref: '#/components/headers/X-Feed-Type' X-Region: $ref: '#/components/headers/X-Region' Content-Encoding: $ref: '#/components/headers/Content-Encoding' content: application/json: schema: oneOf: - $ref: '#/components/schemas/RawEnvelope' - type: array items: $ref: '#/components/schemas/RawEvent' - $ref: '#/components/schemas/Quote' examples: page: summary: GET /v2/odds/raw?limit=2&sports=tennis value: meta: feed_type: raw region: uk odds_format: decimal processed_at: '2026-09-17T02:20:58.882Z' count: 2 version: v2 next_cursor: eyJrIjoiMjAyNi0wOS0xN1QwMjoyNDowMFoiLCJpIjoib3JfZXZ0X2ZiN2YzNzQyYjY4MSIsInQiOjE3ODk2MTE2NjF9 total: 1312 board_version: 1789611660384 data: - id: or_evt_da71d5873e85 cluster_id: null sport_key: tennis_itf_women_kyoto_2026 sport_title: ITF Women Kyoto 2026 commence_time: '2026-09-17T02:24:00Z' home_team: Natsuki Yoshimoto away_team: Tiana Tian Deng bookmakers: - key: bet600 title: Bet600 region: uk last_update: '2026-09-17T02:18:09Z' markets: - key: h2h last_update: '2026-09-17T02:17:02Z' outcomes: - name: Natsuki Yoshimoto price: 1.7 - name: Tiana Tian Deng price: 2.0 - key: correct_score last_update: '2026-09-17T02:17:02Z' outcomes: - name: 0 - 2 price: 3.0 - name: 1 - 2 price: 4.75 - key: betgoodwin title: BetGoodwin region: uk last_update: '2026-09-17T02:18:09Z' markets: - key: h2h last_update: '2026-09-17T02:17:02Z' outcomes: - name: Natsuki Yoshimoto price: 1.7 - name: Tiana Tian Deng price: 2.0 - key: correct_score last_update: '2026-09-17T02:17:02Z' outcomes: - name: 0 - 2 price: 3.0 - name: 1 - 2 price: 4.75 description: Trimmed from a live reply. `total` and `next_cursor` are the live reply's. exchange_with_link: summary: GET /v2/odds/raw?limit=1&sports=tennis&exchangesOnly=true&includeLinks=true&markets=h2h value: meta: feed_type: raw region: uk odds_format: decimal processed_at: '2026-09-17T02:24:34.977Z' count: 1 version: v2 next_cursor: eyJrIjoiMjAyNi0wOS0xN1QwMjowODoyNFoiLCJpIjoib3JfZXZ0XzQ5MWM0NzEyOGRiNiIsInQiOjE3ODk2MTE4Nzh9 total: 212 board_version: 1789611876257 data: - id: or_evt_491c47128db6 cluster_id: null sport_key: tennis_tennis sport_title: Tennis commence_time: '2026-09-17T02:08:24Z' home_team: Natsuki Yoshimoto away_team: Tiana Tian Deng bookmakers: - key: betfair_exchange title: Betfair Exchange region: uk last_update: '2026-09-17T02:23:45Z' markets: - key: h2h last_update: '2026-09-17T02:23:45Z' outcomes: - name: Natsuki Yoshimoto price: 1.73 - name: Tiana Deng price: 2.14 link: https://www.betfair.com/exchange/plus/tennis/event/36079457 description: A live reply. '304': description: 'Not modified: no body, free.' headers: X-OddsRelay-Api-Version: description: 'Always sent: #/components/headers/X-OddsRelay-Api-Version' required: true schema: type: string Cache-Control: description: 'Always sent: #/components/headers/Cache-Control' required: true schema: type: string Vary: description: 'Always sent: #/components/headers/Vary' required: true schema: type: string X-RateLimit-Limit: description: 'Always sent: #/components/headers/X-RateLimit-Limit' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Remaining: description: 'Always sent: #/components/headers/X-RateLimit-Remaining' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Reset: description: 'Always sent: #/components/headers/X-RateLimit-Reset' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Cost: description: 'Always sent: #/components/headers/X-Tokens-Cost' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' ETag: description: 'Always sent: #/components/headers/ETag' required: true schema: type: string X-OddsRelay-Version: $ref: '#/components/headers/X-OddsRelay-Version' X-Processed-At: description: 'Always sent: #/components/headers/X-Processed-At' required: true schema: type: string X-Count: $ref: '#/components/headers/X-Count' X-Next-Cursor: $ref: '#/components/headers/X-Next-Cursor' X-Feed-Type: $ref: '#/components/headers/X-Feed-Type' X-Region: $ref: '#/components/headers/X-Region' '400': $ref: '#/components/responses/BadRequest' description: Unknown or repeated parameter; a bad format, instant, `updatedSince`, boolean, `limit` or `cursor`; a filter over its cap; gzip refused; a region list, repeat or uppercase code; `region_required`; `unknown_sport`, `unknown_market`, `unknown_bookmaker` (with `valid`). '401': $ref: '#/components/responses/Unauthorized' description: '`missing_api_key`, `invalid_api_key` (also a closed account), `revoked_api_key`, `expired_api_key`, `paused_api_key`.' '402': $ref: '#/components/responses/PaymentRequired' description: '`insufficient_tokens`, before anything is built (a raw page''s price is known after its page is cut), or after it when a concurrent spend emptied the balance: the body is not sent.' '403': $ref: '#/components/responses/Forbidden' description: '`account_suspended`, `origin_not_allowed`, `schema_not_in_scope`, `region_not_in_scope`, `not_in_key_scope`, `bookmaker_not_in_region`, `bookmaker_not_in_plan` (a venue of either kind outside your plan or your account''s raw scope), `sport_not_in_plan`, `market_not_in_plan`.' '404': $ref: '#/components/responses/NotFound' description: '`unknown_region`.' '429': $ref: '#/components/responses/TooManyRequests' description: '`too_many_from_address`, then after authentication `too_many_requests`; the front door''s own caps answer an empty-bodied 429.' '500': $ref: '#/components/responses/InternalError' description: '`internal_error`.' '503': $ref: '#/components/responses/ServiceUnavailable' description: '`raw_not_enabled`, `temporarily_unavailable`.' '502': $ref: '#/components/responses/FrontDoorError' /v2/odds/event/{id}: get: operationId: getRawEvent summary: One event from the raw board description: 'One raw event by id, every venue (a plan with a bookmaker list sees those venues and the region''s exchanges; an account scope sees its raw venue list and markets only, no exchange added). Checks: the key and scope as raw, the parameters and their values, the raw board (503 `raw_not_enabled`), the id (404 `unknown_event`), the event''s sport in your account''s raw scope (403 `sport_not_in_plan`), `quote=true`, the price (402). The price is the whole event''s, whatever the scope cuts. Priced by the event''s share of the region''s raw prices. Gzip unless refused (over 1 kB).' tags: - Odds parameters: - $ref: '#/components/parameters/eventId' - $ref: '#/components/parameters/region' - $ref: '#/components/parameters/oddsFormat' - $ref: '#/components/parameters/dateFormat' - $ref: '#/components/parameters/includeLinks' - $ref: '#/components/parameters/includeLiquidity' - $ref: '#/components/parameters/quote' responses: '200': description: The event, or under `quote=true` its price. headers: X-OddsRelay-Api-Version: description: 'Always sent: #/components/headers/X-OddsRelay-Api-Version' required: true schema: type: string Cache-Control: description: 'Always sent: #/components/headers/Cache-Control' required: true schema: type: string Vary: description: 'Always sent: #/components/headers/Vary' required: true schema: type: string X-RateLimit-Limit: description: 'Always sent: #/components/headers/X-RateLimit-Limit' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Remaining: description: 'Always sent: #/components/headers/X-RateLimit-Remaining' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Reset: description: 'Always sent: #/components/headers/X-RateLimit-Reset' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Cost: description: 'Always sent: #/components/headers/X-Tokens-Cost' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' ETag: description: 'Sent on every 200 but a `quote=true` price: #/components/headers/ETag' x-required-unless-quote: true schema: type: string X-OddsRelay-Version: $ref: '#/components/headers/X-OddsRelay-Version' Content-Encoding: $ref: '#/components/headers/Content-Encoding' content: application/json: schema: oneOf: - $ref: '#/components/schemas/RawEvent' - $ref: '#/components/schemas/Quote' examples: event: summary: GET /v2/odds/event/or_evt_da71d5873e85 value: id: or_evt_da71d5873e85 cluster_id: null sport_key: tennis_itf_women_kyoto_2026 sport_title: ITF Women Kyoto 2026 commence_time: '2026-09-17T02:24:00Z' home_team: Natsuki Yoshimoto away_team: Tiana Tian Deng bookmakers: - key: bet600 title: Bet600 region: uk last_update: '2026-09-17T02:18:09Z' markets: - key: h2h last_update: '2026-09-17T02:17:02Z' outcomes: - name: Natsuki Yoshimoto price: 1.7 - name: Tiana Tian Deng price: 2.0 - key: correct_score last_update: '2026-09-17T02:17:02Z' outcomes: - name: 0 - 2 price: 3.0 - name: 1 - 2 price: 4.75 - key: betgoodwin title: BetGoodwin region: uk last_update: '2026-09-17T02:18:09Z' markets: - key: h2h last_update: '2026-09-17T02:17:02Z' outcomes: - name: Natsuki Yoshimoto price: 1.7 - name: Tiana Tian Deng price: 2.0 - key: correct_score last_update: '2026-09-17T02:17:02Z' outcomes: - name: 0 - 2 price: 3.0 - name: 1 - 2 price: 4.75 description: Trimmed from a live reply. '304': description: 'Not modified: no body, free.' headers: X-OddsRelay-Api-Version: description: 'Always sent: #/components/headers/X-OddsRelay-Api-Version' required: true schema: type: string Cache-Control: description: 'Always sent: #/components/headers/Cache-Control' required: true schema: type: string Vary: description: 'Always sent: #/components/headers/Vary' required: true schema: type: string X-RateLimit-Limit: description: 'Always sent: #/components/headers/X-RateLimit-Limit' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Remaining: description: 'Always sent: #/components/headers/X-RateLimit-Remaining' required: true schema: type: string pattern: ^[0-9]+$ X-RateLimit-Reset: description: 'Always sent: #/components/headers/X-RateLimit-Reset' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Cost: description: 'Always sent: #/components/headers/X-Tokens-Cost' required: true schema: type: string pattern: ^[0-9]+$ X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' ETag: description: 'Always sent: #/components/headers/ETag' required: true schema: type: string X-OddsRelay-Version: $ref: '#/components/headers/X-OddsRelay-Version' '400': $ref: '#/components/responses/BadRequest' description: Unknown or repeated parameter; a bad `oddsFormat`, `dateFormat` or boolean; a region list, repeat or uppercase code; `region_required`. '401': $ref: '#/components/responses/Unauthorized' description: '`missing_api_key`, `invalid_api_key` (also a closed account), `revoked_api_key`, `expired_api_key`, `paused_api_key`.' '402': $ref: '#/components/responses/PaymentRequired' description: '`insufficient_tokens`, before anything is built (a raw page''s price is known after its page is cut), or after it when a concurrent spend emptied the balance: the body is not sent.' '403': $ref: '#/components/responses/Forbidden' description: '`account_suspended`, `origin_not_allowed`, `schema_not_in_scope`, `region_not_in_scope`, `not_in_key_scope`, `sport_not_in_plan` (the event''s sport is outside your account''s raw scope).' '404': $ref: '#/components/responses/NotFound' description: '`unknown_event`, `unknown_region`.' '429': $ref: '#/components/responses/TooManyRequests' description: '`too_many_from_address`, then after authentication `too_many_requests`; the front door''s own caps answer an empty-bodied 429.' '500': $ref: '#/components/responses/InternalError' description: '`internal_error`.' '503': $ref: '#/components/responses/ServiceUnavailable' description: '`raw_not_enabled`, `temporarily_unavailable`.' '502': $ref: '#/components/responses/FrontDoorError' components: examples: too_many_from_address: summary: The per-address shield value: error: code: too_many_from_address message: Too many requests from this address. request_id: or_req_00000000000000000000000000000000 unknown_region: summary: Unknown region value: error: code: unknown_region message: Unknown region 'zz'. request_id: or_req_00000000000000000000000000000000 not_found: summary: No such route value: error: code: not_found message: No such endpoint. request_id: or_req_00000000000000000000000000000000 bad_request_cursor: summary: Expired cursor value: error: code: bad_request message: 'cursor expired: start again from the first page' request_id: or_req_00000000000000000000000000000000 region_required: summary: No region and the key lacks uk value: error: code: region_required message: This key does not cover uk, the region a call without ?region= reads. Add ?region=za (one of your key's regions). request_id: or_req_00000000000000000000000000000000 paused_api_key: summary: Paused key value: error: code: paused_api_key message: This API key is paused. request_id: or_req_00000000000000000000000000000000 product_not_in_region: summary: Product the region does not serve value: error: code: product_not_in_region message: Region 'za' does not serve 'standard'. request_id: or_req_00000000000000000000000000000000 type: product unknown_sport: summary: Unknown sport (valid trimmed) value: error: code: unknown_sport message: 'Unknown sport ''curling_xyz''. A sport or a competition key: full list: /v2/sports.' request_id: or_req_00000000000000000000000000000000 valid: - american_football - aussie_rules - badminton - baseball - basketball - boxing unknown_event: summary: Event not on the raw board value: error: code: unknown_event message: No such event 'or_evt_000000000000'. request_id: or_req_00000000000000000000000000000000 internal_error: summary: Internal error value: error: code: internal_error message: Internal error. request_id: or_req_00000000000000000000000000000000 bad_request_param: summary: Unknown parameter value: error: code: bad_request message: 'Unknown parameter ''foo'': /v2/odds/{type} accepts bookmakers, commenceTimeFrom, commenceTimeTo, dateFormat, exchanges, format, markets, oddsFormat, quote, region, sports.' request_id: or_req_00000000000000000000000000000000 region_not_in_scope: summary: Region outside the key value: error: code: region_not_in_scope message: Key not scoped for region 'za'. request_id: or_req_00000000000000000000000000000000 type: region invalid_api_key: summary: Key not recognised value: error: code: invalid_api_key message: API key not recognised. request_id: or_req_00000000000000000000000000000000 sport_not_in_plan: summary: Sport outside the account's scope value: error: code: sport_not_in_plan message: '''tennis'' is not in your plan''s sports for standard: ''valid'' lists them. GET /v2/usage shows your account''s scope.' request_id: or_req_00000000000000000000000000000000 valid: - horse_racing - soccer insufficient_tokens: summary: A public plan but business value: error: code: insufficient_tokens message: 'This request costs tokens and your account has left until 1 Oct 2026 00:00 UTC. Add filters (sports, bookmakers, exchanges or markets) to make it cheaper, or upgrade your plan: upgrades apply to this key at once.' request_id: or_req_00000000000000000000000000000000 cost: 0 remaining: 0 resets_at: '2026-10-01T00:00:00Z' plan: starter rate_card: 2026-09 quote: add quote=true to price any request for free upgrade_url: https://app.oddsrelay.io/billing description: Numbers are placeholders. unknown_market: summary: Unknown market on this product, carried by others (valid trimmed) value: error: code: unknown_market message: 'Unknown market ''each_way''. Carried by: /v2/odds/each-way, /v2/odds/extra-place.' request_id: or_req_00000000000000000000000000000000 valid: - 1st_half_away_team_total_goals - 1st_half_away_team_total_points - 1st_half_btts - 1st_half_correct_score - 1st_half_double_chance - 1st_half_handicap_goals products: - each-way - extra-place bookmaker_not_in_plan: summary: Venue outside the plan's bookmakers value: error: code: bookmaker_not_in_plan message: '''coral'' is not in your plan''s bookmakers: ''valid'' lists the ones it serves in this region.' request_id: or_req_00000000000000000000000000000000 valid: - bet365 - william_hill not_in_key_scope: summary: Product outside the key value: error: code: not_in_key_scope message: This key does not include 'raw'. request_id: or_req_00000000000000000000000000000000 type: product schema_not_in_scope: summary: Key pinned to another API version value: error: code: schema_not_in_scope message: Key not scoped for the 'v2' API. Use the version your key is scoped to. request_id: or_req_00000000000000000000000000000000 type: schema bad_request_gzip: summary: Gzip refused on standard, dutching or raw value: error: code: bad_request message: 'add Accept-Encoding: gzip; with curl use --compressed' request_id: or_req_00000000000000000000000000000000 expired_api_key: summary: Expired key value: error: code: expired_api_key message: This API key has expired. request_id: or_req_00000000000000000000000000000000 revoked_api_key: summary: Revoked key value: error: code: revoked_api_key message: This API key has been revoked. request_id: or_req_00000000000000000000000000000000 raw_not_enabled: summary: Raw board not held value: error: code: raw_not_enabled message: The raw feed is not enabled on this deployment. request_id: or_req_00000000000000000000000000000000 exchange_not_in_plan: summary: Exchange outside the account's scope value: error: code: exchange_not_in_plan message: '''smarkets'' is not in your plan''s exchanges for standard: ''valid'' lists the ones it serves in this region. GET /v2/usage shows your account''s scope.' request_id: or_req_00000000000000000000000000000000 valid: - betfair_exchange account_suspended: summary: Suspended account value: error: code: account_suspended message: This account is suspended because of a payment problem. request_id: or_req_00000000000000000000000000000000 reason: payment dashboard_url: https://app.oddsrelay.io/billing missing_param: summary: Required parameter missing value: error: code: missing_param message: Required query parameter 'sport' is missing. request_id: or_req_00000000000000000000000000000000 origin_not_allowed: summary: Browser key off its website value: error: code: origin_not_allowed message: This key may not be used from this website. request_id: or_req_00000000000000000000000000000000 insufficient_tokens_business: summary: Plan business value: error: code: insufficient_tokens message: This request costs tokens and your account has left until 1 Oct 2026 00:00 UTC. Add filters (sports, bookmakers, exchanges or markets) to make it cheaper, or talk to us about Enterprise at https://oddsrelay.io/contact. request_id: or_req_00000000000000000000000000000000 cost: 0 remaining: 0 resets_at: '2026-10-01T00:00:00Z' plan: business rate_card: 2026-09 quote: add quote=true to price any request for free upgrade_url: https://app.oddsrelay.io/billing description: Numbers are placeholders. unknown_type: summary: Unknown board value: error: code: unknown_type message: Unknown feed type 'nope'. request_id: or_req_00000000000000000000000000000000 temporarily_unavailable: summary: Board or registry not ready value: error: code: temporarily_unavailable message: Feed temporarily unavailable. request_id: or_req_00000000000000000000000000000000 too_many_requests: summary: The account's flood cap value: error: code: too_many_requests message: Too many requests for this account. request_id: or_req_00000000000000000000000000000000 market_not_in_plan: summary: Market outside the account's scope value: error: code: market_not_in_plan message: '''double_chance'' is not in your plan''s markets for standard: ''valid'' lists them. GET /v2/usage shows your account''s scope.' request_id: or_req_00000000000000000000000000000000 valid: - btts - h2h - totals insufficient_tokens_custom: summary: A custom plan value: error: code: insufficient_tokens message: This request costs tokens and your account has left until 1 Oct 2026 00:00 UTC. Add filters (sports, bookmakers, exchanges or markets) to make it cheaper, or talk to us about raising your allowance at https://oddsrelay.io/contact. request_id: or_req_00000000000000000000000000000000 cost: 0 remaining: 0 resets_at: '2026-10-01T00:00:00Z' plan: ent_acme rate_card: 2026-09 quote: add quote=true to price any request for free description: Numbers are placeholders. unknown_bookmaker: summary: Unknown bookmaker (valid trimmed) value: error: code: unknown_bookmaker message: Unknown bookmaker 'nope'. request_id: or_req_00000000000000000000000000000000 valid: - 7bet - 888sport - bet365 - bet600 - bet_target - betano bookmaker_not_in_region: summary: Venue outside the region value: error: code: bookmaker_not_in_region message: '''william_hill'' is not a venue in region ''za'': GET /v2/bookmakers lists each venue''s regions.' request_id: or_req_00000000000000000000000000000000 type: region missing_api_key: summary: No key value: error: code: missing_api_key message: 'No API key presented. Send Authorization: Bearer or_live_… or x-api-key.' request_id: or_req_00000000000000000000000000000000 unknown_exchange: summary: A bookmaker named in exchanges value: error: code: unknown_exchange message: 'Unknown exchange ''william_hill''. ''william_hill'' is a bookmaker: use bookmakers=william_hill.' request_id: or_req_00000000000000000000000000000000 valid: - betdaq - betfair_exchange - matchbook - smarkets responses: Forbidden: description: '403: the account, the key''s scope, the region, the plan or the account''s scope refuses this call.' headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' X-RateLimit-Limit: $ref: '#/components/headers/X-RateLimit-Limit' X-RateLimit-Remaining: $ref: '#/components/headers/X-RateLimit-Remaining' X-RateLimit-Reset: $ref: '#/components/headers/X-RateLimit-Reset' X-Tokens-Cost: $ref: '#/components/headers/X-Tokens-Cost' X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' Access-Control-Allow-Origin: $ref: '#/components/headers/Access-Control-Allow-Origin' content: application/json: schema: $ref: '#/components/schemas/Error403' examples: account_suspended: $ref: '#/components/examples/account_suspended' not_in_key_scope: $ref: '#/components/examples/not_in_key_scope' product_not_in_region: $ref: '#/components/examples/product_not_in_region' origin_not_allowed: $ref: '#/components/examples/origin_not_allowed' region_not_in_scope: $ref: '#/components/examples/region_not_in_scope' bookmaker_not_in_region: $ref: '#/components/examples/bookmaker_not_in_region' bookmaker_not_in_plan: $ref: '#/components/examples/bookmaker_not_in_plan' sport_not_in_plan: $ref: '#/components/examples/sport_not_in_plan' market_not_in_plan: $ref: '#/components/examples/market_not_in_plan' exchange_not_in_plan: $ref: '#/components/examples/exchange_not_in_plan' schema_not_in_scope: $ref: '#/components/examples/schema_not_in_scope' FrontDoorError: description: '502 from the front door when the API behind it does not answer: an empty body, no `code`. Retry with backoff.' TooManyRequests: description: '429 with `Retry-After`. `too_many_requests`: the account''s flood cap or its in-flight cap (with `X-RateLimit-*`). `too_many_from_address`: the per-address shield, before authentication (no token or flood-cap headers). The front door''s own per-address and global caps also answer 429 with `Retry-After`, but with an EMPTY body and no `code`.' headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' Retry-After: description: 'Seconds to wait before retrying: on every 429 and 503. A 402 never carries it.' required: true schema: type: string X-RateLimit-Limit: $ref: '#/components/headers/X-RateLimit-Limit' X-RateLimit-Remaining: $ref: '#/components/headers/X-RateLimit-Remaining' X-RateLimit-Reset: $ref: '#/components/headers/X-RateLimit-Reset' X-Tokens-Cost: $ref: '#/components/headers/X-Tokens-Cost' X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' Access-Control-Allow-Origin: $ref: '#/components/headers/Access-Control-Allow-Origin' content: application/json: schema: $ref: '#/components/schemas/Error429' examples: too_many_requests: $ref: '#/components/examples/too_many_requests' too_many_from_address: $ref: '#/components/examples/too_many_from_address' InternalError: description: 500 `internal_error`. If it came after a spend, `X-Tokens-Cost` reads 0 and the spend stands. headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' X-RateLimit-Limit: $ref: '#/components/headers/X-RateLimit-Limit' X-RateLimit-Remaining: $ref: '#/components/headers/X-RateLimit-Remaining' X-RateLimit-Reset: $ref: '#/components/headers/X-RateLimit-Reset' X-Tokens-Cost: $ref: '#/components/headers/X-Tokens-Cost' X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' Access-Control-Allow-Origin: $ref: '#/components/headers/Access-Control-Allow-Origin' content: application/json: schema: $ref: '#/components/schemas/Error500' examples: internal_error: $ref: '#/components/examples/internal_error' Unauthorized: description: '401: no key, or a key that is not accepted. Carries `WWW-Authenticate`; no token or flood-cap headers.' headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' WWW-Authenticate: description: On every 401. required: true schema: type: string content: application/json: schema: $ref: '#/components/schemas/Error401' examples: missing_api_key: $ref: '#/components/examples/missing_api_key' invalid_api_key: $ref: '#/components/examples/invalid_api_key' revoked_api_key: $ref: '#/components/examples/revoked_api_key' expired_api_key: $ref: '#/components/examples/expired_api_key' paused_api_key: $ref: '#/components/examples/paused_api_key' ServiceUnavailable: description: '503 with `Retry-After`: `temporarily_unavailable` (a board, the key registry or a connection is not ready; retry) or `raw_not_enabled` (the key may use raw but the raw board is not held here).' headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' Retry-After: description: 'Seconds to wait before retrying: on every 429 and 503. A 402 never carries it.' required: true schema: type: string X-RateLimit-Limit: $ref: '#/components/headers/X-RateLimit-Limit' X-RateLimit-Remaining: $ref: '#/components/headers/X-RateLimit-Remaining' X-RateLimit-Reset: $ref: '#/components/headers/X-RateLimit-Reset' X-Tokens-Cost: $ref: '#/components/headers/X-Tokens-Cost' X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' Access-Control-Allow-Origin: $ref: '#/components/headers/Access-Control-Allow-Origin' content: application/json: schema: $ref: '#/components/schemas/Error503' examples: temporarily_unavailable: $ref: '#/components/examples/temporarily_unavailable' raw_not_enabled: $ref: '#/components/examples/raw_not_enabled' PaymentRequired: description: '402 `insufficient_tokens`: the price is above the balance. Price it first with `quote=true`.' headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' X-RateLimit-Limit: $ref: '#/components/headers/X-RateLimit-Limit' X-RateLimit-Remaining: $ref: '#/components/headers/X-RateLimit-Remaining' X-RateLimit-Reset: $ref: '#/components/headers/X-RateLimit-Reset' X-Tokens-Cost: $ref: '#/components/headers/X-Tokens-Cost' X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' Access-Control-Allow-Origin: $ref: '#/components/headers/Access-Control-Allow-Origin' content: application/json: schema: $ref: '#/components/schemas/Error402' examples: insufficient_tokens: $ref: '#/components/examples/insufficient_tokens' insufficient_tokens_business: $ref: '#/components/examples/insufficient_tokens_business' insufficient_tokens_custom: $ref: '#/components/examples/insufficient_tokens_custom' BadRequest: description: '400: a parameter this route does not read or sent twice, a malformed value, gzip refused on standard, dutching or raw, or a filter value the catalogue does not know.' headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' X-RateLimit-Limit: $ref: '#/components/headers/X-RateLimit-Limit' X-RateLimit-Remaining: $ref: '#/components/headers/X-RateLimit-Remaining' X-RateLimit-Reset: $ref: '#/components/headers/X-RateLimit-Reset' X-Tokens-Cost: $ref: '#/components/headers/X-Tokens-Cost' X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' Access-Control-Allow-Origin: $ref: '#/components/headers/Access-Control-Allow-Origin' content: application/json: schema: $ref: '#/components/schemas/Error400' examples: bad_request_param: $ref: '#/components/examples/bad_request_param' bad_request_gzip: $ref: '#/components/examples/bad_request_gzip' bad_request_cursor: $ref: '#/components/examples/bad_request_cursor' missing_param: $ref: '#/components/examples/missing_param' region_required: $ref: '#/components/examples/region_required' unknown_sport: $ref: '#/components/examples/unknown_sport' unknown_market: $ref: '#/components/examples/unknown_market' unknown_bookmaker: $ref: '#/components/examples/unknown_bookmaker' unknown_exchange: $ref: '#/components/examples/unknown_exchange' NotFound: description: '404.' headers: X-OddsRelay-Api-Version: description: The API version label, equal to this document's `info.version`. On every reply but `/healthz`, a `HEAD`'s 405 and the empty-bodied 429s and 502. required: true schema: type: string X-OddsRelay-Request-Id: description: 'This request''s id (`or_req_` + 32 hex), the same value as an error body''s `request_id`: quote it when you contact us. On data and discovery 200s, quotes, `/v2/usage` and `/v2/pricing` 200s and every JSON error but a 405; not on 304s, `/v2/health`, `/v2/regions`, `/v2/coverage` 200s or `/v2/odds/event/{id}` 200s.' required: true schema: type: string Vary: $ref: '#/components/headers/Vary' X-RateLimit-Limit: $ref: '#/components/headers/X-RateLimit-Limit' X-RateLimit-Remaining: $ref: '#/components/headers/X-RateLimit-Remaining' X-RateLimit-Reset: $ref: '#/components/headers/X-RateLimit-Reset' X-Tokens-Cost: $ref: '#/components/headers/X-Tokens-Cost' X-Tokens-Used: $ref: '#/components/headers/X-Tokens-Used' X-Tokens-Remaining: $ref: '#/components/headers/X-Tokens-Remaining' X-Tokens-Limit: $ref: '#/components/headers/X-Tokens-Limit' X-Tokens-Reset: $ref: '#/components/headers/X-Tokens-Reset' Access-Control-Allow-Origin: $ref: '#/components/headers/Access-Control-Allow-Origin' content: application/json: schema: $ref: '#/components/schemas/Error404' examples: unknown_region: $ref: '#/components/examples/unknown_region' unknown_type: $ref: '#/components/examples/unknown_type' unknown_event: $ref: '#/components/examples/unknown_event' not_found: $ref: '#/components/examples/not_found' headers: X-Tokens-Limit: description: The account's tokens for the period, or `unlimited`. Sent when the balance is known. schema: type: string pattern: ^([0-9]+|unlimited)$ example: unlimited X-Region: description: '`format=bare` only, raw and paged discovery: `meta.region`.' schema: type: string example: uk X-RateLimit-Remaining: description: Requests left in that window. schema: type: string pattern: ^[0-9]+$ X-Next-Cursor: description: '`format=bare` only, raw and paged discovery: the next page''s cursor, empty on the last page.' schema: type: string Vary: description: 'Keyed replies: `Authorization, Accept-Encoding, Origin`. Keyless `/v2/coverage`: `Authorization, X-Api-Key, Accept-Encoding`. `/v2/openapi.json`: `Accept-Encoding`. JSON errors on `/v2` routes: `Origin`.' schema: type: string example: Authorization, Accept-Encoding, Origin Access-Control-Allow-Origin: description: '`*` on every GET reply of a keyless route (`/v2/status`, keyless `/v2/coverage`, `/v2/openapi.json`), refusals and errors included. A keyed reply never sends `*`: a browser or dashboard key''s reply echoes the exact request `Origin` when the key allows it, with `Access-Control-Expose-Headers`.' schema: type: string example: '*' X-RateLimit-Limit: description: The account's flood cap for the window with fewer requests left (per 10 seconds or per minute; your numbers are in `/v2/usage` `flood_cap`). On every reply to a valid key after authentication, 429 `too_many_requests` included; never on a 401, the per-address 429 or a keyless route. schema: type: string pattern: ^[0-9]+$ X-Tokens-Used: description: Tokens the account has used this period. Sent with the other balance headers whenever the balance is known (while the key registry is unreachable a reply can carry `X-Tokens-Cost` alone). schema: type: string pattern: ^[0-9]+$ X-Feed-Type: description: '`format=bare` only, raw and paged discovery: `meta.feed_type`.' schema: type: string example: raw X-Tokens-Reset: description: When the pool resets (ISO-8601 UTC, whole seconds); on a trial, the trial's end when that comes first. Sent when the balance is known. schema: type: string example: '2026-10-01T00:00:00Z' X-RateLimit-Reset: description: Seconds until that window ends. schema: type: string pattern: ^[0-9]+$ X-Tokens-Remaining: description: Tokens left this period, or `unlimited` on an unlimited account. Sent when the balance is known. schema: type: string pattern: ^([0-9]+|unlimited)$ example: unlimited Content-Encoding: description: '`gzip` when the body is gzip (see the gzip rules in the description).' schema: type: string enum: - gzip example: gzip X-OddsRelay-Version: description: The publish instant of the board version the body was cut from, epoch milliseconds. One publish reads one value on every route that serves it. Present whenever the board version is known. schema: type: string pattern: ^[0-9]+$ example: '1789611659197' X-Count: description: '`format=bare` only: the events served (matched boards), or the rows across all pages (raw, `/v2/sports`, `/v2/events`, `/v2/bookmakers`).' schema: type: string pattern: ^[0-9]+$ example: '477' X-Tokens-Cost: description: 'Tokens this reply cost: 0 on a free reply (304, an empty reply, discovery, `/v2/usage`, `/v2/pricing`, a quote and every refusal). On every reply to a valid key, refusals included; never on a 401, on the per-address 429 or on a keyless route.' schema: type: string pattern: ^[0-9]+$ parameters: commenceTimeTo: name: commenceTimeTo in: query description: Events kicking off at or before this ISO-8601 instant (no offset = UTC). required: false schema: type: string example: '2026-09-17T23:59:59Z' type: name: type in: path required: true description: 'A matched board: `standard`, `2up`, `dutching`, `each-way`, `extra-place` or `bog`. The aliases `two-up`, `each_way` and `extra_place` are accepted and served as their canonical slug. Anything else is 404 `unknown_type`.' schema: type: string enum: - standard - 2up - dutching - each-way - extra-place - bog - two-up - each_way - extra_place sports: name: sports in: query description: 'Comma-separated, at most 20: a sport key or a prefix of one as `/v2/sports` lists them (`soccer`, `soccer_epl`), lowercased. Unknown is 400 `unknown_sport` with `valid` (the sport key bases). A known value with nothing on the board is an empty 200, free. A value is inside your account''s scope when it starts with a scope sport (`soccer_epl` under `soccer`); outside, 403 `sport_not_in_plan` with `valid`; left out, trimmed to the scope.' required: false schema: type: string example: tennis cursor: name: cursor in: query description: 'The previous page''s `meta.next_cursor` (or `X-Next-Cursor`). A cursor is bound to no account and expires 300 s after it was issued: 400 `cursor expired: start again from the first page`; any other shape 400 `cursor not recognised: start again from the first page`.' required: false schema: type: string markets: name: markets in: query description: 'Comma-separated, at most 20: market keys (`h2h`, `totals`, …; `/v2/sports` rows list the keys seen under each competition). Each product''s keys: keyless `/v2/coverage`''s `markets`. Unknown is 400 `unknown_market` with `valid` (this product''s keys) and, when another product carries it, `products`; another product''s key may instead be accepted and match nothing. Outside your account''s scope for this product: 403 `market_not_in_plan` with `valid`; left out, trimmed to the scope.' required: false schema: type: string example: h2h acceptEncoding: name: Accept-Encoding in: header description: 'Send `gzip`; with curl, `--compressed` sends it and decompresses the reply. `standard` and `dutching` answer a request that refuses gzip (`identity` alone, as Python''s `urllib` sends by default, or `gzip;q=0` or `*;q=0`) with 400 `bad_request`, message `add Accept-Encoding: gzip; with curl use --compressed`; the other four boards answer it plain.' required: true schema: type: string example: gzip limit: name: limit in: query description: Events per page, 1-250 (default and maximum 250). Anything else is 400. required: false schema: type: integer minimum: 1 maximum: 250 exchanges: name: exchanges in: query description: 'Comma-separated, at most 4: exchange keys as `/v2/bookmakers` lists them. Unknown is 400 `unknown_exchange` with `valid`; a bookmaker here is that 400 naming `bookmakers`. Outside your account''s scope for this product: 403 `exchange_not_in_plan` with `valid`; left out, trimmed to the scope.' required: false schema: type: string example: betfair_exchange rawSport: name: sport in: query description: The same as `sports` (what `/v2/events` takes); ignored when `sports` is sent. required: false schema: type: string oddsFormat: name: oddsFormat in: query description: '`decimal` (default) or `american` (a signed integer; null for a price at or below 1.0). `standard` and `dutching` serve decimal only: anything else is 400.' required: false schema: type: string enum: - decimal - american default: decimal rawSports: name: sports in: query description: 'Comma-separated, at most 20: a sport key, a prefix of one, or a sport name raw serves, lowercased. Unknown is 400 `unknown_sport` with `valid`. Inside your account''s raw scope: a key or prefix starting with a scope sport, a sport name only when it equals one (send `soccer`, not `football`); outside, 403 `sport_not_in_plan` with `valid`; left out, trimmed to the scope.' required: false schema: type: string example: tennis includeLinks: name: includeLinks in: query description: '`true`: each venue''s `link` (its primary event page; left out when it has none). Default `false`. `true` or `false`, else 400.' required: false schema: type: string enum: - 'true' - 'false' default: 'false' rawBookmakers: name: bookmakers in: query description: 'Comma-separated, at most 40: venue keys of either kind, bookmakers and exchanges, as `/v2/bookmakers` lists them. Unknown is 400 `unknown_bookmaker` with `valid`; outside the region 403 `bookmaker_not_in_region`; outside your plan''s bookmaker list or your account''s raw scope 403 `bookmaker_not_in_plan` with `valid`. An account scope''s raw list is the whole venue list, exchanges included: left out, trimmed to it (so `exchangesOnly=true` keeps only the exchanges it names).' required: false schema: type: string example: betfair_exchange exchangesOnly: name: exchangesOnly in: query description: '`true`: exchanges only. `true` or `false`, else 400.' required: false schema: type: string enum: - 'true' - 'false' region: name: region in: query description: 'ONE lowercase region code (`/v2/regions` lists yours). Missing means `uk`: on a data route only when your key covers `uk`, else 400 `region_required` naming a code your key has; on discovery and keyless routes always. A comma list, the parameter sent twice or an uppercase code is 400 `bad_request`; an unknown code, or one with no live bookmaker here, is 404 `unknown_region`.' required: false schema: type: string example: uk rawEventIds: name: eventIds in: query description: 'Comma-separated, at most 50 event ids. Narrows the page, not the query: a page pays its share of the same call without `eventIds`.' required: false schema: type: string rawMarkets: name: markets in: query description: 'Comma-separated, at most 20: market keys raw serves. Raw''s keys: keyless `/v2/coverage`''s `markets.raw`. Unknown is 400 `unknown_market` with `valid` (raw''s keys) and, when another product carries it, `products`. Keeps venues quoting one of them, and only those markets. Outside your account''s scope for this product: 403 `market_not_in_plan` with `valid`; left out, trimmed to the scope.' required: false schema: type: string example: h2h quote: name: quote in: query description: '`true` (also `1`, `yes`, `on`): price this exact request instead of serving it. 200 `{cost, remaining, rate_card}`, free, every check before the price still applies; any other value is an ordinary request.' required: false schema: type: string example: 'true' bookmakers: name: bookmakers in: query description: 'Comma-separated, at most 40: bookmaker keys as `/v2/bookmakers` lists them (a listed name is accepted too). Unknown is 400 `unknown_bookmaker` with `valid`; an exchange here is that 400 naming `exchanges` (on `dutching` both lists take venues of either kind); a venue outside the region is 403 `bookmaker_not_in_region`; one outside your plan''s bookmaker list or your account''s scope is 403 `bookmaker_not_in_plan` with `valid` (on `dutching`, an exchange outside the scope is 403 `exchange_not_in_plan`). A plan with a bookmaker list, or an account scope, trims a call naming none to that list.' required: false schema: type: string example: william_hill eventId: name: id in: path required: true description: An event id (`or_evt_…`) from raw, a matched board or `/v2/events`. Not on the raw board now is 404 `unknown_event`. schema: type: string example: or_evt_da71d5873e85 format: name: format in: query description: '`envelope` (default): `{meta, data}`. `bare`: the `data` array alone, `meta` moved to `X-*` headers.' required: false schema: type: string enum: - envelope - bare default: envelope includeLiquidity: name: includeLiquidity in: query description: '`false`: leave out `available` beside an exchange''s `lay_price`. Default `true`. `true` or `false`, else 400.' required: false schema: type: string enum: - 'true' - 'false' default: 'true' rawAcceptEncoding: name: Accept-Encoding in: header description: 'Send `gzip`; with curl, `--compressed` sends it and decompresses the reply. The raw board answers a request that refuses gzip (`identity` alone, as Python''s `urllib` sends by default, or `gzip;q=0` or `*;q=0`) with 400 `bad_request`, message `add Accept-Encoding: gzip; with curl use --compressed`.' required: true schema: type: string example: gzip leagues: name: leagues in: query description: 'Comma-separated, at most 40: competition names (an event''s league or `sport_title`), lowercased. Not checked against a catalogue.' required: false schema: type: string commenceTimeFrom: name: commenceTimeFrom in: query description: Events kicking off at or after this ISO-8601 instant (no offset = UTC; a fraction of a second rounds up). Not ISO-8601 is 400; on the matched boards a From after To is 400. required: false schema: type: string example: '2026-09-17T12:00:00Z' updatedSince: name: updatedSince in: query description: 'Exactly `YYYY-MM-DDTHH:MM:SSZ` (any other form is 400). Keeps venues whose `last_update` is at or after it, and the events holding one. The bulk way to follow raw: a recent `updatedSince` pays a reduced share (`/v2/pricing` `recent`).' required: false schema: type: string example: '2026-09-17T02:20:00Z' dateFormat: name: dateFormat in: query description: '`iso` (default, ISO-8601 UTC) or `unix` (integer epoch seconds) for `commence_time`, `processed_at` and `last_update`. `standard` and `dutching` serve iso only: anything else is 400.' required: false schema: type: string enum: - iso - unix default: iso rawRequireBookmakers: name: requireBookmakers in: query description: 'Comma-separated, at most 40 venue keys, checked as `bookmakers` is (the same 400 and 403 codes). Keeps only the events where EVERY named venue quotes the call''s `markets` (with several, at least one of them each), under `exchangesOnly` if set; a venue that lists the event but not the market does not count. `bookmakers` does not narrow this test and `updatedSince` is not part of it: with `updatedSince`, an event carrying every named venue is kept when any venue shown changed, and the page shows only the venues that changed. It narrows events, never venues: every venue the other filters keep is still shown, named or not. `meta.total` and `X-Count` count the events kept. Price: like `eventIds`, it narrows the page and not the query, so a page pays its share of the same call without `requireBookmakers`, and an empty page is free.' required: false schema: type: string example: sky_bet,paddy_power,william_hill,ladbrokes,betfred,bet365 schemas: Error500: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - internal_error RawEnvelope: type: object required: - meta - data properties: meta: $ref: '#/components/schemas/RawMeta' data: type: array items: $ref: '#/components/schemas/RawEvent' MatchedEvent: type: object required: - event_id - sport_key - sport_title - commence_time - markets description: 'One fixture. Ids are opaque and stable across boards (one fixture is one id on every route), but not durable keys: a paired fixture re-keys once when a higher-ranked exchange lists it.' properties: event_id: type: string sport_key: type: string sport_title: type: string commence_time: type: - string - integer - 'null' home_team: type: - string - 'null' description: Team sports. away_team: type: - string - 'null' description: Team sports. event_name: type: string description: A team fixture that does not split into sides. venue: type: - string - 'null' description: 'Racing: the course.' markets: type: array items: type: object required: - key - outcomes properties: key: type: string outcomes: type: array items: $ref: '#/components/schemas/MatchedOutcome' Error429: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - too_many_requests - too_many_from_address Error403: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - account_suspended - not_in_key_scope - product_not_in_region - origin_not_allowed - region_not_in_scope - bookmaker_not_in_region - bookmaker_not_in_plan - sport_not_in_plan - market_not_in_plan - exchange_not_in_plan - schema_not_in_scope RawMeta: type: object required: - feed_type - region - odds_format - processed_at - count - version - next_cursor - total - board_version properties: feed_type: type: string enum: - raw region: type: string odds_format: type: string enum: - decimal - american processed_at: type: - string - integer description: ISO-8601 UTC, or epoch seconds under `dateFormat=unix`. count: type: integer description: Events on this page. version: type: string next_cursor: type: - string - 'null' total: type: integer description: Events matching the query, all pages. board_version: type: - integer - 'null' description: The page's `X-OddsRelay-Version`. Quote: type: object required: - cost - remaining - rate_card description: '`quote=true`: this request''s price. Free.' properties: cost: type: integer remaining: type: - integer - string - 'null' description: Tokens left, `unlimited`, or null when unknown. rate_card: type: string Error401: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - missing_api_key - invalid_api_key - revoked_api_key - expired_api_key - paused_api_key Error404: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - not_found - unknown_type - unknown_region - unknown_event Error503: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - temporarily_unavailable - raw_not_enabled RawEvent: type: object required: - id - cluster_id - sport_key - sport_title - commence_time - home_team - away_team - bookmakers properties: id: type: string cluster_id: type: - string - 'null' sport_key: type: string sport_title: type: string commence_time: type: - string - integer - 'null' home_team: type: - string - 'null' away_team: type: - string - 'null' bookmakers: type: array items: type: object required: - key - title - region - last_update - markets properties: key: type: string title: type: string region: type: string last_update: type: - string - integer - 'null' description: The newest `last_update` of the venue's markets. link: type: string description: '`includeLinks=true` only.' markets: type: array items: type: object required: - key - last_update - outcomes properties: key: type: string last_update: type: - string - integer - 'null' description: 'When this venue''s market last changed (second precision): a price, a lay price or `available` moved, or a row appeared, including a venue''s rows coming back after a few seconds off the board (see the Freshness note). A service restart can also move it once with nothing changed: dedupe on content. It is not a heartbeat: a quiet venue''s `last_update` ages while the venue is still read (venue freshness is `last_seen` in keyed `/v2/coverage`).' outcomes: type: array items: $ref: '#/components/schemas/RawOutcome' MatchedEnvelope: type: object required: - meta - data properties: meta: $ref: '#/components/schemas/MatchedMeta' data: type: array items: $ref: '#/components/schemas/MatchedEvent' Error400: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - bad_request - missing_param - region_required - unknown_sport - unknown_market - unknown_bookmaker - unknown_exchange Error402: type: object required: - error properties: error: type: object required: - code - message - request_id - cost - remaining - resets_at - plan - rate_card - quote description: '`insufficient_tokens`: the request''s price is above the account''s balance. Nothing was served or spent. The message names the price, the balance and the reset, and two fixes: add filters, or (plan `business`) talk to us about Enterprise at https://oddsrelay.io/contact, or (a custom plan) talk to us about raising your allowance at https://oddsrelay.io/contact, or (every other plan) upgrade. No `Retry-After`.' properties: code: type: string enum: - insufficient_tokens message: type: string request_id: type: string cost: type: integer description: This request's price in tokens. remaining: type: integer description: Tokens left until `resets_at`. resets_at: type: string description: 'ISO-8601 UTC, whole seconds: when the pool resets; on a trial, the trial''s end when that comes first.' plan: type: - string - 'null' rate_card: type: string quote: type: string description: How to price a request for free. upgrade_url: type: string description: 'Where to upgrade. Absent on a custom plan (any plan code but `free`, `trial`, `trial_outreach`, `starter`, `pro` and `business`): it has no self-serve upgrade.' ErrorBody: type: object required: - error description: Every JSON 4xx and 5xx this API writes. properties: error: type: object required: - code - message - request_id properties: code: type: string description: Stable machine code; branch on this, never on `message`. message: type: string description: Human text; may change. request_id: type: string description: '`or_req_` + 32 hex, as `X-OddsRelay-Request-Id`.' type: type: string enum: - product - region - schema description: 'Scope 403s only: which axis refused (`not_in_key_scope` and `product_not_in_region` product; `region_not_in_scope` and `bookmaker_not_in_region` region; `schema_not_in_scope` schema).' valid: type: array items: type: string description: '`unknown_sport` (sport key bases), `unknown_bookmaker`, `unknown_exchange`, `bookmaker_not_in_plan` (your plan''s bookmakers, or your account scope''s, in the call''s region), `sport_not_in_plan`, `market_not_in_plan` and `exchange_not_in_plan` (the scope''s list for that product): the values accepted, sorted. `unknown_market`: the market keys this product carries, sorted (keyless `/v2/coverage`''s `markets`); a key another product carries may also be accepted on this route, and matches nothing here.' products: type: array items: type: string description: '`unknown_market` only, when another product carries the market: those products, sorted, as the `{type}` of `/v2/odds/{type}` (or `raw`). Left out when no product carries it.' reason: type: string enum: - payment - terms description: '`account_suspended` only.' dashboard_url: type: string description: '`account_suspended` only.' MatchedOutcome: type: object required: - name - back properties: name: type: string point: type: number description: The line, on line markets. back: type: array items: type: object required: - bookmaker - price - link properties: bookmaker: type: - string - 'null' description: Venue key (`/v2/bookmakers`). price: type: - number - integer - 'null' link: type: - string - 'null' description: The venue's page for this event. places: type: - integer - 'null' description: '`each-way`, `extra-place`: places paid.' place_fraction: type: - number - 'null' description: '`each-way`, `extra-place`: the place terms as a fraction.' dutch_id: type: string description: '`dutching` only: 8 hex naming the dutch this leg belongs to. Group one market''s offers by it and rate a dutch as 100 / sum(1/price).' description: Best price first. lay: description: Lowest price first. An array of exchange offers; on `each-way` and `extra-place` an object `{win, place}` of two such arrays; absent on `dutching`. oneOf: - type: array items: type: object required: - exchange - price - available - link properties: exchange: type: string price: type: - number - integer - 'null' available: type: - number - 'null' description: Money available at that lay price. places: type: - integer - 'null' description: '`lay.place` only: the place market''s places.' link: type: - string - 'null' - type: object required: - win - place properties: win: type: array items: type: object required: - exchange - price - available - link properties: exchange: type: string price: type: - number - integer - 'null' available: type: - number - 'null' description: Money available at that lay price. places: type: - integer - 'null' description: '`lay.place` only: the place market''s places.' link: type: - string - 'null' place: type: array items: type: object required: - exchange - price - available - link properties: exchange: type: string price: type: - number - integer - 'null' available: type: - number - 'null' description: Money available at that lay price. places: type: - integer - 'null' description: '`lay.place` only: the place market''s places.' link: type: - string - 'null' RawOutcome: type: object required: - name description: A bookmaker quote is `{name, price}`; a venue quoting a lay side is `{name, back_price, lay_price, available}` (`available` left out under `includeLiquidity=false`). No commission is applied. properties: name: type: string price: type: - number - integer - 'null' back_price: type: - number - integer - 'null' lay_price: type: - number - integer - 'null' available: type: - number - 'null' MatchedMeta: type: object required: - feed_type - region - odds_format - processed_at - count - version - next_cursor properties: feed_type: type: string region: type: string odds_format: type: string enum: - decimal - american processed_at: type: - string - integer description: The board time (`X-Processed-At`). last_seen: type: object additionalProperties: type: object additionalProperties: type: string description: '`{bookmaker key: {sport key base: ISO-8601 Z}}`: the last instant each venue and sport was read, the oldest observation behind the board, so `now - last_seen` is an upper bound on that venue''s price age. The region''s venues only, cut to a filtered body''s venues and sports; always ISO; absent until the board carries the table; never on `format=bare`.' count: type: integer description: Events in `data`. version: type: string enum: - v2 next_cursor: type: 'null' securitySchemes: bearerAuth: type: http scheme: bearer description: '`Authorization: Bearer or_live_…`' apiKeyHeader: type: apiKey in: header name: x-api-key description: The same key in `x-api-key`.