Odds Exchange ODDS/EXCHANGE
Local --- -- · --:--:--
UTC --- -- · --:--:--

Time zone

Clock

API reference

Odds Aggregation API

v1.0.0 · OpenAPI 3.0 · 1311 operations

Download openapi.json
Overview, auth, rate limits, response envelope
A canonical odds-aggregation API providing real-time and historical sports
betting odds across books, markets, and sports.

## Authentication
All endpoints require a Bearer token in the `Authorization` header:
```
Authorization: Bearer your_api_key_here
```
API keys are issued per plan (free / starter / pro / enterprise).

## Odds formats
Pass `?odds_format=decimal` (default), `?odds_format=american`, or
`?odds_format=fractional` on any endpoint that returns odds.

## Rate limits
Rate limits are enforced per API key per minute:
| Plan       | Req / min |
|------------|-----------|
| free       | 30        |
| starter    | 120       |
| pro        | 600       |
| enterprise | 6000      |

Exceeded limits return `429` with a `Retry-After` header.

## Response envelope
All successful responses are wrapped in:
```json
{ "data": ..., "meta": { "updated_at": "..." } }
```
Paginated responses additionally include `links` and pagination fields in `meta`.

Alerts

GET /v1/alerts List your active alerts

Responses

  • 200 Paginated list of your alerts
  • 401 Unauthorized (shared)
  • 429 RateLimited (shared)
POST /v1/alerts Create a price or movement alert

Request body

required: true
content:
  application/json:
    schema:
      $ref: '#/components/schemas/AlertRequest'

Responses

  • 201 Alert created
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
  • 429 RateLimited (shared)
DELETE /v1/alerts/{alert} Delete an alert

Parameters

Name In Type Required Description
alert path string yes Alert ULID

Responses

  • 200 Alert deleted
  • 401 Unauthorized (shared)
  • 404 NotFound (shared)
  • 429 RateLimited (shared)

Events

GET /v1/sports/{sport}/events List events for a sport

Parameters

Name In Type Required Description
sport path string yes Sport slug, e.g. `americanfootball_nfl`
status query string (scheduled|live|completed|cancelled) Filter by event status
date_from query string ISO 8601 date — include events on or after this date
date_to query string ISO 8601 date — include events on or before this date
per_page query integer Results per page (1–200, default 50)
page query integer Page number (1-indexed)

Responses

  • 200 Paginated list of events
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
  • 429 RateLimited (shared)
GET /v1/events/{event} Get a single event

Parameters

Name In Type Required Description
event path string yes Event ULID

Responses

  • 200 Single event
  • 401 Unauthorized (shared)
  • 404 NotFound (shared)
  • 429 RateLimited (shared)

Line Movement

GET /v1/events/{event}/odds/movement Line movement history for an event
Returns all recorded price changes for the event's markets, grouped by
market → book → outcome.

Parameters

Name In Type Required Description
event path string yes Event ULID
odds_format query string (decimal|american|fractional) Output format for odds values. Default: `decimal`.
market query string Filter to a specific market key
book query string Filter to a specific book key
outcome query string Filter to a specific outcome name
hours query integer How many hours back to look (1–168, default 48)

Responses

  • 200 Line movement grouped by market → book → outcome
  • 401 Unauthorized (shared)
  • 404 NotFound (shared)
  • 429 RateLimited (shared)
GET /v1/line-movement/{marketId} Historical price series for one market (WO-E2 §2)
Starter+ only. Returns one series per (outcome, point, book) found in
the requested window. Free tier → 403. Window clamped by
PlanGate::historyDays — starter 7d, pro 30d, enterprise unbounded.
Requested `?from` further back than the tier ceiling is silently
clamped; meta.window_from reports the effective window.

Parameters

Name In Type Required Description
marketId path string yes
selection query string Outcome|point key — e.g. 'Lakers|-3.5' or bare 'Lakers'
book query string Book key (stable identifier)
from query string

Responses

  • 200 Series envelope
  • 403 Plan required — free cannot access line movement
  • 404 Market not found

Odds

GET /v1/events/{event}/odds Get odds for an event
Returns all markets for the event with full book comparison per outcome.
Use `markets` to filter which markets are returned.
Use `books` to filter which sportsbooks are included.
Use `odds_format` to control the output format.

Parameters

Name In Type Required Description
event path string yes Event ULID
odds_format query string (decimal|american|fractional) Output format for odds values. Default: `decimal`.
markets query string Comma-separated market keys, e.g. `h2h,spreads,totals`
books query string Comma-separated book keys to include
include_movement query boolean Include last 5 price history rows per outcome inline

Responses

  • 200 Event odds with market and book comparison
  • 401 Unauthorized (shared)
  • 404 NotFound (shared)
  • 429 RateLimited (shared)
