Skip to main content

ufcalendar — Python client for the UFCalendar Fight API

The UFCalendar Fight API is a REST API for MMA data: UFC, PFL, OKTAGON, BKFC and RIZIN events, full fight cards, results within minutes, per-fight and round-by-round statistics, complete fighter careers, the only MMA data API with point-in-time UFC rankings history back to 2013, and the only one serving judges' scorecards — every official, every round: UFC back to 1995, PFL to 2018, OKTAGON to 2025 and RIZIN from March 2026 — plus the UFCalendar consensus odds line (current, opening and closing on every plan, line movement on Pro). This package is a thin requests wrapper over it — one method per endpoint, cursor pagination handled for you.

pip install ufcalendar

Get a key (free 1-day trial, 100 requests, no card) at https://www.ufcalendar.com/account/api?trial=1. Paid plans from $19/month for 30,000 requests (Pro $49 for 200,000, Business $149 for 1,000,000), hard caps, no overage.

Quickstart

from ufcalendar import FightAPI

api = FightAPI("ufcalendar_...")            # or export UFCAL_API_KEY=...

# Upcoming UFC schedule, soonest first
for ev in api.events(org="ufc", limit=5):
    print(ev["starts_at"], ev["title"], "PPV" if ev["is_ppv"] else "")

# Full card + results of the newest completed UFC event
latest = next(api.events(org="ufc", status="completed", limit=1))
card = api.event(latest["slug"])
for f in card["fights"]:
    r = f.get("result")
    if r:
        print(f["fighter_a"]["name"], "vs", f["fighter_b"]["name"], "->", r["method"], f"R{r['round']} {r['time']}")

# Round-by-round statistics for one bout
rounds = api.fight_rounds(card["fights"][0]["id"])

# UFC rankings on any date since February 2013 (rank 0 = champion)
board = api.rankings("ufc", date="2016-11-14")
lw = next(d for d in board["divisions"] if d["division"] == "lightweight")
print(board["snapshot_date"], [e["name"] for e in lw["entries"][:5]])

# A fighter's complete multi-promotion career
history = api.fighter_history("islam-makhachev")

What's covered

