Skip to main content
Glama
UFCalendar

ufcalendar

by UFCalendar

@ufcalendar/sdk — MMA Fight Data API client for TypeScript

UFC, PFL, OKTAGON, BKFC and RIZIN events, fight stats, results, rankings history and judges' scorecards from one typed client.

The MMA Fight Data API by UFCalendar (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 — plus the UFCalendar consensus odds line (current, opening and closing on every plan, line movement on Pro). This package is a dependency-free fetch wrapper over it: one method per endpoint, cursor pagination handled for you, ESM + CJS, Node 18+, browsers and edge runtimes.

npm install @ufcalendar/sdk

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 in 3 lines

import { FightAPI } from '@ufcalendar/sdk';
const api = new FightAPI('ufcalendar_...'); // free 1-day trial key, no card
for await (const ev of api.events({ org: 'ufc', limit: 1 })) console.log(ev.title); // the next UFC card

Related MCP server: MCP Prediction Markets

Quickstart

import { FightAPI } from '@ufcalendar/sdk';

const api = new FightAPI(process.env.UFCALENDAR_API_KEY); // or new FightAPI({ apiKey })

// Upcoming UFC schedule, soonest first
for await (const ev of api.events({ org: 'ufc', limit: 5 })) {
  console.log(ev.starts_at, ev.title, ev.is_ppv ? 'PPV' : '');
}

// Full card + results of the newest completed UFC event
const [latest] = await Array.fromAsync(api.events({ org: 'ufc', status: 'completed', limit: 1 }));
const card = await api.event(latest!.slug);
for (const f of card.card) {
  if (f.result) {
    console.log(f.fighter_a?.name, 'vs', f.fighter_b?.name, '->', f.result.method, `R${f.result.round}`);
  }
}

// The judges' scorecards for one bout — the official commission record
const cards = await api.fightScorecards(card.card[0]!.id);

// UFC rankings on any date since February 2013 (rank 0 = champion)
const board = await api.rankings('ufc', { date: '2016-11-14' });

// A fighter's complete multi-promotion career
const history = await api.fighterHistory('islam-makhachev');

List endpoints are async generators that follow meta.pagination.next_cursor for you; pass limit to stop after N rows. Everything else returns the envelope's data. The last response's meta and rate-limit headers stay on the client (api.lastMeta, api.lastRateLimit).

What's covered

Method

Endpoint

plans()

GET /v1/plans — plans, quotas, trial terms, MCP endpoint (no key required)

orgs() / org(slug)

GET /v1/orgs / …/{slug}

events({ org, status, from, to, order, isTitleCard, isPpv, include: ['headline'], limit })

GET /v1/events (paginated; headline = each card's main event and its result)

event(slug, { include: ['eta', 'odds'] }) / eventChanges(slug)

GET /v1/events/{slug} / …/changes (eta = per-bout estimated start; odds = each bout's latest consensus line)

eventWatch(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, limit })

GET /v1/changes — the card-change feed across every event, newest first (paginated; default last 90 days)

fight(id, { include: ['odds'] }) / fightStats(id) / fightRounds(id)

GET /v1/fights/{id} / …/stats / …/rounds

fightOdds(id) / eventOdds(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), plus props (method of victory per corner, total rounds over/under, goes the distance). Information only, not betting advice

fightOddsHistory(id, { from, to })

GET /v1/fights/{id}/odds/history — every consensus point, oldest first (async generator; Pro plans and up)

findFights({ org, titleOnly, method, division, fighter, winner, from, to, mainEventsOnly, order, limit })

GET /v1/fights/search — completed bouts, filtered, newest first (at least one narrowing filter; 25 a page, 10 pages deep)

fightScorecards(id)

GET /v1/fights/{id}/scorecards — judges, rounds, totals, deductions

judges({ q, org, minFights }) / judge(id) / judgeScorecards(id)

GET /v1/judges / …/{id} / …/{id}/scorecards (lastMeta.league = baseline rates; each card row flags lone_dissent / split and lists colleagues)

splitDecisions({ org, from, to, limit })

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)

fighterHistory / fighterStats / fighterRankings / fighterPowerIndex

GET /v1/fighters/{slug}/…

rankings(org, { date }) / divisionRankings(org, division) / champions()

GET /v1/rankings/… / /v1/champions (entries carry movement, is_new, country_code; lastMeta names the previous/next snapshot)

powerIndex(org, { view, division, days, limit }) / predictionsUpcoming({ event })

GET /v1/power-index/{org} (view: current · movers · peaks) / /v1/predictions/upcoming

matchmaker(org, { division, limit }) / whosNext(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 }) / recordBook(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

orgDivision(org, division)

GET /v1/orgs/{org}/divisions/{division} — one weight class: rankings board, upcoming bouts, latest results, roster by recency

yearStats(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

eventStorylines(slug)

GET /v1/events/{slug}/storylines — the talking points of one card: title fights, eliminators, closest bout, rematches, streaks, debuts, returns, ranked fighters, nations

eventPickem(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

broadcastRights(org, { country, series }) / venue(id) / search(q) / usage()

misc (series: 'dwcs' | 'rtufc' = a UFC sub-series grid)

venues({ q, country, limit }) / venueEvents(id, { status, from, to, order, limit })

GET /v1/venues / …/{id}/events — venue search, and every covered event at one venue, newest first (paginated)

articles({ q, tag, locale, limit }) / 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

webhookEndpoints() / createWebhookEndpoint(url, events) / rotateWebhookSecret(id) / deleteWebhookEndpoint(id)

/v1/webhook-endpoints (Pro+; kinds event.announced, fight.result, card.changed, event.completed, odds.moved — the consensus line moved 5+ points or the favourite flipped)

calendarIcsUrl(org)

GET /v1/calendar/{org}.ics (URL builder — no request)

get(path, params) / getWithMeta(path, params)

escape hatch for anything new

Hand-written types cover events, fights, fighters, rankings, scorecards, consensus odds and plans; the generated paths / operations types from the OpenAPI document are exported for everything else.

Consensus odds

const o = await api.fightOdds(83379);
console.log(o.consensus?.a.american, o.consensus?.b.american, o.consensus?.sources);
console.log(o.movement?.delta_points_a, o.closing); // closing = last line before the start (settled bouts)

for await (const point of api.fightOddsHistory(83379)) {  // Pro plans and up
  console.log(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.

Agents and MCP

The same data is a Model Context Protocol server, so an agent can call it without an HTTP client:

claude mcp add --transport http ufcalendar https://api.ufcalendar.com/mcp \
  --header "Authorization: Bearer $UFCALENDAR_API_KEY"

There is an installable agent skill too: npx skills add UFCalendar/fight-api-skill. Agent quickstart: https://www.ufcalendar.com/developers/agents.

Options

new FightAPI({
  apiKey,                 // or UFCALENDAR_API_KEY / UFCAL_API_KEY in the environment
  baseUrl,                // default https://api.ufcalendar.com/v1
  fetch,                  // inject your own fetch
  timeoutMs,              // default 30_000
  headers,                // extra headers on every request
});

Errors and rate limits

Every error throws FightAPIError with .status, .code, .message and .requestId — quote the request id when you write to api@ufcalendar.com. After each call api.lastRateLimit holds the X-RateLimit-* headers (monthly quota).

Notes

  • Odds are the UFCalendar consensus line only (no book identities). Information only, not betting advice.

  • Fighter images are Wikimedia Commons / Creative Commons files: displaying the license and artist fields as a credit is a licence requirement, not a style preference.

  • Scorecards are the official commission record. Media-member and fan scorecards are not part of the API.

  • source / source_slug values (typed as PublicSource) name official publishers only — ufc-stats, ufc-official, ufc-official-cards, a promotion's *-official, wikipedia, ufcalendar. Every other publisher arrives as public-records (career records, history rows) or commission-record (judges' scorecards).

  • Not affiliated with UFC, Zuffa, TKO or any promotion. Terms: https://www.ufcalendar.com/developers/terms

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

MIT licensed.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Provides comprehensive NBA statistics via Model Context Protocol, enabling queries for player stats, game scores, team info, and advanced analytics through natural language.
    21
    11
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Provides search, trending, odds, arbitrage, and category browsing for prediction markets (Polymarket & Kalshi) via the Model Context Protocol, enabling AI agents to access live market data without API keys.
    5
    1
    MIT
  • A
    license
    A
    quality
    F
    maintenance
    Provides AI agents with direct access to KingsPlaybook's sports data including confirmed lineups, player projections, game lines, and pick history via native MCP tools.
    5
    17 npm
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Provides AI agents with live, grounded sports data including model probabilities, track records, and European soccer and tennis arbitrage opportunities, so they answer from real numbers instead of stale guesses.
    13
    19 npm
    1
    MIT