GET /v1/odds/best Cross-event best-line shopping
Returns the best available price for each outcome across all books,
for a given sport and market.

Parameters

Name In Type Required Description
sport query string yes Sport slug
market query string yes Market key
outcome query string Filter to a specific outcome name
books query string Comma-separated book keys to limit comparison
odds_format query string (decimal|american|fractional) Output format for odds values. Default: `decimal`.
per_page query integer Results per page (1–200, default 50)

Responses

  • 200 Best prices per outcome across all events
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
  • 429 RateLimited (shared)
GET /v1/odds/arb Arbitrage opportunity feed
Scans scheduled and live events for arbitrage opportunities on moneyline
markets. Returns opportunities where the implied probability sum < 1.0.

Parameters

Name In Type Required Description
sport query string Filter to a single sport slug

Responses

  • 200 List of current arb opportunities
  • 401 Unauthorized (shared)
  • 429 RateLimited (shared)
GET /v1/events/{event}/props Player props for one event (pro+)
Gated to the player_props feature (pro/enterprise) — a lower tier gets
403 plan_required, NOT a silent empty array. Returns per-(player, market)
rows: each side (over/under or yes/no) carries a `books` map of present
books at the active point plus derived best/avg/deviation. Every book
cell has american + decimal odds, point, stale flag, age_min, and a
stable prop_line_id. `meta.freshness` makes an empty/all-stale response
self-describing (covered + coverage_reason).

Parameters

Name In Type Required Description
event path string yes Event ULID
market query string Narrow to a single prop market_key (e.g. batter_hits).

Responses

  • 200 Props envelope
  • 403 Requires the pro plan or higher
  • 404 Event not found
GET /v1/events/{event}/analytics Average + best odds per selection (WO-E2 §1)
Pro+ only. Returns OddsCalculationService::forMarket output per
market: book_count, average (decimal/american/implied), best
(decimal/american/book_id). Main lines only.

Parameters

Name In Type Required Description
event path string yes Event ULID
market query string (h2h|spreads|totals)

Responses

  • 200 Analytics envelope
  • 403 Plan required — free/starter cannot access analytics
  • 404 Event not found
GET /v1/arbs Arbitrage opportunities — h2h, next 48h (WO-E2 §3)
Starter+ only. Cross-book arbitrage on h2h for upcoming events.
Algorithm: best decimal per side; arb when sum(1/best) < 1. Margin
= 1 - sum(1/best). Sorted by margin desc.

Parameters

Name In Type Required Description
league query string League slug (basketball_nba) OR shortcode (NBA, EPL)
min_margin query number Minimum margin in [0, 1]. Default 0.5%.

Responses

  • 200 Arb feed
  • 403 Plan required — free cannot access the arb feed

Sports & Leagues

GET /v1/sports List all sports

Responses

  • 200 List of active sports
  • 401 Unauthorized (shared)
  • 429 RateLimited (shared)
GET /v1/sports/{sport}/leagues List leagues for a sport

Parameters

Name In Type Required Description
sport path string yes Sport slug, e.g. `americanfootball_nfl`

Responses

  • 200 List of leagues for the sport
  • 401 Unauthorized (shared)
  • 404 NotFound (shared)
  • 429 RateLimited (shared)
GET /v1/books List supported sportsbooks

Responses

  • 200 All active books
  • 401 Unauthorized (shared)
  • 429 RateLimited (shared)
GET /v1/ping Smoke endpoint
Confirms auth + throttle + usage logging end-to-end. Echoes the resolved plan.

Responses

  • 200 Pong
GET /v1/health Feed freshness + per-league coverage (PUBLIC, no ApiKey)
UNAUTHENTICATED (throttle-only). Lets a consumer/uptime monitor tell a
FROZEN pipeline apart from an off-season/quiet slate — the two otherwise
look identical (an empty payload). HTTP is always 200; read data.status.
Alert on a stale data.as_of, not on an empty props array.

Responses

  • 200 Feed freshness report

Tools

POST /v1/tools/convert Convert odds between formats

Request body

required: true
content:
  application/json:
    schema:
      type: object
      required:
        - value
        - from
        - to
      properties:
        value:
          type: string
          description: "Odds value in the input format. E.g. '+150', '2.50', '3/2'"
        from:
          type: string
          enum:
            - american
            - decimal
            - fractional
        to:
          type: string
          enum:
            - american
            - decimal
            - fractional

Responses

  • 200 Converted odds in all three formats
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
POST /v1/tools/hold Calculate market hold / vig

Request body

required: true
content:
  application/json:
    schema:
      type: object
      required:
        - legs
      properties:
        legs:
          type: array
          minItems: 2
          items:
            type: object
            required:
              - outcome
              - decimal
            properties:
              outcome: { type: string }
              decimal: { type: number, minimum: 1.001 }
              book: { type: string, nullable: true }