Method Endpoint
plans() GET /v1/plans — plans, quotas, trial terms, MCP endpoint (no key required)
events(org, status, from_date, to_date, order, is_title_card, is_ppv, include=["headline"]) GET /v1/events (paginated; headline = each card's main event and its result)
event(slug, include=["eta", "odds"]) / event_changes(slug) GET /v1/events/{slug} / …/changes (eta = per-bout estimated start; odds = each bout's latest consensus line)
event_watch(slug, country) GET /v1/events/{slug}/watch — how to watch one event, per country: the rights deals for its series merged with the event's own listings
changes(org, since, kind) GET /v1/changes — the card-change feed across every event, newest first (paginated; default last 90 days)
fight(id, include=["odds"]) / fight_stats(id) / fight_rounds(id) GET /v1/fights/{id} / …/stats / …/rounds
fight_odds(id) / event_odds(slug) GET /v1/fights/{id}/odds / GET /v1/events/{slug}/odds — the UFCalendar consensus line: current, opening, closing (settled bouts), movement and sources (how many sportsbooks backed each point). Information only, not betting advice
fight_odds_history(id, from_date, to_date) GET /v1/fights/{id}/odds/history — every consensus point, oldest first (paginated; Pro plans and up)
find_fights(org, title_only, method, division, fighter, winner, from_date, to_date, main_events_only, order) GET /v1/fights/search — completed bouts, filtered, newest first (at least one narrowing filter; 25 a page, 10 pages deep)
fight_scorecards(id) GET /v1/fights/{id}/scorecards — judges, rounds, totals, deductions
judges(q, org, min_fights) / judge(id) / judge_scorecards(id) GET /v1/judges / …/{id} / …/{id}/scorecards (last_meta["league"] = baseline rates; each card row flags lone_dissent / split and lists colleagues)
split_decisions(org, from_date, to_date) GET /v1/scorecards/splits — split and majority decisions, newest first, with every judge's card and the dissenters named (paginated)
fighters(q, org, country) / fighter(slug, include=["bonuses", "credentials"]) GET /v1/fighters / …/{slug} (always carries next_fight / last_fight; bonuses = the UFC bonus ledger; credentials = grappling and wrestling pedigree, gyms and coaches, each row sourced and confidence-graded)
fighter_history / fighter_stats / fighter_rankings / fighter_power_index GET /v1/fighters/{slug}/…
rankings(org, date) / division_rankings(org, division) / champions() GET /v1/rankings/… / /v1/champions (entries carry movement, is_new, country_code; last_meta names the previous/next snapshot)
power_index(org, view, division, days, limit) / predictions_upcoming(event) GET /v1/power-index/{org} (view: current · movers · peaks) / /v1/predictions/upcoming
matchmaker(org, division, limit) / whos_next(fighter, limit) GET /v1/matchmaker/{org} / …/next/{fighter} — UFCalendar's matchmaker: the fights worth making (scored 0–100, per division or across the roster; not bookings), and one fighter's best next opponents with the win probability for each and the bout already booked
leaderboard(org, metric, division, country, population, limit) / record_book(org, division, country, population, scope, top) GET /v1/stats/leaders / …/record-book — the Record Book: one leaderboard (48 metrics; ties flagged, sample size on every row), or every board's top rows grouped by category
org_division(org, division) GET /v1/orgs/{org}/divisions/{division} — one weight class: rankings board, upcoming bouts, latest results, roster by recency
year_stats(year, org) GET /v1/stats/years/{year} — one calendar year in numbers for one promotion (or every covered one): methods, divisions, fastest finishes, upsets, Power Index climbers, busiest fighters, countries, judges
compare(a, b) GET /v1/compare — two fighters side by side: bios, career stats, strike mix, streaks, previous meetings, common opponents, booked bout, model prediction (UFC), Power Index
event_storylines(slug) GET /v1/events/{slug}/storylines — the talking points of one card: title fights, eliminators, closest bout, rematches, streaks, debuts, returns, ranked fighters, nations
event_pickem(slug) GET /v1/events/{slug}/pickem — how the UFCalendar community is picking each bout on one card (picks per corner, total, percentage); crowd sentiment, not a market and not a forecast
broadcast_rights(org, country, series) / venue(id) / search(q) / usage() misc (series="dwcs"/"rtufc" = a UFC sub-series grid)
venues(q, country) / venue_events(id, status, from_date, to_date, order) GET /v1/venues / …/{id}/events — venue search, and every covered event at one venue, newest first (paginated)
articles(q, tag, locale) / article(slug, locale) GET /v1/articles / …/{slug} — UFCalendar's own editorial archive, newest first, in any of the 13 site languages (paginated), and one article's full Markdown body
create_webhook_endpoint(url, events) … POST /v1/webhook-endpoints (Pro+; kinds event.announced, fight.result, card.changed, event.completed, odds.moved)
calendar_ics_url(org) GET /v1/calendar/{org}.ics

Full reference: https://api.ufcalendar.com/docs · OpenAPI 3.1: https://api.ufcalendar.com/openapi.json · Also on npm (TypeScript client), RapidAPI Hub, Postman and the hosted MCP server

Consensus odds

o = api.fight_odds(83379)
print(o["consensus"]["a"]["american"], o["consensus"]["b"]["american"], o["consensus"]["sources"])
print(o["movement"]["delta_points_a"], o["closing"])   # closing = last line before the start (settled bouts)

for point in api.fight_odds_history(83379):               # Pro plans and up
    print(point["recorded_at"], point["a"]["american"], point["b"]["american"])

One anonymised UFCalendar consensus line per corner across the sportsbooks we track; book identities are never exposed. Information only, not betting advice.

Webhooks instead of polling (Pro and up)

ep = api.create_webhook_endpoint("https://example.com/hooks/ufcal", ["fight.result", "card.changed"])
print(ep["secret"])   # shown once; verify X-UFCalendar-Signature with it

odds.moved fires when the consensus line on an upcoming bout moves 5+ implied-probability points on corner a, or the favourite flips, measured against the last line delivered, so a slow drift arrives once. Information only, not betting advice.

Errors and rate limits

Every error raises FightAPIError with .status, .code, .message, .request_id. After each call api.last_rate_limit holds the X-RateLimit-* headers.

Notes

  • Odds are the UFCalendar consensus line only (no book identities). Information only, not betting advice.
  • Fighter images are Wikimedia Commons / Creative Commons files: display the license and artist fields as a credit.
  • Not affiliated with UFC, Zuffa, TKO or any promotion. Terms: https://www.ufcalendar.com/developers/terms

MIT licensed.

Release files for ufcalendar 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ufcalendar 0.8.0
File Size Uploaded
ufcalendar-0.8.0.tar.gz 19.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ufcalendar 0.8.0
File Interpreter ABI Platform
ufcalendar-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.5 kB

Release files / ufcalendar-0.8.0.tar.gz

Download URL ufcalendar-0.8.0.tar.gz
Size 19.2 kB
Tags Source
SHA-256 checksum
How to use checksums
d5d1cbea68c702e97d485a7259135c7446d1ca914029d909c5c93b343d70459c
BLAKE2b-256 checksum
How to use checksums
f984d9517f8a0522b84c72c552a0f5d51cc2950657c5557ed877d5be346cac02
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / ufcalendar-0.8.0-py3-none-any.whl

Download URL ufcalendar-0.8.0-py3-none-any.whl
Size 15.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0d5edfcdb1039632f622553bcf1451732270400f5880ac46fd7755f9ea56a272
BLAKE2b-256 checksum
How to use checksums
cfcbafcb815bf6fe014ddb15e4e7dc129114ca7f0eaf96983f35cf19cb1b4cab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page