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 UFC rankings API with point-in-time 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. 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"]) / event_changes(slug) GET /v1/events/{slug} / …/changes (eta = per-bout estimated start)
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)
event_live(slug) GET /v1/events/{slug}/live — real-time LiveState on fight night (Pro plans and up); the same document streams over wss://live.ufcalendar.com/v1?key=…
live_stream(slug) the WebSocket itself — subscribe to wss://live.ufcalendar.com/v1 and iterate every frame (Pro plans and up)
fight(id) / fight_stats(id) / fight_rounds(id) GET /v1/fights/{id} / …/stats / …/rounds
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+)
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 and Postman

Live stream (UFC fight nights, Pro plans and up)

The UFC live API: a WebSocket that pushes the fight-night document the moment it changes — card order and statuses, the bout in progress (round, running clock, unofficial in-fight stats, per-round splits, a timestamped action timeline) and the last result. It is the streaming half of event_live(), and it is the UFC live stats API you want instead of polling.

The socket ships as an optional extra so a REST-only install stays requests-thin:

pip install 'ufcalendar[live]'
from ufcalendar import FightAPI

api = FightAPI()  # UFCAL_API_KEY

for frame in api.live_stream("ufc-331", until_final=True):
    # frame["type"]: "snapshot" | "update" | "fight.final" | "event.completed"
    state = frame["data"]
    if not state:
        continue                      # nothing streaming yet
    cur = state["current"] or {}
    print(frame["type"], "R", cur.get("round"), cur.get("clock_sec"))

until_final=True stops after the first fight.final frame; the default runs until the socket ends. A dropped connection is re-subscribed once automatically.

Runnable ticker: examples/live_ticker.py — ~40 lines that print R2 3:41 · Oliveira 41 vs Makhachev 37 sig. strikes as the round unfolds.

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

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

  • No betting odds are served, by design.
  • 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.6.1

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.6.1
File Size Uploaded
ufcalendar-0.6.1.tar.gz 20.9 kB Details

Built distribution (wheel)

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

Total release size: 36.9 kB

Release files / ufcalendar-0.6.1.tar.gz

Download URL ufcalendar-0.6.1.tar.gz
Size 20.9 kB
Tags Source
SHA-256 checksum
How to use checksums
08e80ed6a430a9107c0c0d9919b8feebae0809ed499501006c36b6896c287267
BLAKE2b-256 checksum
How to use checksums
ee0acb89240ea3f4e4e968585dc278de85f3df04dc2a1ec9d9e1f47895285ab1
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.6.1-py3-none-any.whl

Download URL ufcalendar-0.6.1-py3-none-any.whl
Size 15.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3156ea74d40bc057151075df31581a4823ab14e9e719223a3856771449268bf7
BLAKE2b-256 checksum
How to use checksums
efdb0fda80ad7032c4052380d9296645f16c1aeb25d6ded7fe009e8e6132ba6e
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.6.1 This release

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