- Record
- 13-2-0
- Height
- 5'7"
- Reach
- 65"
- Stance
- Orthodox
- Weight
- 125 lb
- Fights
- 9
Documentation
Base URL https://proptechusa-ufc-api.sales-fd3.workers.dev — this site is the documentation and account host and does not serve the API. Data contract 2026-09-14.1, Fight DNA definitions v1. Every response is { ok, data, meta } or { ok:false, data:null, error, meta }. Every example below is a schema example: a real response captured from the canonical API on Sep 14, 2026 and pinned here, so the shape, field names and semantics you read are exactly what you receive. Pinned examples do not move. For values on the published refresh cadence, see the live demos on the home page, which carry their own capture time and freshness state. Each important endpoint shows request → response → what you can build.
2026-09-11.1, on every plan: official weigh-ins with their full source history, and availability, card changes and fighter status. Additive only — no field was removed or renamed.pt_ufc_live_<id>_<secret>) is provisioned automatically and shown once on the dashboard.export UFC_API_KEY=pt_ufc_live_…curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events?status=upcoming&limit=3const res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events?status=upcoming&limit=3", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events?status=upcoming&limit=3",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} Read data; keep meta.request_id for support; watch X-Quota-Remaining. Or skip the terminal: open the Workspace, paste your key and run the current card.
403 plan_required; anything else under /v1 is 404 route_not_found.curl -H "Authorization: Bearer $UFC_API_KEY" https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc
# equivalent
curl -H "X-API-Key: $UFC_API_KEY" https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc X-API-Key. The key id (12 characters after pt_ufc_live_) is safe to log; the secret is not.401 api_key_required, invalid_api_key, api_key_expired, api_key_revoked, api_key_suspended.One key per subscription, tied to a plan and a channel. Manage it on the dashboard: copy, rotate (new secret issued once, old key revoked immediately, subscription stays attached), see usage, denied requests and recent calls, and open the Stripe customer portal. Enterprise keys can carry per-key overrides for quota and rate limit.
| Plan | Price | Requests / month | Requests / minute | At the limit | Unlocks |
|---|---|---|---|---|---|
| Developer | $79/mo | 25,000 | 60 | 429 quota_exceeded | Core UFC data |
| Pro | $199/mo | 100,000 | 180 | 429 quota_exceeded | Fighter intelligence |
| Ultra | $499/mo | 500,000 | 600 | 429 quota_exceeded | Matchup intelligence |
| Scale | $1499/mo | 2,000,000 | 1200 | allowed, X-Quota-Status: over-quota-soft | Production infrastructure |
| Enterprise | Let's talk | custom | custom | custom | Custom |
Headers on every response: X-Request-Id, X-Upstream-Request-Id, X-API-Version, X-Gateway-Version, X-Plan, X-RateLimit-Limit/Remaining/Reset, X-Quota-Limit/Remaining/Reset, Retry-After (429 only). Denied requests never consume quota. Cache-Control: private, max-age=N mirrors canonical freshness: short for upcoming cards, rankings and search; longer for history and as-of DNA.
| Status | Code | Meaning |
|---|---|---|
401 | api_key_required | No credential sent. |
401 | invalid_api_key | Malformed or unknown key, or secret mismatch. |
401 | api_key_expired | Key past expires_at. |
401 | api_key_revoked | Key revoked or rotated. |
401 | api_key_suspended | Subscription not active (unpaid, canceled). Manage it in the dashboard. |
403 | plan_required | Endpoint or parameter needs a higher plan; detail carries required_plan, current_plan, upgrade_url. |
404 | route_not_found | Path is not part of the commercial contract (no upstream call is made). |
429 | rate_limited | Per-minute limit hit; Retry-After set. |
429 | quota_exceeded | Monthly quota hit on a hard-limit plan; Retry-After set. |
502 / 504 | upstream_unavailable / upstream_timeout | Canonical API unreachable or slow. |
Canonical API errors pass through unchanged:
| Status | Codes | Meaning |
|---|---|---|
400 | invalid_include · invalid_ids · too_many_ids · invalid_as_of · invalid_stance · invalid_metric · invalid_bound · invalid_confidence · invalid_type · invalid_fighter_id · invalid_matchup · invalid_status · invalid_since · invalid_state · invalid_status_type | Validation errors from the canonical API; the message lists allowed values. |
404 | event_not_found · fighter_not_found · bout_not_found · article_not_found · division_not_found | Unknown entity. |
404 | dna_not_available | No Fight DNA snapshot for the fighter (or none at/before as_of). detail.reason: no_snapshot | no_snapshot_at_or_before_as_of. |
503 | rankings_not_available · dna_not_available · ledger_not_available · videos_not_available | The store does not exist yet; data is null, nothing is synthesized. |
null, an explicit status, or a 404/503 with a reason.GET /v1/ufc/events?status=upcoming|recent|all&date=YYYY-MM-DD&limit=N, then GET /v1/ufc/events/{id} by UUID, UFCStats id or ESPN event id.
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events?status=upcoming&limit=3const res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events?status=upcoming&limit=3", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events?status=upcoming&limit=3",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} {
"ok": true,
"data": [
{
"id": "64bfa864-454c-4d0a-92e3-408f25598d6c",
"ufcstats_id": null,
"espn_event_id": "600060737",
"name": "Dana White's Contender Series: Season 10, Week 6",
"event_date": "2026-09-15",
"venue": "Meta APEX",
"city": "Las Vegas",
"region": "NV",
"country": "USA",
"commission": null,
"is_ppv": null,
"card_status": "announced",
"updated_at": "2026-09-14T06:02:40.811+00:00"
},
{
"id": "221ca353-f623-4b66-98aa-3a504a418236",
"ufcstats_id": "8a0a35e7c74bebcc",
"espn_event_id": "600060963",
"name": "UFC 331: Van vs. Pantoja 2",
"event_date": "2026-09-19",
"venue": "crypto.com Arena",
"city": "Los Angeles",
"region": "CA",
"country": "USA",
"commission": null,
"is_ppv": null,
"card_status": "announced",
"updated_at": "2026-09-14T06:02:44.256+00:00"
}
],
"meta": {
"api": "PropSports UFC",
"version": "2026-09-14.1",
"request_id": "68886fa7-9808-4b3a-9fb2-128721d9c514",
"count": 3,
"total": 15,
"status": "upcoming",
"date": null,
"access_tier": "public"
}
} | Date | Event | Venue | Status |
|---|---|---|---|
| 2026-09-15 | Dana White's Contender Series: Season 10, Week 6 | Meta APEX, Las Vegas | announced |
| 2026-09-19 | UFC 331: Van vs. Pantoja 2 | crypto.com Arena, Los Angeles | announced |
| 2026-09-22 | Dana White's Contender Series: Season 10, Week 7 | Meta APEX, Las Vegas | announced |
GET /v1/ufc/events/{id}/card?include=media,results,stats returns event plus ordered bouts[] with both fighters, images and results. include=stats (round rows per bout) requires Pro.
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/cardconst res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/card", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/card",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} {
"ok": true,
"data": {
"event": {
"id": "1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52",
"ufcstats_id": "638cfec7ec559d6e",
"espn_event_id": "600060772",
"name": "Noche UFC: Silva vs. Delgado",
"event_date": "2026-09-12",
"venue": "Desert Diamond Arena",
"city": "Glendale",
"region": "AZ",
"country": "USA",
"commission": null,
"is_ppv": null,
"card_status": "complete",
"updated_at": "2026-09-14T06:02:26.496+00:00"
},
"bouts": [
{
"id": "89e38074-f431-4bc3-b51e-fe42058e60d0",
"bout_order": 13,
"weight_class": "FEATHERWEIGHT",
"scheduled_rounds": 5,
"fighter_a": {
"id": "eed368a5-484b-412f-9d2c-a4fd6d24d019",
"name": "Jean Silva",
"…": "record, stance, primary_image"
},
"fighter_b": {
"id": "55ef67ad-88fe-4f7a-b4cb-dc9a10c12feb",
"name": "Jose Miguel Delgado",
"…": "…"
},
"result": {
"round": 3,
"method": "SUB",
"bout_id": "89e38074-f431-4bc3-b51e-fe42058e60d0",
"judge_1": null,
"judge_2": null,
"judge_3": null,
"referee": "Dan Miragliotta",
"time_sec": 177,
"has_stats": false,
"winner_id": "eed368a5-484b-412f-9d2c-a4fd6d24d019",
"method_raw": "Submission",
"scorecards": null,
"captured_at": "2026-09-14T06:02:39.812+00:00",
"time_format": "5 Rnd (5-5-5-5-5)",
"finish_detail": "Rear Naked Choke",
"result_source": "espn"
}
}
],
"…": "12 more bouts"
},
"meta": {
"api": "PropSports UFC",
"version": "2026-09-14.1",
"request_id": "ccbd7c02-eabe-42e1-8c09-0d134cb4c9cc",
"include": [],
"bouts": 13,
"access_tier": "public"
}
}
GET /v1/ufc/fighters?q=&active=true to search, GET /v1/ufc/fighters/{id}?include=media,ranking,next,history,stats,videos for the composite profile (ids: UUID, UFCStats or ESPN athlete id). include=stats requires Pro. History: /fighters/{id}/history?limit=.
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6?include=ranking,next,history&history_limit=5const res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6?include=ranking,next,history&history_limit=5", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6?include=ranking,next,history&history_limit=5",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} {
"ok": true,
"data": {
"id": "2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6",
"ufcstats_id": "34ff304266360297",
"espn_athlete_id": "4608674",
"name": "Manon Fiorot",
"nickname": "The Beast",
"dob": "1990-02-17",
"height_in": 67,
"reach_in": 65,
"weight_lbs": 125,
"stance": "ORTHODOX",
"record_w": 13,
"record_l": 2,
"record_d": 0,
"record_nc": 0,
"is_active": true,
"career_slpm": 5.66,
"career_str_acc": 42,
"career_sapm": 3.58,
"career_str_def": 66,
"career_td_avg": 1.05,
"career_td_acc": 29,
"career_td_def": 87,
"career_sub_avg": 0,
"fight_history_count": 9,
"updated_at": "2026-09-06T16:30:42.017224+00:00",
"slug_id": "4608674",
"primary_image": {
"id": "61827143-8f84-4f0d-8e06-53486dcc22ac",
"image_url": "https://tkmlnhmylqnttmnsnief.supabase.co/storage/v1/object/public/ufc-media/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/portrait.jpg",
"card_url": "https://tkmlnhmylqnttmnsnief.supabase.co/storage/v1/object/public/ufc-media/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/card.jpg",
"thumb_url": "https://tkmlnhmylqnttmnsnief.supabase.co/storage/v1/object/public/ufc-media/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/thumb.jpg",
"author": "Lucie Bertaud",
"license": "CC BY 3.0",
"source_url": "https://commons.wikimedia.org/wiki/File:MANON_FIOROT_ET_ALDRIC_CASSATA,_LE_COUPLE_GOAL_DU_MMA_-_MMA_EXTRA_ROUND_-22_-_YouTube_-_0-28-31_(cropped).jpg",
"kind": "wikimedia",
"attribution_text": "Lucie Bertaud, CC BY 3.0, via Wikimedia Commons",
"rights_label": "CC BY 3.0"
},
"next_bout": null
},
"meta": {
"api": "PropSports UFC",
"version": "2026-09-14.1",
"request_id": "83b742b9-6010-465c-8430-eb957809a502",
"include": [
"ranking",
"next",
"history"
],
"history_count": 5,
"access_tier": "public"
}
}
GET /v1/ufc/bouts/{id} for one bout; GET /v1/ufc/bouts/{id}/stats for every UFC Stats round row (kd, sig_str_*, total_str_*, td_*, sub_att, rev, ctrl_sec, head/body/leg and distance/clinch/ground splits) plus per-fighter totals, fight_time_sec and provenance. Bouts without rows return empty arrays. Career aggregates with computed rates: GET /v1/ufc/fighters/{id}/stats (Pro).
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/ec94d296-2db3-4e0d-be6a-46de4f480672/statsconst res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/ec94d296-2db3-4e0d-be6a-46de4f480672/stats", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/ec94d296-2db3-4e0d-be6a-46de4f480672/stats",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} GET /v1/ufc/results?limit=N: most recent completed bouts with winner, method, round and time. Results are ESPN-sourced facts; a bout without a result has result: null.
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/results?limit=10const res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/results?limit=10", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/results?limit=10",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} GET /v1/ufc/rankings?division=&womens=: the verified ufc.com snapshot with per-division champion and entries, linked to canonical fighter ids. Attribution is required: render source and source_url. 503 rankings_not_available when no store exists.
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/rankings?division=FEATHERWEIGHTconst res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/rankings?division=FEATHERWEIGHT", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/rankings?division=FEATHERWEIGHT",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"}
Movsar Evloev —
Diego Lopes —
Aljamain Sterling —
Yair Rodriguez —
Jean Silva — GET /v1/ufc/events/{id}/weigh-ins?include=history for one card, or GET /v1/ufc/weigh-ins?event_id=&fighter_id=&status=&since=&limit=&offset= across cards. data.results is the current reading per fighter per event after supersession; data.coverage is the event summary plus state.
contracted_limit_lbs, applicable_limit_lbs and over_by_lbs are null whenever the contracted figure was never published, and limit_basis says which case applies: sourced (the source stated it), division_rule (standard division and known title status) or unsupported (catchweight, open weight, unknown division — over_by_lbs is always null). A source that says “missed weight” with no number gives result: "missed" with null weights: the miss is real, the arithmetic is not available.is_confirmation is true and is_correction is false. is_correction is true only when the weight changed. coverage.confirmations and coverage.corrections count them separately.supersedes_id chains a reading to the one it replaced; ?include=history returns every stored row newest first with supersession_kind (confirmation, correction, or null for an original reading) and superseded_weight_lbs.live while any reading is pending, final once none are, and { state: "no_data" } when the card has no readings at all — so “nobody has weighed in yet” stays distinguishable from “not covered”.source_kind is official (the promotion), commission (the athletic commission) or news. Official outranks a report about it. ?since= filters on detected_at — when PropBetEdge saw the reading, not when the publisher dated it — so a back-dated source cannot slip past a poller.400 invalid_status, 400 invalid_since, 404 event_not_found.curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/weigh-ins?include=historyconst res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/weigh-ins?include=history", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/weigh-ins?include=history",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} Official weigh-ins Source
coverage.state · finalThe same fighter, as stored: the earlier reading is kept and the newer one is labelled.
superseded, not deleted is_confirmation: true · is_correction: false Jean Silva: the official source verified the same 145 lb, so this is a confirmation. Nothing was corrected.
Missed weight across cards, newest first:
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/weigh-ins?status=missed&limit=3const res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/weigh-ins?status=missed&limit=3", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/weigh-ins?status=missed&limit=3",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} Production example
See how this is used in a real product: The weigh-in desk on PropBetEdge UFC →
The production application renders these rows directly, including the confirmation trail and a delta it deliberately cannot compute when the limit was never published.
GET /v1/ufc/injuries?active=true&state=&status_type=&fighter_id=&event_id= is one row per individually sourced availability change, newest first by occurred_at. GET /v1/ufc/events/{id}/card-changes is the same events scoped to one card. GET /v1/ufc/fighters/{id}/status is one fighter's current availability plus the sourced history behind it.
injury_type, body_part and injury_side are null unless the cited source names them in words, and any non-null value ships with clinical_quote, the sentence that licensed it. “Out with an injury” is status_type: "injury" with injury_type: null; render that as an unknown cause. Filling the gap yourself makes a medical claim about a named person that no source made.state is active, resolved (a later sourced event ended it — see resolved_by_event_id) or expired (the card it concerned has passed with nothing resolving it: history, not a present-tense claim)./status, current is the most recent active event of a type that means unavailable, or null when nothing is on file. current_note says so in words: the absence of a report is not a medical clearance.changes: []. That is the normal state of a card, not an error.injury, illness, withdrawal, replacement, suspension, visa_travel, weight_miss, return, cleared, other. source_kind: "official" marks the promotion amending its own card, which outranks a report about it.400 invalid_state, 400 invalid_status_type, 404 event_not_found, 404 fighter_not_found.curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/injuries?active=true&limit=5const res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/injuries?active=true&limit=5", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/injuries?active=true&limit=5",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} | Fighter | Type | What the source said | Cause named | Source |
|---|---|---|---|---|
| Valentina Shevchenko | injury | injury that forced Shevchenko to vacate flyweight title | null | MMA Fighting news |
| Christian Echols | weight miss | weighed in above the middleweight limit | null | UFC.com official |
| Carlos Ulberg | injury | recovering from an ACL tear | acl tear | ESPN news |
One fighter, with nothing on file — the truthful shape of “no news”:
{
"ok": true,
"data": {
"fighter": {
"id": "eed368a5-484b-412f-9d2c-a4fd6d24d019",
"name": "Jean Silva"
},
"current": null,
"current_note": "No availability event on file. This is the absence of a report, not a confirmation of fitness.",
"history": []
},
"meta": {
"api": "PropSports UFC",
"version": "2026-09-14.1",
"request_id": "14d45b43-bd5c-447e-a093-2cb79b7e5b78",
"count": 0,
"access_tier": "public"
}
} One card's changes (empty on a card where nothing has changed):
{
"ok": true,
"data": {
"event": {
"id": "1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52",
"name": "Noche UFC: Silva vs. Delgado",
"event_date": "2026-09-12"
},
"changes": []
},
"meta": {
"api": "PropSports UFC",
"version": "2026-09-14.1",
"request_id": "f1f376b9-ae7e-4885-a086-a84bd18d9fd1",
"count": 0,
"access_tier": "public"
}
} Production example
See how this is used in a real product: Injuries and availability on PropBetEdge UFC →
The production application lists the same sourced events, and shows an unnamed cause as unknown rather than substituting one.
Fighter rows carry primary_image (image_url, card_url 800×1000, thumb_url 320×400) with author, license, source_url, kind, attribution_text and rights_label; fighter detail adds images[]; GET /v1/ufc/fighters/media?ids=… maps up to 150 ids. Render the credit wherever the image is shown; the file's license governs reuse. Official video metadata (/videos, /fighters/{id}/videos, /events/{id}/videos) returns ids, official URLs, embed_url and attribution; play through the provider, never rehost.
{
"id": "61827143-8f84-4f0d-8e06-53486dcc22ac",
"image_url": "https://tkmlnhmylqnttmnsnief.supabase.co/storage/v1/object/public/ufc-media/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/portrait.jpg",
"card_url": "https://tkmlnhmylqnttmnsnief.supabase.co/storage/v1/object/public/ufc-media/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/card.jpg",
"thumb_url": "https://tkmlnhmylqnttmnsnief.supabase.co/storage/v1/object/public/ufc-media/fighters/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/thumb.jpg",
"author": "Lucie Bertaud",
"license": "CC BY 3.0",
"source_url": "https://commons.wikimedia.org/wiki/File:MANON_FIOROT_ET_ALDRIC_CASSATA,_LE_COUPLE_GOAL_DU_MMA_-_MMA_EXTRA_ROUND_-22_-_YouTube_-_0-28-31_(cropped).jpg",
"kind": "wikimedia",
"attribution_text": "Lucie Bertaud, CC BY 3.0, via Wikimedia Commons",
"rights_label": "CC BY 3.0"
} GET /v1/ufc/videos returns published official video metadata newest first, filterable by event_id, fighter_id, bout_id, type and limit. Per-entity feeds are /v1/ufc/events/{id}/videos, /v1/ufc/fighters/{id}/videos and /v1/ufc/bouts/{id}/videos.
embed_url on the provider's privacy-enhanced domain, thumbnail, title, channel, published_at, duration_sec, video_type and links to event, bout and fighters. The footage is not hosted, rehosted or redistributed, and API access conveys no rights to it.link_status = published rows are ever returned; rows in review or rejected never leave the database, so anything you receive is cleared to link.embeddable before building a player. When it is false, link out to url instead — never render an iframe you were not told would load.503 videos_not_available while the video store is not present.curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/videos?event_id=845c3b2c-c201-4873-91eb-f5115d65b549&limit=20const res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/videos?event_id=845c3b2c-c201-4873-91eb-f5115d65b549&limit=20", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/videos?event_id=845c3b2c-c201-4873-91eb-f5115d65b549&limit=20",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} Selected for UFC 332: Silva vs. Wang · fight-week phase early · 60 published clips, 48 on this card · 27 EN · 8 ES · 25 PT · 60 embeddable
Production example
See how this is used in a real product: Fight Week on PropBetEdge UFC →
The production application renders the same feed as its fight-week video desk, with the same selection policy.
GET /v1/ufc/fighters/{id}/dna[?as_of=YYYY-MM-DD]: the fighter's snapshot with metrics (striking, pace, grappling, finish), stance_splits, round_profile, finish_profile, context_splits, position_profile and provenance. Family routes: /splits?opponent_stance=, /round-profile, /finish-profile, /position-profile. Definitions for every metric: GET /v1/ufc/dna/metrics (all plans).
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/ec94d296-2db3-4e0d-be6a-46de4f480672/dnaconst res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/ec94d296-2db3-4e0d-be6a-46de4f480672/dna", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/fighters/ec94d296-2db3-4e0d-be6a-46de4f480672/dna",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} {
"ok": true,
"data": {
"fighter": {
"id": "ec94d296-2db3-4e0d-be6a-46de4f480672",
"name": "Sean Strickland"
},
"snapshot": {
"as_of_date": "2026-09-14",
"definition_version": 1,
"sample_bouts": 25,
"sample_rounds": 84,
"sample_seconds": 24399,
"coverage_status": "high",
"metrics": {
"sig_landed_per_min": {
"unit": "per_min",
"value": 5.9757,
"origin": "pbe_derived",
"numerator": 2430,
"as_of_date": "2026-09-14",
"confidence": "high",
"metric_key": "sig_landed_per_min",
"denominator": 24399,
"sample_bouts": 25,
"sample_rounds": 84,
"sample_seconds": 24399,
"coverage_status": "high",
"source_families": [
"ufcstats"
],
"definition_version": 1
},
"finish_rate": {
"unit": "ratio",
"value": 0.3333,
"origin": "pbe_derived",
"numerator": 6,
"as_of_date": "2026-09-14",
"confidence": "high",
"metric_key": "finish_rate",
"denominator": 18,
"sample_bouts": 18,
"sample_rounds": 84,
"sample_seconds": 24399,
"coverage_status": "high",
"source_families": [
"espn",
"ufcstats"
],
"definition_version": 1
},
"…": "more metric keys"
},
"…": "stance_splits, round_profile, finish_profile, context_splits, provenance"
}
},
"meta": {
"api": "PropSports UFC",
"version": "2026-09-14.1",
"request_id": "375d782a-eb67-419d-9077-ee8b93e4ea14",
"requested_as_of": null,
"resolved_as_of": "2026-09-14",
"definition_version": 1,
"as_of_semantics": "as_of_date is exclusive: the snapshot dated D contains bouts with event_date < D, so a bout fought on D is excluded by the snapshot dated D. ?as_of=D resolves to the latest stored snapshot with as_of_date <= D, which therefore never contains a bout fought on or after D.",
"origin": "pbe_derived",
"origin_label": "PBE DERIVED",
"access_tier": "public"
}
}
Registry families: stance, striking, pace, grappling, finish, context. See the methodology for every definition.
GET /v1/ufc/matchups/{a}/{b}/dna[?as_of=] compares 18 paired metrics (delta, direction, comparable), adds stance_context, and emits insights[] only above the thresholds in meta.insight_rules; the rest is named in warnings[]. Also Ultra: /dna/query and the Fight State Ledger and event intelligence.
curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/matchups/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/1d296292-4969-43a8-9948-cd0a76a642ce/dnaconst res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/matchups/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/1d296292-4969-43a8-9948-cd0a76a642ce/dna", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/matchups/2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6/1d296292-4969-43a8-9948-cd0a76a642ce/dna",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} {
"ok": true,
"data": {
"fighters": [
{
"id": "2c9c2a88-aec1-4bf0-9b16-3ce58adf6fd6",
"name": "Manon Fiorot",
"stance": "ORTHODOX"
},
{
"id": "1d296292-4969-43a8-9948-cd0a76a642ce",
"name": "Alexa Grasso",
"stance": "ORTHODOX"
}
],
"comparisons": [
{
"key": "sig_landed_per_min",
"label": "Sig. strikes landed / min",
"unit": "per_min",
"family": "striking",
"higher_is_better": true,
"mismatch": null,
"min_abs_delta": null,
"a": {
"unit": "per_min",
"value": 5.5661,
"origin": "pbe_derived",
"numerator": 798,
"as_of_date": "2026-09-14",
"confidence": "high",
"metric_key": "sig_landed_per_min",
"denominator": 8602,
"sample_bouts": 10,
"sample_rounds": 30,
"sample_seconds": 8602,
"coverage_status": "high",
"source_families": [
"ufcstats"
],
"definition_version": 1
},
"b": {
"unit": "per_min",
"value": 4.1892,
"origin": "pbe_derived",
"numerator": 1001,
"as_of_date": "2026-09-14",
"confidence": "high",
"metric_key": "sig_landed_per_min",
"denominator": 14337,
"sample_bouts": 16,
"sample_rounds": 49,
"sample_seconds": 14337,
"coverage_status": "high",
"source_families": [
"ufcstats"
],
"definition_version": 1
},
"delta": 1.3769,
"direction": "a",
"comparable": true,
"min_confidence": "high"
},
{
"key": "sig_absorbed_per_min",
"label": "Sig. strikes absorbed / min",
"unit": "per_min",
"family": "striking",
"higher_is_better": false,
"mismatch": null,
"min_abs_delta": null,
"a": {
"unit": "per_min",
"value": 3.7596,
"origin": "pbe_derived",
"numerator": 539,
"as_of_date": "2026-09-14",
"confidence": "high",
"metric_key": "sig_absorbed_per_min",
"denominator": 8602,
"sample_bouts": 10,
"sample_rounds": 30,
"sample_seconds": 8602,
"coverage_status": "high",
"source_families": [
"ufcstats"
],
"definition_version": 1
},
"b": {
"unit": "per_min",
"value": 3.779,
"origin": "pbe_derived",
"numerator": 903,
"as_of_date": "2026-09-14",
"confidence": "high",
"metric_key": "sig_absorbed_per_min",
"denominator": 14337,
"sample_bouts": 16,
"sample_rounds": 49,
"sample_seconds": 14337,
"coverage_status": "high",
"source_families": [
"ufcstats"
],
"definition_version": 1
},
"delta": -0.0194,
"direction": "b",
"comparable": true,
"min_confidence": "high"
}
],
"insights": [
{
"key": "a_vs_orthodox_record",
"label": "Manon Fiorot vs orthodox opponents",
"value": 0.8889,
"unit": "ratio",
"record": {
"w": 8,
"l": 1,
"d": 0,
"nc": 0,
"appearances": 9
},
"sample_bouts": 9,
"sample_rounds": 25,
"sample_seconds": 7102,
"confidence": "high",
"direction": "contextual",
"side": "a",
"metric_keys": [
"stance_record"
],
"explanation": "Manon Fiorot is 8-1-0 in 9 UFC bouts against orthodox opponents (3 KO/TKO, 0 submission, 5 decision wins), PBE-derived from canonical results as of 2026-09-14.",
"origin": "pbe_derived"
}
],
"warnings": [],
"…": "stance_context, bettors_edge_evidence, note"
},
"meta": {
"resolved_as_of": {
"a": "2026-09-14",
"b": "2026-09-14"
},
"insights": 11,
"warnings": 0
}
}
| 65" | Reach | 66" |
|---|---|---|
| Orthodox | Stance | Orthodox |
| 5.57 | Pace (sig. landed/min)high / high | 4.19 |
| 41% | Strike accuracyhigh / high | 41% |
| 92% | Distance sharehigh / high | 90% |
| 3.24 | Takedown attempts /15high / high | 1.07 |
| 29% | Takedown accuracyhigh / high | 35% |
| 0.93× | Pace retention R3/R1high / high | 0.95× |
| 38% | Finish rate (wins)medium / high | 30% |
GET /v1/ufc/bouts/{id}/ledger?limit= returns append-only snapshots of one bout's state with deterministic diffs between consecutive checkpoints. GET /v1/ufc/events/{id}/intelligence is the same view across a whole card, one entry per bout.
meta.append_only). Checkpoints are t_minus_72h, t_minus_24h, post_weigh_in and ad_hoc; meta.checkpoints counts each kind.meta.diff_rules states exactly which paths are compared between consecutive snapshots; each entry in diffs[] carries changed[] (paths) and changes[] (path, from, to) with hours_between. The API reports that a value moved; it never tells you what the move means.status: "unavailable" and appears in meta.unavailable_sources rather than being silently absent.card_shock returns { status: "not_computed", reason: … } until that engine exists.ledger_status, snapshots, latest, hours_to_start, dna readiness per fighter, wire_count, result.status and the newest diff.curl \
-H "Authorization: Bearer $UFC_API_KEY" \
https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/intelligenceconst res = await fetch("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/intelligence", {
headers: { Authorization: `Bearer ${UFC_API_KEY}` },
});
const { ok, data, meta } = await res.json();res = requests.get("https://proptechusa-ufc-api.sales-fd3.workers.dev/v1/ufc/events/1d0b22df-81e7-4e5b-8fa5-8e5d03c24c52/intelligence",
headers={"Authorization": f"Bearer {UFC_API_KEY}"})
body = res.json() # {"ok", "data", "meta"} These two routes are the derived fight-week layer and stay on Ultra and above. The source-backed fight-week facts — weigh-ins, availability, card changes and fighter status — are on every plan.
as_of_date is exclusive: the snapshot dated D contains bouts with event_date < D, so a bout fought on D is excluded by the snapshot dated D. ?as_of=D resolves to the latest stored snapshot with as_of_date <= D, which therefore never contains a bout fought on or after D.
Add ?as_of=YYYY-MM-DD to fighter DNA routes (Pro) or Matchup DNA (Ultra). meta.requested_as_of / meta.resolved_as_of report the snapshot used. A date before the first snapshot is a truthful 404:
{
"ok": false,
"data": null,
"error": {
"code": "dna_not_available",
"message": "No Fight DNA snapshot exists for Sean Strickland at or before 2014-01-01. Nothing is synthesized.",
"detail": {
"fighter_id": "ec94d296-2db3-4e0d-be6a-46de4f480672",
"requested_as_of": "2014-01-01",
"definition_version": 1,
"reason": "no_snapshot_at_or_before_as_of"
}
},
"meta": {
"api": "PropSports UFC",
"version": "2026-09-14.1",
"request_id": "976471ca-2371-4d64-be70-702061b594ca"
}
} | Tier | Rule |
|---|---|
insufficient | sample below the metric's registry minimum (min_bouts / min_rounds / min_seconds); value is null when there is no denominator |
low | minimum met but sample_bouts < 3 (result metrics) or sample_seconds < 1800 (rate metrics) |
medium | sample_bouts < 8 (result metrics) or sample_seconds < 5400 (rate metrics) |
high | sample_bouts >= 8 (result metrics) or sample_seconds >= 5400 (rate metrics) |
coverage_status uses the same scale for stat coverage of the sample. Present insufficient and low as exactly that.
Every derived value is a MetricObject; the gateway never strips it.
{
"unit": "per_min",
"value": 5.9757,
"origin": "pbe_derived",
"numerator": 2430,
"as_of_date": "2026-09-14",
"confidence": "high",
"metric_key": "sig_landed_per_min",
"denominator": 24399,
"sample_bouts": 25,
"sample_rounds": 84,
"sample_seconds": 24399,
"coverage_status": "high",
"source_families": [
"ufcstats"
],
"definition_version": 1
} GET /v1/ufc/fighters?q=fiorot → take id (or accept a UFCStats / ESPN id directly).GET /v1/ufc/fighters/{id}?include=ranking,next,history&history_limit=10 → name, record, height/reach/stance, primary_image (+ credit), ranking positions, next bout, recent history.GET /v1/ufc/fighters/{id}/stats → computed.career_rates with provenance; label as display aggregates.GET /v1/ufc/fighters/{id}/dna → render metrics with their confidence; show the sample line; hide nothing.Result: the fighter profile demo on the homepage, rendered from exactly these responses. Try it live in the Workspace.
Production example
See how this is used in a real product: A fighter profile on PropBetEdge UFC →
Fighter pages on the production application are built from the same fighter, stats and Fight DNA responses documented above.
GET /v1/ufc/events?status=upcoming&limit=1 → the next event id.GET /v1/ufc/events/{id}/card?include=media,results → bouts[] ordered main event first (bout_order), with card_position (main, prelim, early_prelim), weight class, scheduled rounds, title flag, both fighters with images, and result when fought.card_position; render the highest bout_order as the main event; credit every portrait.GET /v1/ufc/events/{id}/videos for the official fight-week video timeline (link out, never rehost).See Fight Week on the homepage or run Current card in the Workspace.
Production example
See how this is used in a real product: Fight Week on PropBetEdge UFC →
The production Fight Week view is composed from the event card response, per-bout fighters and their media.
GET /v1/ufc/matchups/{a}/{b}/dna (Ultra).fighters[] (reach, stance, record, rank); paired rows from comparisons[] (comparable=false → show “—”, never a placeholder number); stance_context for the stance story.insights[] as the read, each with its confidence and sample; surface warnings[] so users see what was not claimed.Live: Workspace matchup builder.
Production example
See how this is used in a real product: A matchup page on PropBetEdge UFC →
Matchup pages render comparisons[], insights[] and warnings[] exactly as returned, with non-comparable rows shown as em-dashes.
GET /v1/ufc/rankings (all divisions, official order) or ?division=FEATHERWEIGHT.champion; list from entries[] with rank, change, is_new; ties are real, ranks may skip.fighter.id values and call GET /v1/ufc/fighters/media?ids=a,b,c (≤150) once; render credits.source / source_url (ufc.com) and snapshot_date.Production example
See how this is used in a real product: Rankings on PropBetEdge UFC →
The production rankings screen renders the same official snapshot, with portraits from one bulk media call.
/fighters/{id} per bout (or reuse card payload)./events/{id}/weigh-ins?include=history → coverage for the desk header, results for the rows, history for the trail. Show a null limit as unpublished; show is_confirmation as a confirmation, not a correction./events/{id}/card-changes for this card, /fighters/{id}/status per fighter. Render injury_type: null as an unknown cause and current: null as “no report on file”, never as “fit”./fighters/{id}/dna (Pro) for each side; /matchups/{a}/{b}/dna (Ultra) for the read; /events/{id}/intelligence (Ultra) for the ledger view across the card./rankings for implications, /events/{id}/videos for official media, /news for PropBetEdge editorial./weigh-ins?since= on detected_at rather than diffing whole cards.Production example
See how this is used in a real product: Fight Week on PropBetEdge UFC →
PropBetEdge UFC's Fight Week is one production implementation of this exact flow: card, faces, per-side Fight DNA, the matchup read and ranking implications.
| Endpoint | Summary | Origin | Minimum plan |
|---|---|---|---|
/v1/ufc | API index | source fact | Developer |
/v1/ufc/events | List events (upcoming, recent, all, by date) | source fact | Developer |
/v1/ufc/events/{id} | Event by UUID, UFCStats id or ESPN id | source fact | Developer |
/v1/ufc/events/{id}/card | Event plus its ordered fight card | source fact | Developer |
/v1/ufc/events/{id}/articles | PropBetEdge articles for an event | editorial | Developer |
/v1/ufc/events/{id}/videos | Official video metadata for an event, grouped for the fight-week timeline | third party link | Developer |
/v1/ufc/events/{id}/intelligence | Fight Week intelligence per bout from the Fight State Ledger | pbe derived | Ultra |
/v1/ufc/fighters | Search and list fighters | source fact | Developer |
/v1/ufc/fighters/media | Bulk primary_image map (max 150 ids) | media | Developer |
/v1/ufc/fighters/{id} | Fighter profile composite (include=media,ranking,next,history,stats,videos) | source fact | Developer |
/v1/ufc/fighters/{id}/history | Fighter bout history | source fact | Developer |
/v1/ufc/fighters/{id}/stats | Career round rows plus computed rates and per-bout aggregates | pbe derived | Pro |
/v1/ufc/fighters/{id}/articles | PropBetEdge articles for a fighter | editorial | Developer |
/v1/ufc/fighters/{id}/videos | Official video metadata for a fighter | third party link | Developer |
/v1/ufc/fighters/{id}/dna | Fighter Fight DNA snapshot (metrics, stance splits, round, finish, context) | pbe derived | Pro |
/v1/ufc/fighters/{id}/splits | Stance DNA split rows | pbe derived | Pro |
/v1/ufc/fighters/{id}/round-profile | Round DNA profile | pbe derived | Pro |
/v1/ufc/fighters/{id}/finish-profile | Finish DNA profile | pbe derived | Pro |
/v1/ufc/fighters/{id}/position-profile | Position profile (licensed family; explicit unavailable until a licensed source exists) | licensed | Pro |
/v1/ufc/bouts/{id} | Bout detail | source fact | Developer |
/v1/ufc/bouts/{id}/stats | Round-level statistics for a bout | source fact | Developer |
/v1/ufc/bouts/{id}/videos | Official video metadata for a bout | third party link | Developer |
/v1/ufc/bouts/{id}/ledger | Fight State Ledger snapshots and deterministic diffs | pbe derived | Ultra |
/v1/ufc/matchups/{fighterA}/{fighterB}/dna | Matchup DNA | pbe derived | Ultra |
/v1/ufc/results | Recent results | source fact | Developer |
/v1/ufc/rankings | Official rankings snapshot (ufc.com) | source fact | Developer |
/v1/ufc/weigh-ins | Official weigh-in readings: current reading per fighter per event, with confirmation vs correction and explicit null limits | source fact | Developer |
/v1/ufc/events/{id}/weigh-ins | One event's weigh-ins with coverage state; include=history returns every stored source reading | source fact | Developer |
/v1/ufc/injuries | Individually sourced availability events: injuries, withdrawals, replacements, suspensions, weight misses, returns (never an inferred diagnosis) | source fact | Developer |
/v1/ufc/events/{id}/card-changes | Sourced withdrawals, replacements and other changes to one event's card | source fact | Developer |
/v1/ufc/fighters/{id}/status | One fighter's current availability event (or null: no report on file, not a fitness claim) and its sourced history | source fact | Developer |
/v1/ufc/live-bouts | Live and recent bout status for the current card: phase, active bout, on deck, every bout with status, source round and official result, with freshness (ufc-live-bout/1) | source fact | Developer |
/v1/ufc/news | PropBetEdge newsroom list | editorial | Developer |
/v1/ufc/articles/{slug} | PropBetEdge article | editorial | Developer |
/v1/ufc/wire | Third-party headline wire (Enterprise / first-party only pending rights review) | third party link | Enterprise |
/v1/ufc/search | Search fighters, events and articles | source fact | Developer |
/v1/ufc/counts | Coverage counts | source fact | Developer |
/v1/ufc/videos | Official video metadata feed | third party link | Developer |
/v1/ufc/dna/metrics | Fight DNA metric registry | pbe derived | Developer |
/v1/ufc/dna/query | Cross-fighter Fight DNA query | pbe derived | Ultra |
| Label | Where | Rule |
|---|---|---|
| Source | events, fighters, bouts, results, round stats, rankings, weigh-ins, availability, card changes, fighter status | Normalized ESPN / UFC Stats / ufc.com facts, official weigh-in readings and individually sourced availability events. Weigh-in and availability rows carry source_url, source_name and source_kind; attribute the source where displayed. |
| PBE derived | Fight DNA, Matchup DNA, computed career rates, Fight State Ledger diffs, event intelligence | PropBetEdge analytics. Show sample and confidence. Never present as official statistics or predictions. |
| Licensed | position profile (reserved) | Licensed enrichment, labelled separately when it exists. |
| Editorial | news, articles | PropBetEdge newsroom content. |
| Media | primary_image, images[], hero_image | Render attribution_text; each file's license governs reuse. |
| Third-party link | videos, wire | Ids and official URLs only. Provider player; never rehost. |
The commercial contract is generated from the canonical API's OpenAPI plus the entitlement config, so data schemas (MetricObject, StanceSplit, DnaSnapshot, MatchupDna, Video, PrimaryImage, envelopes) are byte-identical to upstream and every operation carries x-plan and x-origin. Servers: https://proptechusa-ufc-api.sales-fd3.workers.dev (commercial gateway) and https://ufc-api.propbetedge.ai (canonical, first-party). This documentation site is a separate static host and serves no API routes. Import openapi.json into your client generator, or browse the interactive reference.