Elo ratings

Elo is a single number for how strong a team is, independent of its win-loss record. This page explains what the rating means, exactly how we compute it, and how to read one number against another. Implementation figures verified against the code and against production on 2026-09-04.

What Elo is

Elo is a zero-sum rating system, originally built for chess. Every team holds a number. When two teams play, the system predicts the result from the gap between their numbers, then moves points from the loser to the winner — the same quantity in each direction, so the league total never changes.

The size of the move depends on how surprising the result was. A favourite beating an underdog was already expected, so it gains little. An underdog winning was not, so it gains a lot. That is the whole idea: a rating is the running total of how often a team has done better than the system expected.

The practical consequence is that Elo answers a question a record cannot. A 3-0 record does not say who you beat. Elo does, because beating a strong opponent moves you further than beating a weak one.

How a rating updates after a game

First, the expected result for the home team, on a logistic curve with a 400-point scale:

expected_home = 1 / (1 + 10 ^ ((elo_away - elo_home_adjusted) / 400))

Then the rating change, where actual is 1 for a win, 0.5 for a tie or draw and 0 for a loss:

change    = K * (actual_home - expected_home) * mov_multiplier
elo_home += change
elo_away -= change

mov_multiplier scales the change by the margin of victory. For NFL and NBA it is a real number derived from the scoreline; for every other league we rate it is exactly 1, which reduces the formula to plain Elo. The next section is what it does and why we believe it.

Margin of victory, and how we tested it

A 40-point win says more than a 1-point win, but not forty times more, and a good team beating bad teams badly must not be allowed to inflate itself. The NFL multiplier handles both:

mov_multiplier = ln(|margin| + 1) * ( 2.2 / (0.001 * elo_diff_winner + 2.2) )
  • The log scaling makes the first point of margin worth far more than the twenty-fifth. Linear scaling would let one blowout dominate a season.
  • The denominator damps autocorrelation. It grows with the winner's pre-game Elo edge, shrinking the multiplier when a strong favourite wins big — otherwise favourites feed on themselves: beat weak teams heavily, gain rating, expect a bigger margin, beat them heavily again. Same 14-point win, three different favourites: an evenly matched winner gets 2.71, one 170 Elo ahead gets 2.51, one 400 ahead gets 2.29.
  • elo_diff_winner is signed, so an upset makes it negative and the multiplier larger. That asymmetry is deliberate.
  • A tie takes multiplier 1, not ln(0+1) = 0, which would freeze both ratings. A draw against a strong opponent is still information.

It shipped on evidence rather than on plausibility. Backtested across all 1,754 finished NFL matches we hold, scoring each game's pre-game expectation before the rating was updated — so every forecast is out of sample by construction. Post burn-in, n = 1,485:

MetricFlat K=20With multiplierChange
Log loss0.649900.64064−0.00926
Brier score0.228360.22357−0.00479
Accuracy61.9%64.1%+2.2 pts

Both proper scoring rules improve, so the gain is not an artefact of the model simply becoming more confident.

The form is per sport, and that is measured too. A 20-point NBA win is ordinary where a 20-point NFL win is not. NBA uses 538's power form (|margin| + 3)^0.8 / (7.5 + 0.006 · elo_diff_winner); applying the NFL log form to basketball would be a regression (log loss 0.64913 against 0.64038). NCAAF scores as an even better candidate for the log form than NFL did (0.60446 → 0.56208) and is deliberately left flat until that change is made on its own evidence. NHL cannot be judged yet — one season of finished matches with linescores is not enough.

Our parameters, by league

Every team starts at 1500 the first time it appears. Games are replayed in chronological order. K sets how fast ratings move; the home-field figure is an Elo bonus added to the home team's rating inside the expectation only — it is never stored, so a published rating is venue-neutral.

LeagueKHome fieldMargin of victory
NFL20+48Yes — log form
NBA20+100Yes — power form
NCAAF (FBS and FCS)20+48No — flat
NHL20+50No — flat
MLB4+24No — flat
Soccer20+39No — flat

A +48 home bonus means two evenly-matched NFL teams are a 56.9% / 43.1% proposition rather than a coin flip. In the NBA, where the bonus is +100, the same two teams are at 64.0%. Overtime wins count as full wins everywhere; NFL ties — five since 2020 — are scored 0.5 to both sides rather than skipped, so a real result is never silently dropped.

What happens between seasons

At every season boundary each team regresses 25% toward 1505:

elo = elo + 0.25 * (1505 - elo)

A team finishing at 1650 opens the next season at 1613.8; a team finishing at 1400 opens at 1426.2. This encodes an assumption worth making explicit: last season is evidence about this season, but not the whole story — rosters, coaches and schedules turn over, so three quarters of a team's edge carries forward and one quarter is handed back.