Responses

  • 200 Hold percentage and no-vig fair values
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
POST /v1/tools/arb Calculate arbitrage opportunity

Request body

required: true
content:
  application/json:
    schema:
      type: object
      required:
        - legs
      properties:
        legs:
          type: array
          minItems: 2
          items:
            type: object
            required:
              - outcome
              - decimal
            properties:
              outcome: { type: string }
              decimal: { type: number, minimum: 1.001 }
              book: { type: string, nullable: true }
        total_stake:
          type: number
          minimum: 1
          default: 100

Responses

  • 200 Arb calculation result
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
POST /v1/tools/hedge Calculate hedge bet sizing

Request body

required: true
content:
  application/json:
    schema:
      type: object
      required:
        - original_stake
        - leg1_decimal
        - leg2_decimal
      properties:
        original_stake:
          type: number
          minimum: 0.01
        leg1_decimal:
          type: number
          minimum: 1.001
        leg2_decimal:
          type: number
          minimum: 1.001
        leg1_is_freebet:
          type: boolean
          default: false

Responses

  • 200 Hedge stake and profit scenarios
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
POST /v1/tools/parlay Calculate parlay payout

Request body

required: true
content:
  application/json:
    schema:
      type: object
      required:
        - legs
        - stake
      properties:
        legs:
          type: array
          minItems: 2
          items:
            type: object
            required:
              - decimal
            properties:
              label: { type: string }
              decimal: { type: number, minimum: 1.001 }
        stake:
          type: number
          minimum: 0.01

Responses

  • 200 Parlay payout breakdown
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)
POST /v1/tools/line-shopping Compare lines across books for one outcome

Request body

required: true
content:
  application/json:
    schema:
      type: object
      required:
        - books
      properties:
        outcome:
          type: string
          nullable: true
        books:
          type: array
          minItems: 2
          items:
            type: object
            required:
              - book
              - decimal
            properties:
              book: { type: string }
              decimal: { type: number, minimum: 1.001 }
              point: { type: number, nullable: true }

Responses

  • 200 Sorted book comparison for the outcome
  • 401 Unauthorized (shared)
  • 422 ValidationError (shared)

Schemas

21 reusable component schemas referenced by the operations above.

SuccessEnvelope object
type: object
required:
  - data
  - meta
properties:
  data: {}
  meta:
    type: object
    required:
      - updated_at
    properties:
      updated_at:
        type: string
        format: date-time
PaginatedEnvelope object
type: object
required:
  - data
  - meta
  - links
properties:
  data:
    type: array
    items: {}
  meta:
    type: object
    required:
      - total
      - page
      - per_page
      - last_page
      - updated_at
    properties:
      total:
        type: integer
      page:
        type: integer
      per_page:
        type: integer
      last_page:
        type: integer
      updated_at:
        type: string
        format: date-time
  links:
    type: object
    properties:
      first:
        type: string
        format: uri
        nullable: true
      prev:
        type: string
        format: uri
        nullable: true
      next:
        type: string
        format: uri
        nullable: true
      last:
        type: string
        format: uri
        nullable: true
Sport object
type: object
required:
  - id
  - slug
  - name
  - active
properties:
  id:
    type: string
    description: ULID
  slug:
    type: string
  name:
    type: string
  active:
    type: boolean
League object
type: object
required:
  - id
  - slug
  - name
  - active
properties:
  id:
    type: string
  slug:
    type: string
  name:
    type: string
  region:
    type: string
    nullable: true
  sport:
    $ref: '#/components/schemas/Sport'
  active:
    type: boolean
Book object
type: object
required:
  - key
  - name
  - active
properties:
  key:
    type: string
  name:
    type: string
  region:
    type: string
    nullable: true
  active:
    type: boolean
Event object
type: object
required:
  - id
  - home_team
  - away_team
  - commence_time
  - status
properties:
  id:
    type: string
    description: ULID
  provider_event_id:
    type: string
  home_team:
    type: string
  away_team:
    type: string
  commence_time:
    type: string
    format: date-time
  status:
    type: string
    enum:
      - scheduled
      - live
      - completed
      - cancelled
  league:
    $ref: '#/components/schemas/League'
Price object
type: object
required:
  - book_key
  - book_name
  - price
  - price_decimal
  - is_main
  - is_opening
  - last_update
properties:
  book_key:
    type: string
  book_name:
    type: string
  price:
    description: 'Odds in requested format'
    oneOf:
      -
        type: string
      -
        type: number
  price_decimal:
    type: number
  point:
    type: number
    nullable: true
  is_main:
    type: boolean
  is_opening:
    type: boolean
  last_update:
    type: string
    format: date-time
Outcome object
type: object
required:
  - name
  - best_price
  - best_price_decimal
  - best_book
  - books
