The calibrated probability API, documented honestly.
A versioned, read-only REST API that returns the model's own calibrated
win probabilities { H, D, A } with a transparent rationale
(Elo, form, H2H, expected goals) across football, tennis, basketball, baseball, hockey and more —
plus Elo ratings, a walk-forward backtest, and a live accuracy record.
/healthAuthentication
Pass your per-customer key in the X-API-Key request header. It is
required on every endpoint except /v1/health,
which is public. Keys are issued from the
dashboard; treat them like a password and never ship
them in client-side code. All traffic must be HTTPS — plain HTTP is refused.
mp_live_8Qd2f6Rb1Tn0Yk7Lx3Wp9Vc4Hs2Mg5
# Every call carries the header; /health is the one exception.
curl https://api.matchprior.com/v1/predictions?sport=football&league=E0 \
-H "X-API-Key: mp_live_8Qd2f6Rb1Tn0Yk7Lx3Wp9Vc4Hs2Mg5"
Cursor pagination
List endpoints (/predictions, /fixtures,
/ratings) return a page envelope: a data
array plus a page object. Pagination is forward-only
with an opaque cursor — never compute or parse it yourself.
| Field | Type | Meaning |
|---|---|---|
| page.count | integer | Number of items in this page. |
| page.nextCursor | string | null | Pass it back as the since query param to fetch the next page. null means you have reached the end. |
| limit | integer | Page size, 1–200, default 50. |
| since | string | Opaque forward cursor from a prior page.nextCursor. Omit for the first page. |
# First page
curl ".../v1/fixtures?sport=football&limit=50" \
-H "X-API-Key: mp_live_…"
# → page.nextCursor: "ZjI6MTc4NjAwMDAwMDAwMA"
# Next page: feed nextCursor back as `since`
curl ".../v1/fixtures?sport=football&limit=50\
&since=ZjI6MTc4NjAwMDAwMDAwMA" \
-H "X-API-Key: mp_live_…"
Errors & status codes
Errors use standard HTTP status codes and a single-shape JSON body:
{ "error": "<human-readable message>" }. Always branch on the
status code, not the message text.
| Status | Meaning | Example error |
|---|---|---|
| 200 | OK — request succeeded. | — |
| 304 | Not Modified — your If-None-Match ETag still matches; body omitted. | — |
| 400 | Bad request — malformed query param (e.g. limit out of range). | "invalid limit (1–200)" |
| 401 | Unauthorized — missing or invalid X-API-Key. | "missing or invalid X-API-Key" |
| 404 | Not found — no such fixture / resource. | "not found" |
| 429 | Rate limited — you exceeded your plan's quota. | "rate limit exceeded" |
| 503 | Stale — last data refresh failed or data is stale (/health only). | "stale: last refresh failed" |
{ "error": "missing or invalid X-API-Key" }
Endpoint index
Seven read-only endpoints. Lead with /accuracy
and /backtest — the published
Brier-vs-market record is the point.
| Endpoint | Auth | Returns |
|---|---|---|
| GET /v1/health | public | Service + data freshness. |
| GET /v1/predictions | key | Paged predictions for upcoming fixtures. |
| GET /v1/predictions/{id} | key | One prediction with full rationale. |
| GET /v1/fixtures | key | Paged upcoming fixtures. |
| GET /v1/ratings | key | Elo leaderboard per league. |
| GET /v1/accuracy | key | Live graded accuracy + calibration. |
| GET /v1/backtest | key | Walk-forward backtest, keyed by sport. |
Service liveness and data freshness. Public so you can wire it straight into an uptime monitor. Returns 200 when fresh and 503 when the last refresh failed or the snapshot is stale.
{
"ok": true,
"version": "1.0.0",
"lastRefreshAt": "2026-06-27T06:00:11Z",
"lastRefreshOk": true,
"lastRefreshError": null,
"generatedAt": "2026-06-27T06:00:13Z",
"ageMs": 5384000,
"stale": false,
"refreshMs": 21600000
}
| Field | Type | Description |
|---|---|---|
| ok | boolean | Service is up. |
| version | string | API build version. |
| lastRefreshAt | string | null | UTC timestamp of the last data refresh. |
| lastRefreshOk | boolean | Whether that refresh succeeded. |
| lastRefreshError | string | null | Error from the last failed refresh, else null. |
| ageMs | integer | null | Age of the current snapshot in ms. |
| stale | boolean | true when the snapshot is past its freshness window. |
| refreshMs | integer | Refresh interval (21600000 ms = 6h). |
X-API-Key
predictions
A page of the model's current predictions for upcoming fixtures — calibrated probabilities, the pick, confidence, market comparison and full rationale.
| Param | Type | Description | |
|---|---|---|---|
| sport | string | optional | Sport key — football, tennis, basketball, baseball, hockey, americanfootball, afl, rugbyleague, rugbyunion, netball, cricket. |
| league | string | optional | League code — e.g. E0, SP1, ATP, NBA, MLB, CRIC. |
| date | string<date> | optional | Single match day, yyyy-mm-dd. |
| limit | integer | optional | Page size 1–200. Default 50. |
| since | string | optional | Forward cursor — see pagination. |
curl "https://api.matchprior.com/v1/predictions\
?sport=football&league=E0&date=2026-08-15&limit=1" \
-H "X-API-Key: mp_live_…"
{
"data": [
{
"fixture": {
"id": "E0|2026-08-15|Arsenal|Chelsea",
"sport": "football",
"league": "E0",
"date": "2026-08-15",
"time": "17:30",
"home": "Arsenal",
"away": "Chelsea",
"startMs": 1786000000000,
"market": { "H": 0.52, "D": 0.26, "A": 0.22 }
},
"probs": { "H": 0.55, "D": 0.25, "A": 0.20 },
"pick": "H",
"confidence": 0.55,
"over25": 0.54,
"btts": 0.58,
"likelyScore": { "home": 2, "away": 1 },
"marketEdge": 0.03,
"contrarian": false,
"rationale": {
"homeRating": 1788,
"awayRating": 1742,
"ratingEdge": 111,
"homeForm": [ "W", "W", "D", "L", "W" ],
"awayForm": [ "D", "W", "W", "L", "D" ],
"homeGd": 0.8,
"awayGd": 0.4,
"h2h": { "played": 6, "homeWins": 3, "draws": 2, "awayWins": 1 },
"expectedGoals": { "home": 1.7, "away": 1.1 }
}
}
],
"page": { "count": 1, "nextCursor": null }
}
model prob − market prob on the pick — where the model's view and
the market's diverge. A bigger gap is not a better opportunity and it is not expected
value. Never read it as expected profit.
X-API-Key
predictions
The single prediction for one fixture, with the same shape as an item in the list. Returns 404 if the fixture id is unknown or already in the past.
| Param | Type | Description | |
|---|---|---|---|
| fixtureId | string | required | Composite id of the form {league}|{date}|{home}|{away}, e.g. E0|2026-08-15|Arsenal|Chelsea. URL-encode the pipes (%7C) in path position. |
curl "https://api.matchprior.com/v1/predictions/\
E0%7C2026-08-15%7CArsenal%7CChelsea" \
-H "X-API-Key: mp_live_…"
{
"fixture": { "id": "E0|2026-08-15|Arsenal|Chelsea", "sport": "football", /* … */ },
"probs": { "H": 0.55, "D": 0.25, "A": 0.20 },
"pick": "H",
"confidence": 0.55,
"marketEdge": 0.03,
"contrarian": false,
"rationale": { "ratingEdge": 111, "expectedGoals": { "home": 1.7, "away": 1.1 } }
}
| Field | Type | Description |
|---|---|---|
| probs | { H, D, A } | Calibrated outcome probabilities (sum ≈ 1). |
| pick | "H" | "D" | "A" | The model's highest-probability outcome. |
| confidence | number | Probability assigned to the pick (0–1). |
| over25 / btts | number | P(total goals ≥ 3) and P(both teams score) — football. |
| marketEdge | number | Model prob − market prob on the pick. A disagreement measure, not expected return. |
| contrarian | boolean | The model picks against the market favourite. |
| spread / totalPoints | number | Basketball etc. — expected home margin and total. |
| rationale | object | The transparent inputs: Elo ratings, ratingEdge, form, goal difference, H2H, expected goals. |
X-API-Key
fixtures
A page of upcoming fixtures the model can predict — the bare schedule,
without the probability layer. Tennis fixtures additionally carry
surface, tournament and
round.
| Param | Type | Description | |
|---|---|---|---|
| sport | string | optional | Sport key (see predictions). |
| league | string | optional | League code. |
| date | string<date> | optional | Single day, yyyy-mm-dd. |
| limit | integer | optional | Page size 1–200. Default 50. |
| since | string | optional | Forward cursor. |
curl "https://api.matchprior.com/v1/fixtures\
?sport=tennis&league=ATP&limit=2" \
-H "X-API-Key: mp_live_…"
{
"data": [
{
"id": "ATP|2026-06-29|Alcaraz|Sinner",
"sport": "tennis",
"league": "ATP",
"date": "2026-06-29",
"time": "14:00",
"home": "Alcaraz",
"away": "Sinner",
"startMs": 1782655200000,
"surface": "Grass",
"tournament": "Wimbledon",
"round": "F",
"neutral": true
}
],
"page": { "count": 1, "nextCursor": "dDoxNzgyNjU1MjAwMDAw" }
}
| Field | Type | Description |
|---|---|---|
| id | string | {league}|{date}|{home}|{away}. |
| startMs | integer | UTC kick-off epoch in ms. |
| market | { H, D, A } | — | De-vigged market probabilities, where an odds feed exists (football, tennis). |
| surface | string | Tennis court surface. |
| neutral | boolean | true = neutral venue (no home-field adjustment). |
X-API-Key
ratings
The Elo leaderboard for a league, highest rating first — the same
ratings that feed every prediction's rationale.
| Param | Type | Description | |
|---|---|---|---|
| sport | string | optional | Sport key. |
| league | string | optional | League code, e.g. E0. |
| limit | integer | optional | Page size 1–200. Default 50. |
curl "https://api.matchprior.com/v1/ratings\
?sport=football&league=E0&limit=3" \
-H "X-API-Key: mp_live_…"
{
"data": [
{
"team": "Manchester City",
"league": "E0",
"rating": 1841,
"games": 38,
"form": [ "W", "W", "W", "D", "W" ],
"goalsFor": 89,
"goalsAgainst": 33,
"lastPlayed": "2026-05-24"
},
{ "team": "Arsenal", "rating": 1788, /* … */ },
{ "team": "Liverpool", "rating": 1776, /* … */ }
],
"page": { "count": 3, "nextCursor": null }
}
| Field | Type | Description |
|---|---|---|
| team | string | Team / player name. |
| rating | number | Current Elo rating. |
| games | integer | Games included in the rating. |
| form | ("W"|"D"|"L")[] | Most-recent-first results, from the team's perspective. |
| goalsFor / goalsAgainst | number | Cumulative goals scored / conceded. |
| lastPlayed | string | null | Date of the most recent counted game. |
X-API-Key
accuracy
The live, graded accuracy and calibration record — the honest core of the API. It reports the model's hit rate and Brier score alongside the market's Brier over the same graded set, so the comparison is in the payload, not the marketing.
{
"graded": 1284,
"correct": 758,
"hitRate": 0.590,
"brier": 0.491,
"marketBrier": 0.453,
"byLeague": {
"E0": { "graded": 312, "correct": 181 },
"ATP": { "graded": 204, "correct": 132 }
},
"recent": [
{
"id": "E0|2026-05-24|Arsenal|Everton",
"league": "E0", "date": "2026-05-24",
"home": "Arsenal", "away": "Everton",
"pick": "H", "actual": "H", "hit": true,
"probs": { "H": 0.71, "D": 0.18, "A": 0.11 }
},
{
"id": "E0|2026-05-24|Brentford|Newcastle",
"league": "E0", "date": "2026-05-24",
"home": "Brentford", "away": "Newcastle",
"pick": "H", "actual": "A", "hit": false,
"probs": { "H": 0.44, "D": 0.29, "A": 0.27 }
}
]
}
| Field | Type | Description |
|---|---|---|
| graded | integer | Settled fixtures the model was scored on. |
| correct | integer | Settled fixtures the pick got right. |
| hitRate | number | correct / graded. |
| brier | number | Mean Brier score of the model's probabilities (lower = better). |
| marketBrier | number | null | The market's Brier over the same graded set, for an apples-to-apples comparison. |
| byLeague | object | Per-league { graded, correct }. |
| recent | object[] | Most recent graded fixtures with pick, actual outcome and probabilities — wins and losses alike. |
X-API-Key
backtest
Out-of-sample, walk-forward backtest reports over ingested history,
returned as an object keyed by sport. Includes the reliability curve,
calibration error, a home-team baseline, and the market-agree / market-disagree split that
shows where the model is least reliable. Pass ?sport= to narrow
to one report.
| Param | Type | Description | |
|---|---|---|---|
| sport | string | optional | Return only this sport's report. Omit for all sports. |
curl "https://api.matchprior.com/v1/backtest\
?sport=football" \
-H "X-API-Key: mp_live_…"
{
"football": {
"graded": 3120,
"correct": 1841,
"hitRate": 0.590,
"brier": 0.491,
"marketBrier": 0.453,
"homeBaselineHitRate": 0.452,
"calibrationError": 0.029,
"reliability": [
{ "bucket": "0.4–0.5", "predicted": 0.45, "actual": 0.44, "n": 880 },
{ "bucket": "0.5–0.6", "predicted": 0.55, "actual": 0.56, "n": 1042 },
{ "bucket": "0.6–0.7", "predicted": 0.65, "actual": 0.63, "n": 734 }
],
"byLeague": {
"E0": { "graded": 760, "correct": 449, "hitRate": 0.591 }
},
"marketAgree": { "n": 2480, "correct": 1602 },
"marketDisagree": { "n": 640, "correct": 239 },
"monthly": [
{ "month": "2026-04", "n": 260, "hitRate": 0.585 }
],
"missBreakdown": { "total": 1279, "draws": 611 }
}
}
| Field | Type | Description |
|---|---|---|
| hitRate / brier | number | Out-of-sample hit rate and Brier score. |
| marketBrier | number | null | Market Brier over the same set — lower than the model's, as published. |
| homeBaselineHitRate | number | Hit rate of naively always picking the home team — the bar the model must clear. |
| calibrationError | number | Mean gap between predicted confidence and realised hit rate. |
| reliability | object[] | Per-confidence-bucket { predicted, actual, n } — the reliability curve. |
| marketAgree / marketDisagree | { n, correct } | Hit counts split by whether the model agrees with the market favourite. Disagreement is the less reliable bucket. |
| monthly / bySurface | object[] / object | Time- and surface-sliced hit rates. |
| missBreakdown | { total, draws } | How many misses were draws — the hardest outcome to call. |
Rate limits
Quotas are per API key and per plan. Exceeding your quota returns
429 with { "error": "rate limit exceeded" }.
Because the underlying snapshot only refreshes every 6 hours, responses are cacheable —
honour the ETag / If-None-Match headers and
you will rarely approach your cap.
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | Requests allowed in the current window. |
| X-RateLimit-Remaining | Requests left in the current window. |
| X-RateLimit-Reset | Unix epoch (seconds) when the window resets. |
| Retry-After | Seconds to wait, returned alongside a 429. |
/v1/health endpoint is exempt from per-key limits.
Object glossary
The core shapes shared across endpoints. Generate typed clients from the machine-readable
contract at /v1/openapi.json.
| Object | Shape | Used by |
|---|---|---|
| Probs | { H, D, A: number } | predictions, accuracy |
| Outcome | "H" | "D" | "A" | predictions, accuracy |
| Fixture | { id, sport, league, date, home, away, … } | fixtures, predictions |
| Rationale | { ratingEdge, form, h2h, expectedGoals, … } | predictions |
| TeamRating | { team, rating, games, form, … } | ratings |
| PageMeta | { count, nextCursor } | all list endpoints |
| Error | { error: string } | all non-2xx responses |