The mean is 1505 rather than 1500. That is deliberate and shared by every sport we rate; it is the long-run centre of the pool, not the starting value for a new team.

FBS and FCS are separate pools

College football is rated twice, as two independent runs over two league sets. An FBS rating and an FCS rating are not on the same scale and should never be compared directly — the FCS median sits about 30 points below the FBS median, and the two pools barely play each other.

One consequence we would rather state than hide: a team that plays in both classifications holds one current rating — its FBS one — and two separate trend lines. The rating table stores one row per team, so the FCS run is written only for teams that do not already hold a rating; history is keyed per pool and keeps both.

How to read a rating

1500 is average. Above it is better than average, below it is worse, and the distance is what matters — not the number itself. Translated into a prediction on a neutral field:

Rating gapStronger team wins
050.0%
2553.6%
5057.1%
10064.0%
15070.3%
20076.0%
30084.9%

So 1650 versus 1480 is a 170-point gap: the stronger team wins 72.7% of the time on neutral ground, 77.8% at home in the NFL, and 66.9% on the road. A rule of thumb worth memorising is that 100 points ≈ 64%.

What one game is worth

Take that same 1650 favourite against a 1480 underdog, in the NFL. The margin decides how far the rating moves, and an upset moves it much further:

ResultMultiplierWinner gains
Favourite wins by 31.29+7.0
Favourite wins by 71.93+10.5
Favourite wins by 283.13+17.1
Upset — underdog by 31.50+21.8
Upset — underdog by 72.25+32.8
Upset — underdog by 283.65+53.0

Under flat K those six rows would read +5.5 for every favourite win and +14.5 for every upset, regardless of score. That is the difference the multiplier makes.

But a rating only means something inside its own league

This is the mistake worth avoiding. The scales are genuinely different, because K, home advantage, whether margin of victory is used, season length and pool size all differ. Measured on 2026-09-04:

LeagueRated teamsLowestMedianHighestStd dev
NFL321337.51492.21713.194.8
NCAAF (FBS)1381363.51513.41667.654.0
NCAAF (FCS)1271385.51483.61662.248.2
NBA301196.01542.51790.8161.6
NHL321350.01495.01689.270.1
MLB381432.01505.01564.731.8

Read that table before judging any single number. A 1650 is a contender in the NFL, near the very top of college football, mid-table in the NBA — and impossible in MLB, where K is 4 and the entire league fits between 1432 and 1565. The best team in baseball rates 1565; a 1650 baseball team cannot exist. Same number, four different meanings.

For a concrete NFL anchor: the top-rated team is the Seattle Seahawks at 1713.1 and the bottom is the Arizona Cardinals at 1337.5 — a 375.6-point spread, which makes Seattle an 89.7% favourite on a neutral field and 92.0% at home.

What it is for

Elo gives you team strength independent of record, which is the input a record cannot provide. Three uses developers actually build on:

  • Matchup analysis. The gap between two ratings is directly a win probability, with no model of your own required.
  • A model feature. One well-behaved, comparable, already-normalised number per team — a cheap opponent-quality term for a prediction model, and a strong baseline to beat.
  • Schedule strength. Averaging the Elo of the teams on a schedule says more about difficulty than their combined record does.

Getting a rating

curl -H "X-Api-Key: $BBS_API_KEY" \
  https://api.bigballsdata.com/v1/teams/{team_id}/elo

Returns elo_rating, games_counted, last_computed and league context. elo_rank, the full rating history and model win probabilities are on paid plans; on the free tier elo_rank comes back null alongside an upgrade block naming what is withheld — the rating itself is free. Agents can read the same data through the get_team_elo tool over MCP.

elo_rank is the team's position within its own league, ordered by rating — consistent with the cross-league warning above.

Limits worth knowing

  • Ratings are recomputed in batches, not live. The job replays a league's full game history from scratch, so a rating is current as of the last run, not the last whistle. last_computed on the response tells you exactly when — check it rather than assuming.
  • A method change moves every rating. The NFL multiplier landed on 2026-09-04 and shifted the pool from a 1397–1613 range to 1338–1713. Any NFL Elo you recorded before that date is a flat-K number and is not comparable to one you read today.
  • Teams under 10 games get no published rating. The endpoint still answers 200, with elo_rating null and a note reading pending — insufficient games. Handle the null; it is not an error.
  • No adjustment for injuries, rest, travel or weather. Elo sees results, opponents and — for NFL and NBA — margins. Nothing else.
  • Not comparable across leagues, and not comparable between FBS and FCS.
  • A new team starts at 1500 and is provisional. Early in a team's history the rating is mostly the starting value, which is why the 10-game floor exists.

The replay is deterministic and idempotent: Elo depends only on prior games, so rerunning the job over the same game order reproduces identical values.