properties:
  name:
    type: string
  description:
    type: string
    nullable: true
  best_price:
    oneOf:
      -
        type: string
      -
        type: number
  best_price_decimal:
    type: number
  best_book:
    type: string
  opening_price:
    oneOf:
      -
        type: string
      -
        type: number
  books:
    type: array
    items:
      $ref: '#/components/schemas/Price'
Market object
type: object
required:
  - key
  - label
  - outcomes
properties:
  key:
    type: string
  label:
    type: string
  description:
    type: string
    nullable: true
  outcomes:
    type: array
    items:
      $ref: '#/components/schemas/Outcome'
EventOdds object
type: object
required:
  - event_id
  - home_team
  - away_team
  - commence_time
  - status
  - markets
properties:
  event_id:
    type: string
  home_team:
    type: string
  away_team:
    type: string
  commence_time:
    type: string
    format: date-time
  status:
    type: string
  markets:
    type: array
    items:
      $ref: '#/components/schemas/Market'
ArbOpportunity object
type: object
properties:
  event_id:
    type: string
  home_team:
    type: string
  away_team:
    type: string
  market_key:
    type: string
  profit_percent:
    type: number
  implied_prob_sum:
    type: number
  stakes:
    type: array
    items:
      type: number
  legs:
    type: array
    items:
      type: object
      properties:
        outcome:
          type: string
        book:
          type: string
        price:
          type: number
PriceHistoryPoint object
type: object
properties:
  price: {}
  price_decimal:
    type: number
  point:
    type: number
    nullable: true
  recorded_at:
    type: string
    format: date-time
OutcomeMovement object
type: object
properties:
  outcome_name:
    type: string
  history:
    type: array
    items:
      $ref: '#/components/schemas/PriceHistoryPoint'
BookMovement object
type: object
properties:
  book_key:
    type: string
  outcomes:
    type: array
    items:
      $ref: '#/components/schemas/OutcomeMovement'
MarketMovement object
type: object
properties:
  market_key:
    type: string
  books:
    type: array
    items:
      $ref: '#/components/schemas/BookMovement'
AlertRequest object
type: object
required:
  - event_id
  - market_key
  - outcome_name
  - threshold_type
  - threshold_value
  - webhook_url
properties:
  event_id:
    type: string
  market_key:
    type: string
  outcome_name:
    type: string
  threshold_type:
    type: string
    enum:
      - above
      - below
      - movement
  threshold_value:
    type: number
  webhook_url:
    type: string
    format: uri
HoldResult object
type: object
properties:
  hold_percent:
    type: number
  implied_prob_sum:
    type: string
  outcomes:
    type: array
    items:
      type: object
      properties:
        outcome:
          type: string
        book:
          type: string
          nullable: true
        decimal:
          type: number
        implied_prob:
          type: number
        fair_value_prob:
          type: number
        fair_decimal:
          type: number
          nullable: true
        fair_american:
          type: string
          nullable: true
ArbCalcResult object
type: object
properties:
  arb_exists:
    type: boolean
  profit_percent:
    type: number
    nullable: true
  guaranteed_profit:
    type: number
    nullable: true
  total_stake:
    type: number
  implied_prob_sum:
    type: number
  message:
    type: string
    nullable: true
  legs:
    type: array
    items:
      type: object
      properties:
        outcome:
          type: string
        decimal:
          type: number
        book:
          type: string
          nullable: true
        stake:
          type: number
          nullable: true
        gross_return:
          type: number
          nullable: true
HedgeCalcResult object
type: object
properties:
  hedge_stake:
    type: number
  profit_if_leg1_wins:
    type: number
  profit_if_leg2_wins:
    type: number
  guaranteed_profit:
    type: number
    nullable: true
  is_free_money_hedge:
    type: boolean
  inputs:
    type: object
    properties:
      original_stake:
        type: number
      leg1_decimal:
        type: number
      leg1_american:
        type: string
      leg2_decimal:
        type: number
      leg2_american:
        type: string
      is_freebet:
        type: boolean
ParlayCalcResult object
type: object
properties:
  combined_decimal:
    type: number
  combined_american:
    type: string
  stake:
    type: number
  payout:
    type: number
  profit:
    type: number
  implied_probability:
    type: string
  leg_count:
    type: integer
  legs:
    type: array
    items:
      type: object
      properties:
        label:
          type: string
        decimal:
          type: number
        american:
          type: string
        implied_prob:
          type: number
LineShoppingResult object
type: object
properties:
  outcome:
    type: string
  best_book:
    type: string
  best_decimal:
    type: number
  best_american:
    type: string
  worst_decimal:
    type: number
  difference_units:
    type: number
  comparison:
    type: array
    items:
      type: object
      properties:
        book:
          type: string
        decimal:
          type: number
        american:
          type: string
        point:
          type: number
          nullable: true
        implied_prob:
          type: number