Riftcast Developer API
League of Legends esports data and machine-learning match predictions over a simple REST API. List teams, players and tournaments, pull detailed stats, and get calibrated win probabilities with expected value - the same models that power riftcast.gg.
Get an API key →You'll need a free Riftcast account to generate a key - create one or sign in, then open the Developer API tab on your account page.
Base URL
https://riftcast.gg/v1
All responses are JSON. Every charged response includes an
X-Credits-Remaining header.
Authentication
Pass your key on every request, via either header:
Authorization: Bearer YOUR_KEY # or X-Api-Key: YOUR_KEY
Create and manage keys, and see your balance, on your account page. A key is shown only once at creation. Every account gets 20 free credits per month - claim them, or subscribe to a credit plan / top up, on your account page.
Credits & cost
Calls are billed from a prepaid credit pool shared across all your keys. Discovery and usage calls are free; data and predictions cost credits:
| Call | Cost |
|---|---|
Discovery lists & /usage | Free |
| Stats detail (team / player / tournament) | 1 credit |
| Market odds - series winner | 1 credit* |
| Market odds - all markets | 2 credits* |
| Prediction - FastTree | 2 credits |
| Prediction - LightGBM or PCA Sweep | 5 credits |
| Prediction - Consensus (all 3 models + blend) | 10 credits |
*Odds calls are charged only when the series actually has captured odds
for the requested scope - a "no odds yet" 404 is always free.
Failed calls are never charged: a 404 (unknown id),
400 (bad request) or 500 (prediction error) costs nothing.
When your balance is too low you get 402 Payment Required -
top up and retry.
Every account gets 20 free credits per month. For more, subscribe to a credit plan (best value per credit) or buy one-time top-ups on your account page.
Fair use: credits are per account. Creating multiple accounts to farm the free allowance isn't allowed - using the API from more than one account on the same IP address will get those accounts banned.
Discovery free
Fetch the valid IDs to use in stats and prediction calls. These change rarely - cache them.
- GET
/v1/teams- all teams (id, name, region, logo) - GET
/v1/players- all players (id, name, role, team) - GET
/v1/champions- all champions - GET
/v1/tournaments- all tournaments
curl https://riftcast.gg/v1/teams -H "Authorization: Bearer YOUR_KEY"
{
"count": 420,
"teams": [
{ "id": 279, "name": "100 Thieves", "region": null, "logoUrl": "https://..." },
...
]
}Stats 1 credit
- GET
/v1/teams/{id}- record, win rate, GPM, side win rates, champion pool - GET
/v1/players/{id}- KDA, CS/min, gold share, kill participation, champion pool - GET
/v1/tournaments/{id}- side win rates, first-objective rates, leaders
curl https://riftcast.gg/v1/teams/279 -H "Authorization: Bearer YOUR_KEY"
{
"id": 279,
"name": "100 Thieves",
"record": "5W - 1L",
"winRate": 83.3,
"stats": { "gpm": 2000, "csPerMin": 32.2, "kda": 5.68, ... },
"sideWinRate": { "blue": 1.0, "red": 0.75 },
"championPool": [ ... ]
}Odds 1–2 credits
The latest captured market odds for the next scheduled series between two
teams - the same market line riftcast.gg surfaces. Snapshots refresh periodically
pre-match; capturedAtUtc tells you how fresh the line is, and
isClosingLine marks the final pre-kickoff snapshot. You're charged
only when odds exist for the requested scope - a "no odds yet"
404 is free.
GET /v1/odds
| Query param | Type | Notes |
|---|---|---|
team1Id / team2Id | int | Required (or team1 / team2 names). Order doesn't matter - every price is labeled with its team. |
markets | string | winner (default, 1 credit) · all (2 credits: adds handicap lines, total maps, correct score, per-map and series stat totals — kills, towers, dragons, barons, inhibitors — and per-map kill handicaps) |
curl "https://riftcast.gg/v1/odds?team1Id=13&team2Id=3&markets=all" \
-H "Authorization: Bearer YOUR_KEY"
{
"match": {
"esportsMatchId": "...", "league": "MSI", "bestOf": 5,
"scheduledUtc": "2026-07-04T08:00:00",
"team1": { "id": 13, "name": "Bilibili Gaming" },
"team2": { "id": 3, "name": "T1" }
},
"capturedAtUtc": "2026-07-03T12:04:11",
"isClosingLine": false,
"markets": {
"seriesWinner": [
{ "team": { "id": 13, "name": "Bilibili Gaming" }, "decimalOdds": 2.20 },
{ "team": { "id": 3, "name": "T1" }, "decimalOdds": 1.61 }
],
"handicap": [
{ "line": -1.5, "odds": [
{ "team": { "id": 3, "name": "T1" }, "line": -1.5, "decimalOdds": 2.30 },
{ "team": { "id": 13, "name": "Bilibili Gaming" }, "line": 1.5, "decimalOdds": 1.57 } ] }
],
"totalMaps": [ { "line": 4.5, "over": 2.75, "under": 1.40 } ],
"correctScore": [
{ "team": { "id": 3, "name": "T1" }, "score": "3-0", "decimalOdds": 4.33 },
...
],
"mapTotals": [
{ "map": 1, "stat": "kills", "line": 28.5, "over": 1.83, "under": 1.83 },
{ "map": 1, "stat": "towers", "line": 11.5, "over": 1.90, "under": 1.76 },
...
],
"matchTotals": [ { "stat": "kills", "line": 62.5, "over": 1.83, "under": 1.83 }, ... ],
"killHandicap": [
{ "map": 1, "line": 10.5, "odds": [
{ "team": { "id": 13, "name": "Bilibili Gaming" }, "line": 10.5, "decimalOdds": 1.83 },
{ "team": { "id": 3, "name": "T1" }, "line": -10.5, "decimalOdds": 1.83 } ] }
]
}
}
Markets the line-setter hasn't published come back null.
With markets=winner only seriesWinner is returned. Coverage
isn't universal - smaller leagues often have no listed odds, in which case the call is
a free 404.
Predictions
Win probabilities for any matchup. Provide team IDs (from
/v1/teams) - names also work but IDs are unambiguous. Optionally pass
decimal oddsBlue / oddsRed to get expected value (EV) back.
POST /v1/predict
| Field | Type | Notes |
|---|---|---|
blueTeamId / redTeamId | int | Required (or blueTeam / redTeam names) |
model | string | fasttree · lightgbm · pcasweep · consensus |
oddsBlue / oddsRed | number | Optional decimal odds → EV in response |
tournament | string | Optional league context (e.g. "MSI 2026", "LCK 2026 Summer") - mirrors what the site passes for its stored predictions: it sets the models' league feature, keys the consensus weights' per-league record and selects the calibration league. Whether the league feature moves the raw output depends on the active model configuration (league features are sweep-selected), so identical raw results with and without it are normal. |
bestOf | int | Optional series format: 1, 3 or 5. The models carry per-format features and a per-format calibration curve; omitted → Bo3 assumed. |
curl -X POST https://riftcast.gg/v1/predict \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"blueTeamId":279,"redTeamId":110,"oddsBlue":2.0,"oddsRed":1.8,"model":"consensus","tournament":"LCS 2026 Summer"}'
{
"blueTeam": { "id": 279, "name": "100 Thieves" },
"redTeam": { "id": 110, "name": "3BL Esports" },
"model": "consensus",
"blueWinProbability": 0.8105,
"redWinProbability": 0.1895,
"calibratedBlueWinProbability": 0.7462,
"calibratedRedWinProbability": 0.2538,
"winner": "blue",
"odds": { "blue": 2.0, "red": 1.8, "blueEv": 0.621, "redEv": -0.659,
"calibratedBlueEv": 0.4924, "calibratedRedEv": -0.5432 },
"models": [
{ "model": "fasttree", "blueWinProbability": 0.79, "redWinProbability": 0.21, "calibratedBlueWinProbability": 0.74, "calibratedRedWinProbability": 0.26, "winner": "blue" },
{ "model": "lightgbm", "blueWinProbability": 0.83, "redWinProbability": 0.17, "calibratedBlueWinProbability": 0.76, "calibratedRedWinProbability": 0.24, "winner": "blue" },
{ "model": "pcasweep", "blueWinProbability": 0.81, "redWinProbability": 0.19, "calibratedBlueWinProbability": 0.75, "calibratedRedWinProbability": 0.25, "winner": "blue" }
]
}
A consensus call runs all three base models and returns them in
models alongside the blended top-level probability - one call, the full picture.
POST /v1/predict-draft
As /v1/predict, plus bluePicks and redPicks -
lists of { "role": "TOP", "champion": "Aatrox" } (roles: TOP, JUNGLE, MID,
BOT, SUPPORT). Champion names come from /v1/champions.
How to read the probabilities
Raw vs calibrated. blueWinProbability /
redWinProbability are the models' raw output.
calibratedBlueWinProbability / calibratedRedWinProbability run
that raw number through the same calibration layer riftcast.gg renders and prices with:
a per-model curve bucketed by recent graded predictions, corrected for systematic over-
or under-confidence at each confidence level. When you supply both
oddsBlue and oddsRed the calibrated number is additionally
anchored toward the de-vigged market price — that market-anchored value is the exact
probability the site's surfaces display and its value engine prices EV from, and
calibratedBlueEv / calibratedRedEv in the odds
object are computed from it (blueEv / redEv stay priced off
the raw probability). Pass tournament to select the league's calibration
record. Until enough graded history exists, calibrated equals raw.
Series semantics. The probability is the series-winner estimate used across riftcast - it is not a single-map number to compound with a best-of-N formula. Maps in a series are not independent (sides alternate, drafts adapt), so riftcast deliberately avoids iid Bo3/Bo5 amplification and instead grades and calibrates the raw number against realized series outcomes.
Sides. Both prediction endpoints are side-neutral by
design: map sides are unknown pre-match and alternate across a series, so the engine
always averages the two side orientations - swapping blueTeam and
redTeam returns complementary probabilities (within a small numerical
tolerance). The blue/red labels orient the response; they are
not a claim about which map side a team will play. /v1/predict-draft differs
by being draft-aware - each team's picks stay attached to that team, not by
being side-aware.
Consensus. The API's consensus is the same
blend the site stores and displays: each base model weighted by its recent graded
log-loss record (league-aware, and conditioned on model disagreement when this
matchup's picks split) times a bounded tilt for its realized value-pick ROI. The
calibrated consensus is also clamped inside its members' calibrated range — the blend
is never more convinced than its most convinced member.
Errors
Errors are JSON: { "error": "code", "message": "..." }.
| Status | Meaning |
|---|---|
401 | Missing or invalid/revoked key |
402 | Not enough credits - top up |
404 | Unknown id (not charged) |
429 | Rate limit exceeded (120 req/min per key) |
400 | Bad request (not charged) |