---
title: "Consensus line movement (Pro+)"
method: GET
path: "/v1/fights/{id}/odds/history"
tags: ["Odds"]
---

# Consensus line movement (Pro+)

`GET /v1/fights/{id}/odds/history`

## Request

- Base URL: `https://api.ufcalendar.com`
- URL: `https://api.ufcalendar.com/v1/fights/{id}/odds/history`
- Auth: HTTP bearer

Every stored consensus point for one bout, oldest first: the line-movement series. A point is stored only when the consensus changes, so the timestamps follow real market moves, not our refresh cadence. Each point: `id`, `a` / `b` (`american`, `decimal`, raw `implied_probability`), `favourite`, `fair_probability_a` (margin removed), `sources` (how many sportsbooks backed it) and `recorded_at`. `from` / `to` (inclusive `YYYY-MM-DD`) narrow the window; `limit` 1 to 500 (default 100); pass `meta.pagination.next_cursor` back as `cursor` for the next page. Requires Pro or higher (403 `tier_required`, answered before the fight is looked up). Book identities are never exposed. Information only, not betting advice.

## Path parameters

- `id` integer, required

## Query parameters

- `from` string, date
- `to` string, date
- `cursor` string
- `limit` integer

## Response `200`

Consensus points, oldest first

- FightOddsHistoryResponse: Oldest first.
  - `data` OddsHistoryPoint[], required
    - `id` integer, required: Point id (the cursor unit).
    - `a` OddsLine, required: One corner's side of a consensus point.
      - `american` integer, required: American moneyline (`-152`, `+131`).
      - `decimal` number, required: Decimal odds, 3 places.
      - `implied_probability` number, required: Raw implied probability (market margin included), 4 places.
    - `b` OddsLine, required: One corner's side of a consensus point.
      - `american` integer, required: American moneyline (`-152`, `+131`).
      - `decimal` number, required: Decimal odds, 3 places.
      - `implied_probability` number, required: Raw implied probability (market margin included), 4 places.
    - `favourite` 'a' | 'b' | 'null', nullable, required: The corner the market prices as more likely; null on an even pair.
    - `fair_probability_a` number, required: Probability of corner a with the market margin removed (pA / (pA + pB)), 4 places.
    - `sources` integer, required: How many sportsbooks backed this consensus point. Book identities are never exposed.
    - `recorded_at` string, date-time, required: When this consensus point was recorded.
  - `meta` object, required
    - `fight_id` integer, required
    - `note` string, required
    - `pagination` CursorPagination, required: Cursor pagination WITHOUT the applied `limit` (the newer list routes — changes, fight search, splits, venues, articles — do not echo it).
      - `next_cursor` string, nullable, required: Opaque; pass as `?cursor=`. Null on the last page.
      - `has_more` boolean, required
    - `ignored_params` string[]: Present only when the request carried query parameters this endpoint does not take — they were ignored. Same list as the `X-UFCalendar-Ignored-Params` header. Sorted, unique.

## Other responses

- `304`: Not Modified — `If-None-Match` matched the current weak `ETag`. Empty body.
- `400`: Invalid input (`invalid_cursor`, `invalid_date`). Dates must be real calendar days (`2026-02-30` is `invalid_date`).
- `401`: Missing/invalid API key (`unauthorized`).
- `403`: Requires Pro or above (`tier_required`).
- `404`: Not found (`not_found`; staged-org data 404s until the org is promoted).
- `429`: Rate limited (`rate_limited`) or monthly quota reached (`quota_exceeded`). Honor `Retry-After`.
- `500`: Something broke on our side (`internal_error`). It has been logged under the `request_id`.

---

[API](https://skmtc.dev/ufcalendar/apis/ufcalendar-fight-api.md) · [All operations](https://skmtc.dev/ufcalendar/apis/ufcalendar-fight-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ufcalendar/apis/ufcalendar-fight-api/revisions/9623cc8fb70b?raw)
