MMA fighter, event and fight card data. REST, JSON, versioned.
Every endpoint is GET, returns JSON, and lives under a
version prefix. All times are ISO‑8601 in UTC — a fight card is
read in every timezone there is, so nothing is ever returned as a
local time without an offset.
https://ufc.gg/api/v1
Send your key as a bearer token. There is no anonymous access and no public read tier.
Authorization: Bearer ufcgg_a1b2c3d4e5.YOUR_SECRET
If Authorization is reserved by something else in your stack,
X-Api-Key is accepted instead.
Confirm your key works and see what you have left:
curl -H "Authorization: Bearer $UFCGG_KEY" \
https://ufc.gg/api/v1/me
Then fetch a card:
curl -H "Authorization: Bearer $UFCGG_KEY" \
"https://ufc.gg/api/v1/events?status=scheduled&per_page=1"
/fighters
| Parameter | Type | Notes |
|---|---|---|
search | string | Matches the start of a first name, last name or nickname. |
division | string | Exact division name, e.g. Lightweight. |
active | boolean | Currently on a roster. |
updated_since | date | Only records changed since. See incremental sync. |
order | string | name (default) or updated_at. |
per_page | integer | 1–100. Defaults to 50. |
/fighters/{slug}
The detailed record: physicals, stance, finish breakdown, career striking and grappling averages, and a licensed image where one exists.
{
"data": {
"slug": "islam-makhachev",
"first_name": "Islam",
"last_name": "Makhachev",
"nickname": null,
"gender": "male",
"division": "Welterweight",
"is_active": true,
"is_champion": true,
"record": { "wins": 27, "losses": 1, "draws": 0, "no_contests": 0 },
"height_cm": 178,
"reach_cm": 178,
"statistics": {
"significant_strikes_landed_per_min": "2.35",
"takedown_average": "3.30"
},
"image": {
"url": "https://…/cutout.png",
"license": "CC BY-SA 4.0",
"attribution": "Photographer name / CC BY-SA 4.0 - this image and any version of it are licensed under the same terms.",
"share_alike": true
},
"updated_at": "2026-08-19T14:02:11+00:00"
}
}
/events
| Parameter | Type | Notes |
|---|---|---|
status | string | scheduled, live, completed, cancelled, postponed. |
from / to | date | Bounds on the event start. |
updated_since | date | Only cards changed since. |
per_page | integer | 1–100. Defaults to 25. |
/events/{slug}
The whole card in one call, in fight order, with segment, corners and results. This is the call most integrations are actually built on.
{
"data": {
"slug": "ufc-330-makhachev-vs-machado-garry",
"name": "UFC 330: Makhachev vs Machado Garry",
"status": "completed",
"starts_at": "2026-08-16T02:00:00+00:00",
"venue": { "name": "T-Mobile Arena", "city": "Las Vegas", "country": "United States" },
"card": [
{
"card_segment": "main_card",
"bout_order": 0,
"is_main_event": true,
"is_title_fight": true,
"weight_class": "Welterweight",
"scheduled_rounds": 5,
"status": "completed",
"red_corner": { "slug": "islam-makhachev", "name": "Islam Makhachev" },
"blue_corner": { "slug": "ian-machado-garry", "name": "Ian Machado Garry" },
"winner": "islam-makhachev",
"method": "unanimous_decision",
"method_detail": "Decision - Unanimous",
"ended_round": 5,
"ended_at_seconds": 300
}
]
}
}
/me
Your plan, your usage this month, and your license terms as machine-readable flags — so your own code can check what it is permitted to do rather than relying on somebody having read the contract.
{
"data": {
"key": "ufcgg_a1b2c3d4e5",
"plan": "api-pro",
"monthly_limit": 500000,
"used_this_month": 18422,
"credits_remaining": 0,
"rate_limit_per_minute": 300,
"license": {
"internal_display": true,
"redistribution": false,
"resale": false,
"sublicensed_api": false,
"cache_days": 90,
"attribution_required": false,
"ai_training": false
}
}
}
Two separate limits, with two different meanings:
429 with a Retry-After
header. Back off and retry.
402, not 429, because
it is a billing state rather than a temporary one. Retrying will not
help; upgrading will.
Metered responses carry your position on every call:
X-RateLimit-Limit: 500000
X-RateLimit-Remaining: 481578
X-RateLimit-Reset: 2026-09-01T00:00:00+00:00
5xx)
are not counted against your quota.
Running out mid-month does not have to stop your integration. Prepaid credits are spent automatically once the monthly allowance is gone, one credit per request, and every response reports what is left:
X-Credits-Remaining: 48213
The 402 above is returned only when the allowance
and the credit balance are both exhausted. Buy packs from your
license page, or ask us to add them.
Unused credits carry over for as long as the account stays active:
every request resets a rolling dormancy window, and
credits_lapse_at on /me reports the date they
would lapse if you stopped calling entirely.
Every error has the same shape:
{ "error": { "code": "quota_exceeded", "message": "This month's allowance of 50,000 requests is used up." } }
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_key | No key presented. |
| 401 | invalid_key | Unknown, revoked, expired, or the subscription lapsed. |
| 402 | quota_exceeded | Monthly allowance spent. |
| 404 | not_found | No record with that slug. |
| 422 | — | A parameter failed validation. |
| 429 | rate_limited | Too many requests this minute. |
401 does not distinguish between an unknown key and a revoked
one. Telling an anonymous caller which it was would confirm that a
particular key exists.
Do not refetch the roster nightly. Every list endpoint takes
updated_since, which returns only what has changed —
it costs you far less quota and it is dramatically faster.
curl -H "Authorization: Bearer $UFCGG_KEY" \
"https://ufc.gg/api/v1/fighters?updated_since=2026-08-01T00:00:00Z&order=updated_at"
Store the highest updated_at you have seen and pass it back
on the next run. Records are timestamped when we publish a change, so a
fighter whose record was corrected reappears in the next sync.
Fighter images carry their own license, independent of your plan, and the terms travel with the image in the response.
attribution is present it must be displayed wherever
the image appears. Where share_alike is true,
the image and any version you derive from it carry the same
license — those obligations belong to the photographer, and are not
ours to sell away at any price.
If a placement cannot carry the credit, use a differently licensed image there rather than dropping it.
What you may do with the data depends on your plan. These are rendered from the same records the billing system quotes from, so they are always the current terms. See pricing for monthly and yearly rates.
Fighters, events and full cards. Enough to build a real integration against.
Higher limits and licensed fighter imagery for a live product.
Unmetered access for products that read the whole dataset continuously.
Unmetered access with the right to redistribute, resell and sublicense the data.
The binding terms are set out in the Data License and API Terms of Service. The summaries on this page are for orientation; where they and the agreement differ, the agreement governs.
The version is in the path. Within v1, new fields may be
added to a response — your parser should ignore fields it does not
recognise. Fields will not be removed or change meaning; that would be
v2, announced ahead of time and served alongside
v1 rather than replacing it.
Terms of service · Need redistribution rights, a higher volume, or an SLA? Get in touch — Enterprise terms are agreed per contract.