openapi: 3.2.0 info: title: OddsRelay Discovery 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: Discovery description: Sports, events, venues, regions. Free. paths: /v2/sports: get: operationId: listSports summary: Sports and competitions description: Every competition on the matched boards, grouped, with the market keys seen under each. Free. Paged 250 rows at a time in (group, key) order; a cursor expires 300 s after it was issued. `region` missing means `uk`; discovery is not scoped to your key's regions. tags: - Discovery parameters: - $ref: '#/components/parameters/region' - $ref: '#/components/parameters/format' - $ref: '#/components/parameters/dateFormat' - $ref: '#/components/parameters/cursor' responses: '200': description: A page. Gzip unless refused (over 1 kB). 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-Processed-At: description: 'Always sent: #/components/headers/X-Processed-At' required: 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: - type: object required: - meta - data properties: meta: $ref: '#/components/schemas/PageMeta' data: type: array items: $ref: '#/components/schemas/Sport' - type: array items: $ref: '#/components/schemas/Sport' examples: page: summary: GET /v2/sports value: meta: feed_type: sports region: uk odds_format: decimal processed_at: '2026-09-17T02:20:11.103Z' count: 250 version: v2 next_cursor: eyJrIjoiQ3JpY2tldCIsImkiOiJjcmlja2V0X21pbm9yX2xlYWd1ZV8yMDI2IiwidCI6MTc4OTYxMTYzMH0= data: - key: american_football_american_football group: American Football title: American Football description: American Football (American Football) active: true has_outrights: false regions: - uk markets: - h2h - handicap_points - over_under_points - key: american_football_canada_cfl group: American Football title: Canada CFL description: Canada CFL (American Football) active: true has_outrights: false regions: - uk markets: - h2h - handicap_points - odd_even - over_under_points description: Trimmed from a live reply. '304': description: 'Not modified: no body.' 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-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`, `dateFormat` or `cursor`; a region list, repeat or uppercase code. '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`.' '403': $ref: '#/components/responses/Forbidden' description: '`account_suspended`, `origin_not_allowed`, `schema_not_in_scope`.' '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: '`temporarily_unavailable`.' '502': $ref: '#/components/responses/FrontDoorError' /v2/events: get: operationId: listEvents summary: Events of a sport description: 'The events on the matched boards under one sport key or prefix, with the boards carrying each. Free. Paged 250 at a time in (kick-off, id) order; a cursor resumes strictly after the last event it saw, so an event whose kick-off moves across the cursor during a walk can appear twice or be missed: dedupe by `id` and take a missed event on the next walk. A cursor expires 300 s after it was issued. `region` missing means `uk`.' tags: - Discovery parameters: - $ref: '#/components/parameters/region' - $ref: '#/components/parameters/format' - $ref: '#/components/parameters/dateFormat' - $ref: '#/components/parameters/cursor' - $ref: '#/components/parameters/eventsSport' - $ref: '#/components/parameters/eventsEventIds' - $ref: '#/components/parameters/commenceTimeFrom' - $ref: '#/components/parameters/commenceTimeTo' responses: '200': description: A page. Gzip unless refused (over 1 kB). 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-Processed-At: description: 'Always sent: #/components/headers/X-Processed-At' required: 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: - type: object required: - meta - data properties: meta: $ref: '#/components/schemas/PageMeta' data: type: array items: $ref: '#/components/schemas/Event' - type: array items: $ref: '#/components/schemas/Event' examples: page: summary: GET /v2/events?sport=tennis value: meta: feed_type: events region: uk odds_format: decimal processed_at: '2026-09-17T02:20:42.023Z' count: 250 version: v2 next_cursor: eyJrIjoiMjAyNi0wOS0xN1QxMjowMDowMFoiLCJpIjoib3JfZXZ0X2IzYjBlMzIzZDMwMiIsInQiOjE3ODk2MTE2NjF9 data: - id: or_evt_46fa37cda1f2 sport_key: tennis_australia_sydney sport_title: Australia - Sydney commence_time: '2026-09-17T02:25:00Z' home_team: James Girdler away_team: Roger Rogowski status: upcoming has_coverage: - dutching - id: or_evt_aa4ff8d0b3f3 sport_key: tennis_itf_w35_kyoto sport_title: ITF W35 Kyoto commence_time: '2026-09-17T02:25:00Z' home_team: Natsuki Yoshimoto away_team: Tiana Tian Deng status: upcoming has_coverage: - dutching description: Trimmed from a live reply. '304': description: 'Not modified: no body.' 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-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; `missing_param` (`sport`); `unknown_sport` (with `valid`); a bad `format`, `dateFormat`, instant or `cursor`; `eventIds` over 50; a region list, repeat or uppercase code. '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`.' '403': $ref: '#/components/responses/Forbidden' description: '`account_suspended`, `origin_not_allowed`, `schema_not_in_scope`.' '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: '`temporarily_unavailable`.' '502': $ref: '#/components/responses/FrontDoorError' /v2/bookmakers: get: operationId: listBookmakers summary: Venues description: 'Every bookmaker and exchange key the API serves, with its display name, regions and kind. Free. Takes no `region`: a `region` parameter is 400.' tags: - Discovery parameters: - $ref: '#/components/parameters/format' responses: '200': description: The catalogue. Gzip unless refused (over 1 kB). 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-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: - type: object required: - meta - data properties: meta: type: object required: - feed_type - count - version properties: feed_type: type: string count: type: integer version: type: string data: type: array items: $ref: '#/components/schemas/Venue' - type: array items: $ref: '#/components/schemas/Venue' examples: catalogue: summary: GET /v2/bookmakers value: meta: feed_type: bookmakers count: 70 version: v2 data: - key: 7bet name: 7bet regions: - uk is_exchange: false - key: 888sport name: 888sport regions: - uk is_exchange: false - key: bet365 name: Bet365 regions: - uk is_exchange: false description: Trimmed from a live reply. '304': description: 'Not modified: no body.' 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 '400': $ref: '#/components/responses/BadRequest' description: Unknown or repeated parameter; a bad `format`. '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`.' '403': $ref: '#/components/responses/Forbidden' description: '`account_suspended`, `origin_not_allowed`, `schema_not_in_scope`.' '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/regions: get: operationId: listRegions summary: Your regions description: The regions your key covers that have live bookmakers here, with each region's exchanges and coverage model. Free. tags: - Discovery parameters: [] responses: '200': description: The regions. Gzip unless refused (over 1 kB). 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' Content-Encoding: $ref: '#/components/headers/Content-Encoding' content: application/json: schema: $ref: '#/components/schemas/Regions' examples: regions: summary: GET /v2/regions value: version: v2 default_region: uk regions: - code: uk name: UK & Ireland requestable: true bookmakers: 58 exchanges: - Betdaq - Betfair Exchange - Matchbook - Smarkets matched_coverage: true coverage_model: matched exchange_available: true board: filtered description: A live reply. '400': $ref: '#/components/responses/BadRequest' description: Any parameter. '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`.' '403': $ref: '#/components/responses/Forbidden' description: '`account_suspended`, `origin_not_allowed`.' '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' components: schemas: Event: type: object required: - id - sport_key - sport_title - commence_time - home_team - away_team - status - has_coverage properties: id: type: string sport_key: type: string sport_title: type: string commence_time: type: - string - integer - 'null' home_team: type: - string - 'null' away_team: type: - string - 'null' status: type: string enum: - upcoming - started has_coverage: type: array items: type: string description: The matched boards carrying it. Sport: type: object required: - key - group - title - description - active - has_outrights - regions - markets properties: key: type: string group: type: string title: type: string description: type: string active: type: boolean has_outrights: type: boolean regions: type: array items: type: string markets: type: array items: type: string description: Market keys seen under it on the matched boards. Error500: allOf: - $ref: '#/components/schemas/ErrorBody' - type: object properties: error: type: object properties: code: type: string enum: - internal_error 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 PageMeta: 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 processed_at: type: - string - integer description: ISO-8601 UTC, or epoch seconds under `dateFormat=unix`. count: type: integer description: Rows on this page. version: type: string next_cursor: type: - string - 'null' 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 Regions: type: object required: - version - default_region - regions properties: version: type: string default_region: type: string regions: type: array items: type: object required: - code - name - requestable - bookmakers - exchanges - matched_coverage - coverage_model - exchange_available - board properties: code: type: string name: type: string requestable: type: boolean bookmakers: type: integer description: Bookmakers with rows now. exchanges: type: array items: type: string matched_coverage: type: boolean coverage_model: type: string enum: - matched - dutching exchange_available: type: boolean board: type: string enum: - filtered - partitioned 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 Venue: type: object required: - key - name - regions - is_exchange properties: key: type: string name: type: string regions: type: array items: type: string is_exchange: type: boolean 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.' 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 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 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 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' 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-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' eventsSport: name: sport in: query description: 'Required: a sport key or a prefix of one as `/v2/sports` lists them. Missing is 400 `missing_param`; unknown 400 `unknown_sport` with `valid`.' required: true 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 eventsEventIds: name: eventIds in: query description: Comma-separated, at most 50 event ids. required: false schema: type: string 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 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 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' 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 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`.