matchprior
API Reference · v1

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.

Read this first. These are the model's calibrated probabilities and Elo ratings — not pass-through bookmaker odds, and not betting advice. The model is well-calibrated: across 67,667 out-of-sample backtested predictions, every confidence tier landed within about a point of its claim (say 70–80% → won 75.3%; say 80%+ → won 86.3%). The /v1/accuracy and /v1/backtest endpoints publish exactly that, so you can verify it yourself. Simulated backtest results, a measure of calibration and not a profit claim — for information and entertainment only.
Base URL
api.matchprior.com/v1
HTTPS only · JSON only
Auth
X-API-Key
Header on every route except /health
Said 70–80%
75.3%
won, backtested (n=5,382)
Said 80%+
86.3%
won, backtested (n=2,212)

Authentication

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.

Your key looks like
mp_live_8Qd2f6Rb1Tn0Yk7Lx3Wp9Vc4Hs2Mg5
authenticated-request.sh cURL
# 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"
A missing or invalid key returns 401 with { "error": "missing or invalid X-API-Key" }. See errors.

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.

FieldTypeMeaning
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, 1200, default 50.
since string Opaque forward cursor from a prior page.nextCursor. Omit for the first page.
page-1.sh cURL
# First page
curl ".../v1/fixtures?sport=football&limit=50" \
  -H "X-API-Key: mp_live_…"

# → page.nextCursor: "ZjI6MTc4NjAwMDAwMDAwMA"
page-2.sh cURL
# 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.

StatusMeaningExample error
200OK — request succeeded.
304Not Modified — your If-None-Match ETag still matches; body omitted.
400Bad request — malformed query param (e.g. limit out of range)."invalid limit (1–200)"
401Unauthorized — missing or invalid X-API-Key."missing or invalid X-API-Key"
404Not found — no such fixture / resource."not found"
429Rate limited — you exceeded your plan's quota."rate limit exceeded"
503Stale — last data refresh failed or data is stale (/health only)."stale: last refresh failed"
401 Unauthorized JSON
{ "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.

EndpointAuthReturns
GET /v1/healthpublicService + data freshness.
GET /v1/predictionskeyPaged predictions for upcoming fixtures.
GET /v1/predictions/{id}keyOne prediction with full rationale.
GET /v1/fixtureskeyPaged upcoming fixtures.
GET /v1/ratingskeyElo leaderboard per league.
GET /v1/accuracykeyLive graded accuracy + calibration.
GET /v1/backtestkeyWalk-forward backtest, keyed by sport.
GET /v1/health No API key required health

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.

Response — 200
200 OK · Health JSON
{
  "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
}
FieldTypeDescription
okbooleanService is up.
versionstringAPI build version.
lastRefreshAtstring | nullUTC timestamp of the last data refresh.
lastRefreshOkbooleanWhether that refresh succeeded.
lastRefreshErrorstring | nullError from the last failed refresh, else null.
ageMsinteger | nullAge of the current snapshot in ms.
stalebooleantrue when the snapshot is past its freshness window.
refreshMsintegerRefresh interval (21600000 ms = 6h).
GET /v1/predictions Requires 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.

Query parameters
ParamTypeDescription
sportstringoptionalSport key — football, tennis, basketball, baseball, hockey, americanfootball, afl, rugbyleague, rugbyunion, netball, cricket.
leaguestringoptionalLeague code — e.g. E0, SP1, ATP, NBA, MLB, CRIC.
datestring<date>optionalSingle match day, yyyy-mm-dd.
limitintegeroptionalPage size 1200. Default 50.
sincestringoptionalForward cursor — see pagination.
request.sh cURL
curl "https://api.matchprior.com/v1/predictions\
?sport=football&league=E0&date=2026-08-15&limit=1" \
  -H "X-API-Key: mp_live_…"
200 OK · PredictionPage JSON
{
  "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 }
}
marketEdge is a disagreement signal, not an edge. It is simply 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.
GET /v1/predictions/{fixtureId} Requires 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.

Path parameter
ParamTypeDescription
fixtureIdstringrequiredComposite id of the form {league}|{date}|{home}|{away}, e.g. E0|2026-08-15|Arsenal|Chelsea. URL-encode the pipes (%7C) in path position.
request.sh cURL
curl "https://api.matchprior.com/v1/predictions/\
E0%7C2026-08-15%7CArsenal%7CChelsea" \
  -H "X-API-Key: mp_live_…"
200 OK · Prediction JSON
{
  "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 } }
}
Selected fields
FieldTypeDescription
probs{ H, D, A }Calibrated outcome probabilities (sum ≈ 1).
pick"H" | "D" | "A"The model's highest-probability outcome.
confidencenumberProbability assigned to the pick (0–1).
over25 / bttsnumberP(total goals ≥ 3) and P(both teams score) — football.
marketEdgenumberModel prob − market prob on the pick. A disagreement measure, not expected return.
contrarianbooleanThe model picks against the market favourite.
spread / totalPointsnumberBasketball etc. — expected home margin and total.
rationaleobjectThe transparent inputs: Elo ratings, ratingEdge, form, goal difference, H2H, expected goals.
GET /v1/fixtures Requires 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.

Query parameters
ParamTypeDescription
sportstringoptionalSport key (see predictions).
leaguestringoptionalLeague code.
datestring<date>optionalSingle day, yyyy-mm-dd.
limitintegeroptionalPage size 1200. Default 50.
sincestringoptionalForward cursor.
request.sh cURL
curl "https://api.matchprior.com/v1/fixtures\
?sport=tennis&league=ATP&limit=2" \
  -H "X-API-Key: mp_live_…"
200 OK · FixturePage JSON
{
  "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" }
}
FieldTypeDescription
idstring{league}|{date}|{home}|{away}.
startMsintegerUTC kick-off epoch in ms.
market{ H, D, A } | —De-vigged market probabilities, where an odds feed exists (football, tennis).
surfacestringTennis court surface.
neutralbooleantrue = neutral venue (no home-field adjustment).
GET /v1/ratings Requires X-API-Key ratings

