Skip to content
MMA Calendar
MMA Fight Data API · Code guide

How do I get UFC closing odds and line movement?

Request the bout's odds from the MMA Fight Data API, api.fight_odds(fight_id) in Python. It returns one UFCalendar consensus line, the mean across the sportsbooks we track: the opening line, the closing line at the start of the event, the latest point, the movement between them and how many sportsbooks backed each point. Every plan gets these lines; the full movement series is on Pro and Business. Information only, not betting advice.

Free plan: 1,000 requests a month, no card. Paid plans from $19/mo.

Endpoints used

Endpoints used
Method & pathWhat it returnsPlan
GET /v1/fights/{id}/oddsOne bout's consensus moneyline: American and decimal prices, implied and de-vigged probabilities, the opening and closing line and the movement between them.All plans
GET /v1/events/{slug}/oddsThe consensus line for every bout on a card in one call, in card order, unpriced bouts included.All plans
GET /v1/fights/{id}/odds/historyEvery movement point of a bout's consensus line, oldest first, with the number of sportsbooks behind each point.Pro and up
POST /v1/webhook-endpointsA signed odds.moved delivery when a bout's consensus line moves 5 points of implied probability or the favourite flips.Pro and up

The code

curl and Python read one bout's opening and closing line, then the first points of its movement series; the JavaScript block prints the closing line of every bout on a card.

Every block runs as shown with your API key in the UFCAL_API_KEY environment variable. Python uses pip install ufcalendar and JavaScript or TypeScript uses npm install @ufcalendar/sdk; any HTTP client works too.

curl
curl "https://api.ufcalendar.com/v1/fights/83680/odds" \
  -H "Authorization: Bearer $UFCAL_API_KEY"

# Pro and Business: the full movement series
curl "https://api.ufcalendar.com/v1/fights/83680/odds/history?limit=100" \
  -H "Authorization: Bearer $UFCAL_API_KEY"
Python
# pip install ufcalendar
from ufcalendar import FightAPI

api = FightAPI()  # reads UFCAL_API_KEY from the environment

o = api.fight_odds(83680)  # Petr Yan vs Merab Dvalishvili, UFC 333
a, b = o["fighter_a"]["name"], o["fighter_b"]["name"]
op, cl = o["opening"], o["closing"]
print(f"{a}: opened {op['a']['american']:+d}, closed {cl['a']['american']:+d}")
print(f"{b}: opened {op['b']['american']:+d}, closed {cl['b']['american']:+d}")
print(f"closing line from {cl['sources']} sportsbooks; de-vigged {a} {cl['fair_probability_a']:.1%}")
print("movement on", a, o["movement"]["delta_points_a"], "points since", o["movement"]["since"][:10])

# Pro and Business plans: every point the consensus moved, oldest first
for p in api.fight_odds_history(83680, limit=5):
    print(p["recorded_at"][:16], p["a"]["american"], p["b"]["american"], p["sources"], "books")
JavaScript
// npm install @ufcalendar/sdk
import { FightAPI } from "@ufcalendar/sdk";

const api = new FightAPI(); // reads UFCAL_API_KEY from the environment

// Closing line for every bout on a card, in card order
const { fights } = await api.eventOdds("ufc-333-2026-10-24");
for (const f of fights) {
  const c = f.closing;
  if (!c) { console.log(`${f.fighter_a.name} vs ${f.fighter_b.name}: no line`); continue; }
  const pa = Math.round(c.fair_probability_a * 100);
  console.log(`${f.fighter_a.name} ${c.a.american} / ${f.fighter_b.name} ${c.b.american}  (${pa}% / ${100 - pa}%)`);
}

Sample response

A real response from the live API, trimmed for length. … marks removed fields.

GET /v1/fights/83680/odds
{
  "data": {
    "fight_id": 83680,
    "status": "completed",
    "event": {
      "id": 2530,
      "slug": "ufc-333-2026-10-24",
      "title": "UFC 333: Volkanovski vs Evloev",
      "org": "ufc",
      "starts_at": "2026-10-24T14:00:00.000Z"
    },
    "fighter_a": { "id": 360, "slug": "petr-yan", "name": "Petr Yan" },
    "fighter_b": { "id": 194, "slug": "merab-dvalishvili", "name": "Merab Dvalishvili" },
    "consensus": …,
    "opening": {
      "a": { "american": -157, "decimal": 1.637, "implied_probability": 0.6109 },
      "b": { "american": 130, "decimal": 2.3, "implied_probability": 0.4348 },
      "favourite": "a",
      "fair_probability_a": 0.5842,
      "sources": 4,
      "recorded_at": "2026-08-16T20:50:33.686Z"
    },
    "closing": {
      "a": { "american": -156, "decimal": 1.641, "implied_probability": 0.6094 },
      "b": { "american": 131, "decimal": 2.31, "implied_probability": 0.4329 },
      "favourite": "a",
      "fair_probability_a": 0.5847,
      "sources": 18,
      "recorded_at": "2026-10-24T13:50:18.968Z"
    },
    "movement": {
      "delta_points_a": -0.15,
      "direction": "b",
      "since": "2026-08-16T20:50:33.686Z"
    },
    "points": 136,
    "updated_at": "2026-10-24T13:50:18.968Z",
    "props": …
  },
  "meta": {
    "note": "UFCalendar consensus line across the sportsbooks we track (book identities are not exposed). Information only, not betting advice.",
    "history": "/v1/fights/83680/odds/history"
  }
}

Notes and pitfalls

  • One line per bout, never a single sportsbook's: the mean across the sportsbooks we track, with prediction markets and exchanges left out. sources counts the books behind a point, and no book is named.
  • opening is the first point recorded, closing the last point at or before the event start (settled bouts only) and consensus the latest. Each side carries American and decimal prices and an implied probability; fair_probability_a removes the margin.
  • movement.delta_points_a is the shift from the opening line to the latest point, in implied-probability points on fighter_a, and direction names the side it moved toward. A new point is stored only when the line moves.
  • The movement series and the odds.moved webhook need Pro or Business; lower plans get 403 tier_required. An unpriced bout still appears, with null lines and points at 0.

Related pages

More code guides

All code guides →

UFCalendar is an independent publication, not affiliated with UFC/Zuffa/TKO, PFL, OKTAGON MMA, BKFC, RIZIN FF or any promotion. Organization names are used to describe the data. Terms of service · Refund Policy

How do I get UFC closing odds and line movement? | UFCalendar