The Elo leaderboard for a league, highest rating first — the same ratings that feed every prediction's rationale.

Query parameters
ParamTypeDescription
sportstringoptionalSport key.
leaguestringoptionalLeague code, e.g. E0.
limitintegeroptionalPage size 1200. Default 50.
request.sh cURL
curl "https://api.matchprior.com/v1/ratings\
?sport=football&league=E0&limit=3" \
  -H "X-API-Key: mp_live_…"
200 OK · ratings JSON
{
  "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 }
}
FieldTypeDescription
teamstringTeam / player name.
ratingnumberCurrent Elo rating.
gamesintegerGames included in the rating.
form("W"|"D"|"L")[]Most-recent-first results, from the team's perspective.
goalsFor / goalsAgainstnumberCumulative goals scored / conceded.
lastPlayedstring | nullDate of the most recent counted game.
GET /v1/accuracy Requires 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.

hitRate
0.590
correct / graded
brier (model)
0.491
lower is better
marketBrier
0.453
closing line, same set
200 OK · AccuracyRecord JSON
{
  "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 }
    }
  ]
}
FieldTypeDescription
gradedintegerSettled fixtures the model was scored on.
correctintegerSettled fixtures the pick got right.
hitRatenumbercorrect / graded.
briernumberMean Brier score of the model's probabilities (lower = better).
marketBriernumber | nullThe market's Brier over the same graded set, for an apples-to-apples comparison.
byLeagueobjectPer-league { graded, correct }.
recentobject[]Most recent graded fixtures with pick, actual outcome and probabilities — wins and losses alike.
Want the full reliability curve and per-bucket calibration? See the calibration page and the /v1/backtest endpoint.
GET /v1/backtest Requires 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.

Query parameters
ParamTypeDescription
sportstringoptionalReturn only this sport's report. Omit for all sports.
request.sh cURL
curl "https://api.matchprior.com/v1/backtest\
?sport=football" \
  -H "X-API-Key: mp_live_…"
200 OK · { [sport]: BacktestReport } JSON
{
  "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 }
  }
}
FieldTypeDescription
hitRate / briernumberOut-of-sample hit rate and Brier score.
marketBriernumber | nullMarket Brier over the same set — lower than the model's, as published.
homeBaselineHitRatenumberHit rate of naively always picking the home team — the bar the model must clear.
calibrationErrornumberMean gap between predicted confidence and realised hit rate.
reliabilityobject[]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 / bySurfaceobject[] / objectTime- and surface-sliced hit rates.
missBreakdown{ total, draws }How many misses were draws — the hardest outcome to call.
What the backtest is for. It exists to show calibration and honest limits — not returns. In this report the model agrees with the market on 2480 fixtures (hitting 64.6%) but on the 640 it disagrees most, it hits only 37.3%. Backtested performance is simulated and not indicative of future results.

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.

HeaderMeaning
X-RateLimit-LimitRequests allowed in the current window.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetUnix epoch (seconds) when the window resets.
Retry-AfterSeconds to wait, returned alongside a 429.
Plan quotas and pricing live on the pricing page. The public /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.

ObjectShapeUsed 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
Honest by design. Every field here is the model's own derived output. Nothing on this page is betting, investment or financial advice, and no field should be read as expected profit. The model is well-calibrated — treat each field as data you can verify, not as a betting edge.