JerryGM Projections API — v1
Weekly and full-season NFL fantasy projections (QB/RB/WR/TE, plus kickers and team defenses from v1.1 and individual defensive players from v1.3) from the JerryGM projection model. Backtested against held-out seasons; every response includes projected points and the projected stat line, under your league's own scoring.
All data is JerryGM model output derived from openly licensed nflverse data — licensed for external consumption.
Base URL: https://api.jerrygm.com/api/ext/v1 (staging: https://api-dev.jerrygm.com/api/ext/v1)
This page is served by the API itself at GET /api/ext/v1/docs — it always matches the deployed version. No API key needed to read it.
Try it live: the interactive console lets you build requests, see responses (with a results table and rate-limit info), and copy cURL commands. Bring your API key.
How accurate is it? The projection accuracy report grades the number this API served before every kickoff against the box score — by year, week and position, beside a popular free weekly projection graded on the same players.
Machine-readable spec: GET /api/ext/v1/openapi.json (OpenAPI 3.0) — generated from the same constants the server validates against, so its enums cannot drift from the code. Point a client generator at it.
Authentication
Every request needs an API key:
X-API-Key: jgmk_<48 hex>
(Authorization: Bearer jgmk_… also works.) Keys are per-account, rate-limited per minute (responses carry X-RateLimit-Limit / X-RateLimit-Remaining; 429 + Retry-After when exceeded), and revocable.
Plans also carry a monthly call allowance. Every response includes X-Quota-Limit, X-Quota-Remaining and X-Quota-Reset (ISO timestamp of the first of next month, UTC); exceeding it is a 429 whose body names your plan, the limit, resetAt and an upgradeUrl. Some parameters are plan-gated (see 402 under Errors). GET /sources echoes your current plan and limits.
Getting a key. API access is enabled per account by JerryGM. Once your JerryGM account is enabled, create keys yourself in the app:
- Sign in at JerryGM and open Settings → System → JerryGM API. (The tab only appears once your account is enabled for API access.)
- Under Name your new key, give it a label (e.g. "JGM Projections") and click + Create key. You can rename a key later with the ✏️ button.
- Copy the key immediately — it is shown exactly once; only a hash is kept on our side. Revoking a key in the same screen disables it within a minute.
Don't have access yet, or lost a key? Contact JerryGM and we can enable your account, or issue and manage keys for you.
GET /projections
| param | default | meaning |
|---|---|---|
season | current season | e.g. 2026 |
week | *(omitted)* | 1–18 → that week's projection using data available before it; current → the upcoming week (see below); omit → full-season/preseason projection |
horizon | week / season | ros (with week=) adds a ros block per player: the per-game line summed over the games left in your league's season. See *Rest of season* |
regularSeasonWeeks | 18 | Your league's regular-season length. With playoffWeeks it defines the fantasy season (weeks 1..R+P): seasonTotal and ros count only those games |
playoffWeeks | 0 | Playoff weeks after the regular season (R + P ≤ 18). ros then splits into regularSeason and playoffs |
scoring | half | ppr | half | std, or a URL-encoded JSON weights object (below) |
source | jgm_baseline | jgm_baseline | jgm_cofactor | nflverse_naive |
ids | — | comma list of player ids |
idType | gsis | namespace for ids: gsis espn sleeper mfl yahoo cbs sportradar sdio pff fantasydata |
names | — | ;-separated names; , also works when no "Last, First" entries. Team defenses match by franchise name, nickname, or code — see below |
positions | all | comma list of QB RB WR TE K DST DL LB DB — narrows the pool (applied before any plan cap). IDP means DL,LB,DB; a fine defensive spelling (DE, EDGE, DT, ILB, OLB, CB, S, FS, SS) folds to its group |
includeInactive | false | true adds the players the all-players list leaves out: off an NFL roster or on a practice squad (rosterStatus offRoster / practiceSquad; projectedPPG 0). ids= and names= always return every player asked for |
No ids/names → all active players (everyone the model projects on an NFL active roster, reserve lists included — kickers, team defenses and individual defenders too). Practice-squad and off-roster players are left out unless includeInactive=true; the response's inactiveExcluded counts them.
Kickers and team defense (v1.1)
Kickers (position: "K") and team defenses (position: "DST", id DST-<team>, name <team> D/ST) are projected as the stat line their scoring needs, then scored under your tier weights:
- K
statLine:fgAtt,fgMade, and per distance bucketfgAtt0to19…fgAtt50plus,fgMade…,fgMissed…, pluspatAtt,patMade,patMissed. Attempts per bucket are a trailing blend; the make rate per bucket is the kicker's own, shrunk toward the league rate for that distance (breakdown.makeRates). Kicking data starts with the 2025 season. - DST
statLine:defSacks,defInterceptions,defFumbleRecoveries,defFumblesForced,defTds,defReturnTds,defTacklesForLoss,defSafeties,defBlocks,defTwoPtReturns,pointsAllowed(mean),yardsAllowed.defTdsis every DST touchdown (interception and fumble returns plus kick/punt returns) anddefReturnTdsis the kick/punt-return share of it. Points allowed keeps a spread (breakdown.pointsAllowedSd), and the tier score is the expected value over that spread (breakdown.paTierExpectedPPG) — scoring the mean alone would snap every defense into one tier.
Event-set semantics, stated so nothing is scored twice. Each of these is a distinct set of plays; a league may price any of them independently.
| field | counts | does NOT count |
|---|---|---|
defSacks | sacks | — |
defTacklesForLoss | tackle-for-loss credits on plays that were not sacks | sacks — even though the NFL credits a TFL on most of them. defSacks × a + defTacklesForLoss × b never scores one play under both. If your league's provider counts sacks as tackles for loss, use defSacks + defTacklesForLoss. |
defBlocks | blocked field goals + blocked PATs + blocked punts | — (there is no other blockable kick). Equivalent to a single "Blocked Kick" category. |
defTds | every touchdown scored by the defense/special teams unit: interception returns, fumble returns, kick and punt returns, blocked-kick returns | offensive touchdowns |
defReturnTds | the kick/punt-return share of defTds | interception and fumble returns |
defInterceptions, defFumbleRecoveries, defFumblesForced, defSafeties, defTwoPtReturns | as named | — |
stTds (QB/RB/WR/TE) | touchdowns the player scored on a special-teams play — kick and punt returns in practice (2025: 19, all by WR/RB); a blocked-kick recovery would count | offensive fumble-recovery touchdowns (fumbleRecoveryTds), anything defensive |
fumbleRecoveryTds (QB/RB/WR/TE) | the player recovering a fumble for a touchdown, including a teammate's fumble recovered in the end zone with no return | stTds; the two are separate nflverse columns |
The active points-allowed ladder is whatever your defense.paTiers says; without one it is the default returned by GET /sources under defenseDefaults.paTiers: [[0,10],[6,7],[13,4],[20,1],[27,0],[34,-1],[999,-4]] — the Sleeper / Yahoo standard. The band edges are yours to set; they do not have to be the standard ones. ESPN's default, for example, has eight rungs with different edges and an open top tier at 46+:
"defense": {"paTiers": [[0,5],[6,4],[13,3],[17,1],[27,0],[34,-1],[45,-3],[999,-5]]}
Each rung is [upper bound inclusive, points], ascending; the first rung whose bound is ≥ the points allowed scores, and the projection prices the ladder as an expectation over the defense's points-allowed spread, so a team projected at 20.4 is not snapped into one band.
Looking a defense up by name. Every platform spells a team defense differently, so names= accepts all of them and resolves to the one row:
Los Angeles Chargers D/ST Chargers D/ST Chargers LAC D/ST LAC
all return DST-LAC, as do the box-score spelling LA Chargers and the glued form LACDST. Full franchise name, nickname, city where it is unambiguous, and any code your platform uses (ESPN WSH, MFL KCC, the old OAK/SD/STL) are accepted, with or without a D/ST / DST / DEF / Defense marker, spaced or glued to the code. Three rules worth knowing: a shared city alone (Los Angeles, New York) is ambiguous and comes back in unmatched rather than guessed; the bare code LA is the Rams (it is their nflverse code), so LA, LA D/ST and LADST all return DST-LA — write LAC or LA Chargers for the Chargers; and a *player* whose name contains a city word (Dallas Goedert) is always matched as a player. Our own canonical spelling is <TEAM> D/ST with the nflverse code (LA for the Rams, WAS for Washington, JAX), which is also what ids.gsis carries as DST-<TEAM>.
Scoring: presets use standard unit scoring. Override with scoring.kicker and/or scoring.defense inside the weights object (GET /sources returns the defaults under kickerDefaults / defenseDefaults):
{"rec":1,"passTd":4,
"kicker":{"fg0to19":3,"fg20to29":3,"fg30to39":3,"fg40to49":4,"fg50to59":5,"fg60plus":6,"fgMiss":-1,"pat":1,"patMiss":-1},
"defense":{"sack":1,"int":2,"fumbleRecovery":2,"defTd":6,"safety":2,"blockKick":2,
"paTiers":[[0,10],[6,7],[13,4],[20,1],[27,0],[34,-1],[999,-4]]}}
paTiers is [[upperBound, points], …] ascending — the first tier whose bound is ≥ points allowed scores; use 999 for the open top tier. defense.fumbleRecovery also accepts the short fumRec on input (folded to the long name — rec means *receiving* everywhere else in this API). defense.tfl is points per tackle for loss. defense.retTd prices a kick/punt return touchdown apart from a defensive one: leave it out and defTd covers every DST touchdown; set it and return TDs score retTd while defTd covers only the interception and fumble returns ({"defTd":6,"retTd":7}). yaTiers (yards allowed) has the same shape; paPerPoint / yaPerYard are the linear alternatives. Per-bucket miss weights (fgMiss40to49 …) override the flat fgMiss. fgYd / fgYdOver30 (points per made field-goal yard, Sleeper's fgm_yds) are scored from the projected made yardage per bucket (statLine.fgMadeYds, fgMadeYdsOver30).
Individual defensive players (v1.3)
Defenders are projected at three groups — position: "DL", "LB", "DB" — with the fine spelling nflverse lists him at in positionDetail (DE, DT, NT, OLB, ILB, MLB, CB, SAF, FS, SS…). The group is the fine position folded (DE/DT/NT/EDGE → DL, ILB/OLB/MLB → LB, CB/S/FS/SS → DB), nflverse's position_group deciding when the spelling is unfamiliar. Your platform may slot an edge rusher differently (Sleeper lists some as DE, nflverse as LB); positionDetail is there so you can re-group by your own rule.
statLine (per game, always numeric):
| field | counts | does NOT count |
|---|---|---|
tacklesSolo | tackles he made — nflverse def_tackles_solo plus def_tackles_with_assist (a tackle he made with help is his tackle) | assists |
tacklesAssist | assists he was credited on someone else's tackle (def_tackle_assists) | his own tackles |
tacklesTotal | tacklesSolo + tacklesAssist — equals the official combined tackle count (2024: Zaire Franklin 173, Roquan Smith 154) | — |
tacklesForLoss | tackle-for-loss credits on plays that were not sacks (from play-by-play) | sacks — the NFL credits a TFL on ~81% of them (2025: 1,042 of 1,287; Myles Garrett's 33 raw credits were 21 sacks + 12 others), so the raw count under sack × a + tfl × b would score most sacks twice. Same rule as the team line's defTacklesForLoss, the one place this API departs from the official stat. If your provider counts sacks as TFL, use sacks + tacklesForLoss |
sacks | sacks, fractional — a half-sack is 0.5 | QB hits |
qbHits | hits on the passer, sacks included — the official stat, which is what every platform prices; play-by-play credits the sacker with a hit on 94% of sacks (2025: 1,213 of 1,287). A league scoring sack and qbHit scores a sack under both, as the platforms do; use qbHits − sacks for hits that were not sacks | — |
interceptions | interceptions | passes defended |
passesDefended | passes defended, interceptions included — the official stat (every 2025 interception carries a pass-defended credit to the interceptor). int + passDefended on an interception is how the platforms score it; use passesDefended − interceptions for the rest | — |
fumblesForced | fumbles forced | recoveries |
fumblesRecovered | an opponent's fumble recovered (fumble_recovery_opp) | his own team's fumble he fell on |
defTds | his interception-return and fumble-return touchdowns | a kick or punt return he took back (that is a special-teams score) |
safeties | safeties | — |
How it is projected. A trailing blend of the per-game line (recency-weighted current season plus the prior season, the same window as every other position) anchored on defensive snap share: the line is scaled by his recent snap share over his trailing share (half-weight, capped ±50%, breakdown.snapShare), so a backup promoted by an injury or a starter losing his job projects off the role he has now. A week he played (defensive snaps) without registering a stat is a real zero game, not a missing one. The result is shrunk toward what a defender with his snap share produces in his group — not the flat group mean, which is built from starters and would pull every part-timer up. Rookies: a defender is projected from his first NFL row; there is no pre-debut prior for IDP in v1.3 (the draft-round classes are too mixed by role to support one), so a rookie who has not played is not on the board yet.
Backtested on held-out 2024 and 2025 (weeks 3-18, projecting each week from the weeks before it): the model beats a naive trailing average by 3-5% MAE in every group and orders players better (rank correlation DL 0.41, LB 0.62, DB 0.54). IDP is noisy — a linebacker's weekly error is about 2 points on an 8-point line — which is the ceiling of the data, not of the model; the value is the component line under your scoring.
Scoring. Presets score defenders under typical IDP weights (GET /sources → idpDefaults); override with scoring.idp inside the weights object:
{"rec":1,"passTd":4,
"idp":{"tackleSolo":1,"tackleAssist":0.5,"tackle":0,"tfl":1,"sack":2,"qbHit":0,
"int":3,"passDefended":1,"ff":2,"fumRec":2,"td":6,"safety":2}}
tackle prices the combined total for leagues that do not split solo/assist (leave tackleSolo and tackleAssist at 0 then). fumRec is the fumble-recovery key here — rec means receiving everywhere else in this API. Sleeper leagues map directly (idp_tkl_solo, idp_tkl_ast, idp_tkl, idp_tkl_loss, idp_sack, idp_qb_hit, idp_int, idp_pass_def, idp_ff, idp_fum_rec, idp_def_td, idp_safe), and so do MFL leagues (the TK/AS/SK/QH/TKL/IC/PD/FF/FC/#D/SF events on a defensive position group; AS2 is read as half a point per assist) and ESPN leagues (statIds 108 solo, 107 assisted, 109 total, 112 stuffs = TFL, 106 forced fumbles, 113 passes defensed, plus the league-wide side of 99 sacks / 95 INT / 96 fumble recoveries / 98 safeties / 103-104 return TDs — read only when the league carries a defender-only stat), Yahoo leagues (the DP stats 38-47 and 65: solo, assist, sack, INT, forced fumble, fumble recovery, TD, safety, pass defended, blocked kick, TFL) and CBS leagues (TK, ATK, PDef, TFL, and the one-value-per-category SACK/Int/FF/DFR/STY/DFTD when the roster starts defenders).
Custom league scoring
scoring accepts a JSON object of per-stat weights (unknown keys are a 400):
passYd passTd passInt rushYd rushTd rec tePremiumRec recYd recTd
fumbleLost twoPt specialTd passFd rushFd recFd
passCmp sackTaken retYd pickSix fumbleRecoveryTd
fumble stFumbleForced stFumbleRecovery
passFd / rushFd / recFd are points per first down (passing, rushing, receiving), for PPFD leagues; the projected per-game first downs ride on statLine as passFd, rushFd, recFd.
passCmp is points per completion, sackTaken per sack the passer takes (the offense's — a defense's sacks are scoring.defense.sack), retYd per kick or punt return yard (one weight; statLine.retYds is their sum), and pickSix per interception the passer throws that is returned for a touchdown — a Yahoo league's -2 sacks / -6 pick-six / 1 per 20 return yards is {"sackTaken": -2, "pickSix": -6, "retYd": 0.05}. Completions and sacks project from the passer's own line; return yards belong to whoever returns kicks, which the depth chart decides, so they are the noisiest number on the line. A pick-six is rare enough (~30 a season league-wide) that a passer's own history proves little: statLine.pickSix is projected attempts × a per-attempt rate shrunk hard toward the league rate (about a third of the way toward his own after a full season), and breakdown.pickSix shows the rate, the league rate, and the attempts and pick-sixes it was built from.
fumbleRecoveryTd is an offensive player's own fumble-recovery touchdown (Yahoo's "Offensive Fumble Return TD", Sleeper's fum_rec_td; a recovery in the end zone with no return counts too — and it is spelled out because rec means *receiving* everywhere else in this list). It exists so a league's whole rule set can be sent in one object — there were two in all of 2025 across ~6,000 skill player-games, so it is projected at the league per-game rate for everyone (statLine.fumbleRecoveryTds, a few ten-thousandths), never from a player's own history.
fumble is every fumble, recovered by either team — Sleeper's fum, which it charges on top of fum_lost. The two are a superset and its subset, so sending both ({"fumble": -1, "fumbleLost": -2}) charges a lost fumble −3 and a recovered one −1, exactly as Sleeper scores it. Unlike the rare events below, this is a real projection: fumbles run about twice the lost ones and a carrier's rate is stable enough to blend, so statLine.fumbles is his own number.
stFumbleForced / stFumbleRecovery are a forced fumble or a fumble recovery on a kickoff or punt (Sleeper st_ff / st_fum_rec; the IDP block carries def_st_ff / def_st_fum_rec under the same two names for defenders). They exist so a league's whole rule set is accepted rather than rejected — in the entire 2025 regular season there were 32 forced fumbles and 61 recoveries on those plays across every player in the league, so like fumbleRecoveryTd they are projected at the league per-game rate, never from one player's history.
These ride on every statLine as raw components — passCmp, sacks, retYds, pickSix, fumbleRecoveryTds, fumbles, stFumblesForced, stFumbleRecoveries — whether or not you pass the weights, so a client that applies its own league scoring to our component line can read them directly with no scoring object at all.
Long plays (v1.2)
longPlays prices a single play by its length — *+1 for each 40+ yard completion*, *+2 for each 40+ yard touchdown*. It rides inside the scoring object beside bonuses, and the threshold is yours to choose. A rule with points: 0 is report-only: breakdown.longPlays[].perGame still carries the expected count per game and projectedPPG is untouched, for clients that apply their own scoring to our components:
"longPlays": [
{"type": "passCmp", "min": 40, "points": 1},
{"type": "passTd", "min": 40, "points": 2},
{"type": "recTd", "min": 40, "points": 2},
{"type": "rushTd", "min": 40, "points": 2}
]
Rates without scoring — longPlayRates, every plan. The expected count of long plays is component data, like any statLine number, so it is not gated behind custom scoring and does not require a weights object at all. longPlayRates=40 reports every long-play type at 40+ yards; longPlayRates=rec:40,rush:50,passCmp:40-49 names types and bands. It works beside any scoring value — presets included — and lands in breakdown.longPlays as points: 0 rows (perGame is the number you want), with projectedPPG untouched:
GET /projections?week=2&scoring=half&longPlayRates=40
→ breakdown.longPlays: [{type:"rec", min:40, points:0, perGame:0.296, ppg:0}, …]
If the scoring object also prices longPlays, those rows keep their points and the report-only rows follow them in the same array. A longPlays rule with points: 0 inside a scoring object does the same thing, but the object itself is custom scoring (Starter+) — longPlayRates is the way to get the rates on Free.
type | counts | credited to |
|---|---|---|
passCmp | completions of min+ yards | the passer |
passTd | passing touchdowns of min+ yards | the passer |
rec | receptions of min+ yards | the receiver |
recTd | receiving touchdowns of min+ yards | the receiver |
rush | rushes of min+ yards | the rusher |
rushTd | rushing touchdowns of min+ yards | the rusher |
A rule is worth points × the player's expected number of such plays per game — a rate, not a probability, because one game can hold two. The rate comes from that player's own long plays (recent games weighted more, last season behind them), shrunk toward the positional rate, so a receiver with a quiet stretch is not priced at zero. breakdown.longPlays shows each rule with its perGame rate and the ppg it added, and breakdown.expectedLongPlayPPG is their sum.
- Any threshold from 20 yards up.
minis a yard count, not a bucket:37works. Below 20 is a 400 — play-by-play is only kept from there up, so a lower threshold would quietly undercount rather than be wrong loudly. maxcloses a band.{"type":"passCmp","min":40,"max":49,"points":4}plus{"type":"passCmp","min":50,"max":59,"points":5}pays a 55-yard completion 5, once. Withoutmaxa rule is open-ended and overlapping rules stack (see below).999means open, so a provider's40-999is the same rule as a hand-written40+and shares its cached board.- One 42-yard completion feeds two rules:
passCmpfor the quarterback,recfor the receiver. A 42-yard touchdown feeds theTdtypes as well, so a league paying for both the play and the score pays twice, as it should. - Overlapping thresholds stack. Paying 1 at 40+ and 2 at 50+ scores 3 on a 55-yarder. If your league means them exclusively, add
max— the response'sscoringWarningswill point this out whenever rules overlap. - Play-by-play covers 1999 on. A player with no long plays in the sample is priced at the positional rate, not zero.
Example — 6-pt pass TD, full PPR, +0.5 TE premium:
GET /projections?season=2026&week=3&scoring={"passYd":0.04,"passTd":6,"passInt":-2,"rushYd":0.1,"rushTd":6,"rec":1,"tePremiumRec":0.5,"recYd":0.1,"recTd":6,"fumbleLost":-2,"twoPt":2,"specialTd":6}
(URL-encode the JSON.) GET /sources returns the preset-equivalent weight objects to start from.
#### Threshold bonuses
Add a bonuses array to the weights object for rules like *+5 for 300 passing yards* or *+3 for 100 rushing/receiving yards*. Any threshold works — min is yours to set:
{"passYd":0.04,"passTd":4,"rushYd":0.1,"recYd":0.1,"rec":1,
"bonuses":[{"stat":"passYds","min":300,"points":5},
{"stat":"rushRecYds","min":100,"points":3},
{"stat":"passTds","min":3,"points":2}]}
stat is any statLine key, plus rushRecYds (rushing + receiving yards combined); GET /sources lists them under bonusStats. Up to 12 per object.
Add max to close a band, for leagues that pay tiers rather than a single threshold. 100-149 pays only in that range, so consecutive tiers each pay once instead of stacking:
"bonuses":[{"stat":"recYds","min":100,"max":149,"points":2},
{"stat":"recYds","min":150,"max":199,"points":3},
{"stat":"recYds","min":200,"points":5}]
Add positions to scope a rule, for leagues that tier the same stat by position — a tight end at 75 receiving yards where a receiver needs 100:
"bonuses":[{"stat":"recYds","min":75,"points":2,"positions":["TE"]},
{"stat":"recYds","min":100,"points":2,"positions":["WR"]}]
Listing every position is the same as omitting positions, and a rule without it applies to everyone.
Overlapping rules stack, and unknown keys are refused. Two rules that both match a game each pay — 100+ worth 2 and 150+ worth 3 pay 5 on a 160-yard game. That is deliberate: plenty of leagues write the upper tier as an extra on top. If yours means exclusive tiers, give the lower rule a max. When rules overlap the response carries a scoringWarnings array saying so; nothing is silently reinterpreted. Any key we do not recognize is a 400 rather than a quiet drop, because a discarded max turns exclusive bands into stacking ones and overpays the big games without telling you.
How it's scored. A bonus is worth points × P(stat ≥ min) per game, and that probability is *not* derivable from the mean stat line — two players projected at 260 yards can have very different 300-yard rates — so it is modeled from each player's game-to-game spread (his own per-game variance, shrunk toward his position's when the sample is thin). Count stats treat "3 or more" as "more than 2.5". Every rule's probability and expected points appear in breakdown.bonuses, and their sum in breakdown.expectedBonusPPG. With bonuses, projectedPPG = your weights applied to statLine plus expectedBonusPPG.
Per-*play* rules (a 40+ yard completion) are a different thing — see Long plays below.
Boards are cached per rule set, so a second caller asking for the same thresholds gets the cached board.
Matching players by name
Name matching is case-, punctuation-, suffix-, and word-order-insensitive: Allen, Josh, josh allen, and st brown amon-ra all resolve. If your roster source has only proprietary ids + first/last names (e.g. balldontlie.io), join by names= on first sync, then cache our ids.gsis per player — it's stable — and use ids=/idType=gsis afterwards. Requested ids/names that don't resolve come back in unmatched (never an error).
Response
// GET /projections?week=current&scoring=half&names=Josh Allen;Tyrod Taylor;Travis Etienne;Kendre Miller;KhaDarel Hodge;Fake Person
{
"season": 2026, "week": 7, "weekAlias": "current", "horizon": "week", "scoring": "half", "source": "jgm_baseline",
"generatedAt": "2026-10-20T15:56:29Z", "dataThroughWeek": 6, "dataSeasons": [2026, 2025],
"statLineCoverage": { "passAtt": "modeled", "retYds": "modeled", "pickSix": "modeled", … },
"count": 5,
"unmatched": [ { "type": "name", "value": "Fake Person" } ], // only if any
// "inactiveExcluded": 402 all-players calls only: practice-squad and off-roster rows left out
"players": [
{
"gsisId": "00-0034857", "name": "Josh Allen", "position": "QB", "team": "BUF",
"rosterStatus": "active",
"projectedPPG": 21.87, // per game in his role
"potentialPPG": 21.87, // if he starts: a starter reads the same number twice
"depthChart": { "position": "QB", "rank": 1, "label": "QB1" },
"seasonTotal": 371.8, // season per-game reading × games in your season (17 by default)
"potentialSeasonTotal": 401.4, // seasonPointsToDate + potentialPPG × his team's games left
"seasonGames": 17,
"seasonPointsToDate": 160.8, // actually scored, under your scoring
"expectedSeasonGamesPlayed": 15.8, "expectedSeasonGamesAvailable": 15.8,
"expectedSeasonTotal": 375.1, // seasonPointsToDate + season reading × available games ahead
"pPlay": 0.98, "expectedPPG": 21.43, // weekly calls: injury alone, and projectedPPG × pPlay
"injuryStatus": null, "injuryNote": null, "practiceStatus": null,
"statLine": { // per-game, points-consistent
"passAtt": 25.27, "passCmp": 17.06, "passYds": 213.87, "passTds": 1.31,
"passInts": 0.59, "rushAtt": 6.67, "rushYds": 34.65, "rushTds": 0.95,
"targets": 0.1, "receptions": 0.1, "recYds": 0.1, "recTds": 0,
"fumblesLost": 0.14, "twoPt": 0.15, "stTds": 0, …
},
"gamesSampled": { "current": 6, "prior": 16 },
"ageMultiplier": 1,
"breakdown": {
"trailingPPG": 24.67, "positionMean": 12.81, "shrinkWeight": 0.764, "spotPrior": 12.76, …
// "cofactors": [ { "key", "delta", "detail" }, … ] jgm_cofactor source only
},
"ids": { "gsis": "00-0034857", "espn": "3918298", "sleeper": "4984",
"mfl": "13589", "yahoo": "30977", "cbs": "2181054", "sportradar": "…" }
},
{
// A healthy QB2 behind a healthy starter: a sliver of a start in his role this
// week, a starter's rate if he gets the job, and over the season the starts
// Love's expected missed games hand him (2.6 a game).
"gsisId": "00-0028118", "name": "Tyrod Taylor", "position": "QB", "team": "GB", "rosterStatus": "active",
"projectedPPG": 0.52, "potentialPPG": 13.09,
"depthChart": { "position": "QB", "rank": 2, "label": "QB2" },
"seasonTotal": 44.2, "potentialSeasonTotal": 143.8, "seasonGames": 17,
"seasonPointsToDate": -0.2,
"expectedSeasonGamesPlayed": 3.6, // appearances, kneel-downs included: not "Love misses 4"
"expectedSeasonGamesAvailable": 15.1,
"expectedSeasonTotal": 23.5,
"pPlay": 0.97, "expectedPPG": 0.5, // his role is already in projectedPPG
"statLine": { "passAtt": 1.18, "passYds": 6.85, "passTds": 0.044, "rushYds": 1.23, … }, // scaled with projectedPPG
…
},
{
// A starter ruled out this week (listed Out): 0 for the week, line included.
// His rate and his season reading stay.
"gsisId": "00-0036973", "name": "Travis Etienne", "position": "RB", "team": "NO", "rosterStatus": "active",
"projectedPPG": 0, "potentialPPG": 9.7,
"depthChart": { "position": "RB", "rank": 1, "label": "RB1" },
"roleState": { "state": "holds", "job": "RB1", "source": "usage",
"why": "Out since week 4, and every week since was an injury, so he keeps the RB1 job." },
"seasonTotal": 164.9, "expectedSeasonTotal": 86.5,
"pPlay": 0, "expectedPPG": 0,
"injuryStatus": "Out", "injuryNote": "Hamstring", // the reason for the 0
…
},
{
// The man behind him steps into RB1 at the full rate of the spot.
"gsisId": "00-0038551", "name": "Kendre Miller", "position": "RB", "team": "NO", "rosterStatus": "active",
"projectedPPG": 6.09, "potentialPPG": 6.09,
"depthChart": { "position": "RB", "rank": 1, "label": "RB1" },
"promotion": { "ahead": ["Travis Etienne"], "pMiss": 1, "pStart": 1 }, // pStart is not in projectedPPG
"seasonTotal": 103.7, "expectedSeasonTotal": 82.3,
"pPlay": 0.9, "expectedPPG": 5.48, // projectedPPG × pPlay × promotion.pStart
…
},
{
// Off an NFL roster: 0, his last team kept so you can join on it, and why.
"gsisId": "00-0034854", "name": "KhaDarel Hodge", "position": "WR", "team": "SF", "rosterStatus": "offRoster",
"projectedPPG": 0, "potentialPPG": 2.26, "depthChart": null,
"seasonTotal": 0, "potentialSeasonTotal": null, "seasonPointsToDate": 2.3, "expectedSeasonTotal": 2.3,
"expectedSeasonGamesPlayed": null, "expectedSeasonGamesAvailable": null, // no injury read off a roster
"injuryStatus": "Released", "pPlay": null, "expectedPPG": 0,
…
}
],
"attribution": "JerryGM projections. Derived from openly licensed nflverse data (nflverse.com)."
}
Field reference (what the numbers mean)
| field | meaning |
|---|---|
team | His NFL team. For a practice-squad player, the team whose practice squad he is on; off an NFL roster, his last team (rosterStatus offRoster says he is gone). |
rosterStatus | Where the NFL's weekly roster file has him: active (the active roster, reserve lists and inactives included), practiceSquad, or offRoster (cut, released, retired, exempt). practiceSquad and offRoster read projectedPPG 0 and are left out of the all-players list unless includeInactive=true. null for past seasons and before week 1. Team defenses are active. |
projectedPPG | The headline number: fantasy points we expect per game in his role, under the scoring you requested — potentialPPG × his share of a starter's game on his team's live NFL depth chart (that share is projectedPPG ÷ potentialPPG). A starter reads his full rate, Questionable included (the chance he sits is in pPlay). A QB2 behind a starter who is not ruled out reads 4% of his rate (QB2s threw about 4% of their teams' passes in 2023-25), a QB3 1%; running backs, receivers and tight ends deeper than the starting group carry a share by slot (RB3 37%, RB4 3%, RB5 and lower 1%; WR4 75%, WR5 and lower 19%; TE3 50%, TE4 15%, TE5 and lower 3%). A player off an NFL roster or on a practice squad reads 0 (rosterStatus). It is never negative: a player the depth chart does not price — off a roster, on a practice squad, or a rate at or under 0 — reads 0, statLine included. A rostered running back, receiver or tight end the live chart does not list is priced behind his room's last man. Weekly calls: 0 when this week's report lists him Out or Doubtful or he is on a reserve list (IR, PUP, NFI, suspended) — the line and long plays too — and the players behind him are promoted on that same rule: a promoted player reads the full rate of the spot he steps into, with his chance to start beside it in promotion.pStart. Season and rest-of-season calls keep his per-game rate; the games he misses come out through the expected-games fields, and a reserve's reading adds, game by game, the games the players ahead of him are expected to miss. Current season only: past seasons and team defenses are the raw model rate (projectedPPG = potentialPPG). |
potentialPPG | Per game if he starts — the number a backup would score stepping into the job. For a backup QB it is his points per full game of playing time (a two-snap kneel-down counts as almost no game), pulled toward what fill-in starters score league-wide (breakdown.spotPrior). Equals projectedPPG for a starter. |
depthChart | His spot on his NFL team's live depth chart as priced this week: position, rank (1 = starter) and label ("QB2"). A promoted player reads the rank he steps into (see promotion). null when he has no chart spot, for team defenses and for past seasons. No two players share a spot: a tie on the source charts (two safeties both listed first, or a player the live chart does not list beside one it does) goes to the live chart's player first, then the better projection, and gaps stay — so each player ruled out ahead of a man moves him up exactly one spot. A ruled-out player keeps his own label while the man who steps in reads it too (he holds the spot only this week). The charts are live NFL team depth charts, refreshed every few hours. A job change can show before the chart moves, through recent starts or editor-confirmed news (see roleState). |
promotion | Weekly calls, present when a player ahead of him on his team's depth chart is ruled out this week — listed Out or Doubtful, or on a reserve list (a Questionable player never promotes anyone) — or is off an NFL roster. The players still playing fill the opened spots in chart order, one player per spot; a player ruled out himself is never promoted. ahead — their names; pMiss — 1, the spot ahead of him is open; pStart — on every position, the chance he is the one who plays the spot he is priced at: 1 outside a QB room; for a QB stepping in as the starter, the chance his team starts him, read from who started its last game (0.93 when he did, 0.70 for a fresh absence, 0.19 when someone else did); for a QB, also pStartIfOut — the chance his team starts him if every QB ahead of him sits. The two are equal for a QB stepping into the starting spot; a QB moving up into a reserve spot reads pStart 1 and pStartIfOut his odds should the start open too. pStart is not in projectedPPG — a promoted player reads the full rate of his new spot — and expectedPPG multiplies it in. |
roleState | Present when recent starts, the chart or editor-confirmed news read his job apart from his chart spot: state — holds (an injured starter keeps his job), lost (a starter who lost it, priced at his chart spot), new (a new QB1) or open (a contested QB1 job); job ("QB1"); why, in plain words; source (chart, usage or news — a confirmed story that the coach named, kept or benched a starter, or has not decided, read before the chart moves; current season, the upcoming week or later only); and, for a new or contested QB1, pStart — the chance he starts this week (0.93 for a new starter; 0.46 / 0.54 for the challenger and the incumbent in a contested job). Like promotion.pStart, it is not in projectedPPG — he reads his full starting rate — and expectedPPG multiplies it in. |
seasonTotal | The season per-game reading × the team's games in your league's season (regularSeasonWeeks + playoffWeeks; all 17 when omitted) — a pace number for rankings and auction math, not a games-played prediction. The season reading is projectedPPG for a starter. For a reserve it adds, game by game, the games the players ahead of him are expected to miss, whatever the horizon: Tyrod's week-7 call reads 0.52 this week and 2.6 a game over the season. A player ruled out this week keeps his season reading, not the week's 0. |
potentialSeasonTotal | What he finishes your league's season with if he starts every game from here: seasonPointsToDate plus potentialPPG × his team's games still ahead in your season (not yet played — a game counts as played once its stats post, as in ros.gamesRemaining; schedule games, not injury-adjusted). Before week 1 it is potentialPPG × seasonGames. A team defense reads its projectedPPG. null without a team or schedule, off an NFL roster or on a practice squad, and once the regular season is over (as expectedSeasonTotal). |
expectedSeasonTotal | What he finishes your league's season with: seasonPointsToDate (what he has actually scored) plus the season per-game reading (see seasonTotal) × the games still ahead that he is expected to be available for. His own missed games are priced in once, through availability; his role once, through the season reading — for a reserve, the starts he gets in the games the players ahead of him are expected to miss. A player ruled out this week keeps his season reading, not the week's 0. On a practice squad or off an NFL roster, his points to date. |
seasonPointsToDate | Points actually scored this season under the scoring you requested — the line, threshold bonuses met, long plays, kicker, defense and IDP rules included — for every game whose stats have posted, through the week you asked about. A game is on the books once nflverse posts it, so a Thursday game counts by Saturday; in the hour or so between the final whistle and the stats, it is still expected. 0 before his first game; null whenever expectedSeasonTotal is. |
statLine | The projected per-game box score behind the points: pass/rush/receiving attempts, yards, TDs, receptions, turnovers. Always consistent with projectedPPG — score the line under your weights and you get the same number (a backup's line, and a ruled-out player's 0, scale with projectedPPG). Useful if you want raw stats instead of (or as well as) points. |
pPlay / expectedPPG | Weekly calls only: pPlay is the chance injury or absence does not keep him out of this game — injury alone: his role is already in projectedPPG, so a backup is not discounted twice; null on a practice squad or off an NFL roster (no game to miss). It reads the official report and, for the upcoming week or later, an editor-confirmed injury story the report does not carry yet; a story moves pPlay and the expected-games fields, never projectedPPG or injuryStatus. expectedPPG = projectedPPG × pPlay × his chance to start (promotion.pStart, or roleState.pStart for a new or contested QB1; 1 without either) — the unconditional number a lineup sums; 0 on a practice squad or off an NFL roster. |
ros | Only with horizon=ros: gamesRemaining (the team's games from week through the end of your season that have not been played yet — a game is played once its stats post, so a Thursday game is not remaining by Saturday; no injury adjustment), projectedTotal (projectedPPG × gamesRemaining), byeWeek (if still ahead), regularSeason / playoffs windows ({weeks, games, projectedTotal}, the same unplayed count), statLine totals over gamesRemaining, longPlays[] with events = perGame × gamesRemaining when long plays were requested, pointsToDate (actual points from the window's games already played), and the availability-adjusted expectedGamesPlayed (games he is expected to get on the field), expectedGamesAvailable (games he is expected to be healthy and active for) and expectedTotal (pointsToDate + projectedPPG × expectedGamesAvailable; on a practice squad or off an NFL roster, pointsToDate), with their availability explanation (below). null fields for a player with no team. |
seasonGames | The games on the team's schedule inside your league's season — the count seasonTotal multiplies. null without a schedule or team. |
expectedSeasonGamesPlayed | Games he is expected to get on the field in your league's season: the ones he has appeared in plus the expectation for what is left, read from his own history — injury, role and production (see *Expected games played*). It counts appearances, not starts: any snap is a game, a reserve QB's kneel-down included, so a healthy reserve reads few and the number is not the games his starter is expected to miss (Tyrod's 3.6 is his own cameos, not "Love misses 4"; Love's expected missed games are in Tyrod's season reading). Before week 1's injury report it is the draft-time prior — last season's role and production, position and age — and basis says so. A team defense plays every game (its games on the books plus the games ahead). A defender (IDP) reads the same model — a game he played without a stat (on the field for defensive snaps) counts as appeared in. Past weeks are read off the schedule of the team he was on that week, so a traded player's count follows both teams' byes (it can reach 18). null whenever the data is unavailable, on a practice squad and off an NFL roster. |
expectedSeasonGamesAvailable | Games he is expected to be healthy and on the active roster for, whether or not he gets in: past games where he appeared, or was active (or a healthy inactive) and not listed Out or Doubtful, plus the injury-only expectation for what is left. Equals expectedSeasonGamesPlayed for a regular; well above it for a reserve, whose role is already in projectedPPG. This is the count expectedSeasonTotal multiplies. null when expectedSeasonGamesPlayed is (including on a practice squad and off an NFL roster). |
gamesSampled | How much real evidence the projection rests on: current = games this season, prior = games last season. Low numbers mean a projection that will move quickly as new games arrive. |
ageMultiplier | Age discount applied (1 = none). Older players at age-sensitive positions (RB from 27, WR from 30, TE 31, QB 36) are trimmed a few percent per year. |
breakdown.trailingPPG | The player's recency-weighted scoring rate before any adjustments — "what he's been doing lately," with recent games counting more. |
breakdown.positionMean | The typical scoring rate at the position in our pool. Small samples get pulled toward this so two hot games don't read as a superstar. |
breakdown.shrinkWeight | How much of the projection comes from the player's own data (1 = all his own, lower = more position-average). Rises with games played. |
breakdown.cofactors | (jgm_cofactor source only) Adjustments with plain-English reasons. Currently: a preseason market prior that fades as real games arrive ("Preseason market (ADP 26) implies ~20.7 PPG — weighted 50% until real games take over"), TD-luck regression ("Scoring 1.3 more TDs than usage supports — regression expected"), and, for weekly calls, Vegas implied team totals ("Vegas implies ~29.5 team points (+31% vs average) — pace-up spot") and current depth-chart slot ("Listed #2 on the current depth chart — limited role expected"). Every cofactor must beat our held-out backtests to ship. |
breakdown.bonuses / breakdown.expectedBonusPPG | Present when your scoring carries bonuses: one row per rule — {stat, min, points, probability, expectedPPG} — and their sum, which is already included in projectedPPG. |
breakdown.rookiePrior | Present for pre-debut rookies: the projection comes from the draft-capital prior (see below) rather than NFL games. Values: r1, r2, r3, late. |
injuryStatus | Weekly calls only: Out, Doubtful, Questionable, Reserve (on a reserve list — IR, PUP, NFI, suspended — without an Out or Doubtful this week), Released / Retired / Exempt (off an NFL roster, in the NFL weekly roster file's terms: cut or a free agent / retired / the commissioner's exempt list), or null (not listed / no designation). It is the official injury report's designation; when projectedPPG reads 0 for a designation the report does not show yet (the game-day feed leads it by a few hours), it is the one that ruled him out — which also wins over Released / Retired / Exempt. Out, Doubtful and Reserve make projectedPPG 0 for the week. Full-season calls return null. |
injuryNote / practiceStatus | The listed injury (e.g. Hamstring) and practice participation for the same report. null when not listed. |
ids | The player's ids across ecosystems. gsis is our anchor: the NFL's own Game Statistics & Information System id (00-00XXXXX), assigned by the league, career-stable, and never reused — cache this one. The others (espn, sleeper, mfl, yahoo, cbs, sportradar) are for joining your data. |
Your league's calendar (regularSeasonWeeks, playoffWeeks)
Almost no fantasy league plays week 18, and most split the season into a regular season and playoffs. Tell the API yours and every season-length number is counted over the right games from the real NFL schedule (byes included): seasonTotal becomes the per-game line × the team's games in weeks 1..R+P, and horizon=ros splits what is left. Omit both and the whole NFL regular season counts (17 games, no playoffs), which is what the API always did. The response echoes leagueWeeks: {regularSeasonWeeks, playoffWeeks, endWeek} so you can see what was applied.
The upcoming week (week=current)
A script that runs every week should not have to know what week it is. week=current resolves to the week whose lineups are next — it turns over Tuesday morning (09:00 UTC) after Monday night's game, or earlier once every game of the week has a score — and the response's week carries the number it landed on, with "weekAlias": "current" beside it:
GET /projections?week=current&scoring=half
→ { "season": 2026, "week": 4, "weekAlias": "current", "horizon": "week", … }
It composes with everything a numeric week does (horizon=ros, longPlayRates, ids, names, positions). Current season only — a historical call names its week. GET /sources reports the same number as nflWeek (null in the off-season, when week=current is a 400).
Rest of season (horizon=ros)
The week=N call already answers "what is this player's per-game rate right now" — this season's games weighted by recency, last season behind them — so rest-of-season is that line times the games he has left. horizon=ros does the multiplication with the actual schedule and your calendar:
GET /projections?week=3&horizon=ros®ularSeasonWeeks=14&playoffWeeks=3 # or week=current
→ "ros": { "gamesRemaining": 14, "projectedTotal": 266.6, "byeWeek": 7,
"regularSeason": { "weeks": [3, 14], "games": 11, "projectedTotal": 209.4 },
"playoffs": { "weeks": [15, 17], "games": 3, "projectedTotal": 57.1 },
"statLine": { "passAtt": 463.4, "passYds": 3497.2, … } }
gamesRemaining counts the team's games from week through the end of your season (byes are simply not games; a player with no team reads null); byeWeek is the team's bye if it is still ahead and inside your season — a bye that lands in playoffs is exactly what you want to know; regularSeason and playoffs are the same arithmetic over each window (playoffs is null without playoffWeeks); ros.statLine is the per-game line × gamesRemaining, so a client that scores components itself can sum it under its own rules. When the call carries longPlays or longPlayRates, ros.longPlays[] repeats each rule with events (perGame × gamesRemaining) and points_total, so explosive plays total over the same games as the box score. The per-game fields on the row are unchanged. ros uses source=jgm_baseline — the cofactor sources fold *this week's* matchup into the line, and a ROS total would multiply one Sunday's Vegas number across the season.
gamesRemaining is the schedule, not a prediction. It counts the games the team has left to play from week — a game whose stats have posted is no longer remaining — and does not subtract games the player may miss. The injury-adjusted count is a SEPARATE field, ros.expectedGamesPlayed, and it is never folded into ros.statLine or gamesRemaining — component totals stay a pure schedule × rate product you can adjust yourself.
Expected games played (availability)
"ros": { "gamesRemaining": 9, "projectedTotal": 181.4,
"expectedGamesPlayed": 8.1, "expectedGamesAvailable": 8.1, "expectedTotal": 163.3,
"availability": { "designation": "Healthy", "injury": null, "category": "none",
"role": "4", "tier": "T1", "expectedMissed": 0.88, "reportWeek": 10,
"basis": "Healthy · T1 · 4 · none · QB · 26to29 (n=302 player-weeks, 2016-2025)" } }
expectedGamesPlayed is gamesRemaining minus the games the player is expected to MISS — to injury, and for a backup to his role (a QB2 has no stat row in most games). expectedGamesAvailable counts only the games injury takes: for a player priced below a starter's full game (projectedPPG < potentialPPG) it reads him as a regular (role 4, and the tier of his potentialPPG), because his role is already in projectedPPG and pricing it twice would discount a backup twice. For a starter the two are the same number. expectedTotal is pointsToDate + projectedPPG × expectedGamesAvailable — a game already played in the window counts at its actual points, never projected again; availability describes his own history — the read behind expectedGamesPlayed (its expectedMissed is the model's read from week). The expectation comes from how players like this one actually fared in 2016-2025 — the same designation, injury, role and production tier, position and age — summed game by game over the team's remaining schedule, so a Questionable hamstring in week 2 and in week 17 read the same curve over 16 games and 2. availability says why:
designation—Out,Doubtful,Questionable,Listed(on the report with a practice note but no game status) orHealthy(not on the report).injuryis the report's text andcategoryour grouping of it (knee,hamstring,ankle_foot,leg,soft_tissue,shoulder,concussion,back,hand_wrist,ribs_chest,illness_rest,other;nonewhen healthy).role— how many of his team's last four games he posted a stat row in (a defender: a stat row or a defensive snap, since a defender can play a game without a stat) (0-4,nafor a player with no prior season). This is the biggest factor after the designation: a regular who played all four misses about 6% of games when healthy; a player who played one of four, 44%. It is also how the model sees a player the injury report never lists — the NFL report carries no reserve/IR entries, so a player with no designation and no recent games reads as absent rather than healthy.- last game — whether he played his team's previous game (the bye is not a game). Role counts four games; this says which one he missed: a healthy part-timer who sat LAST week misses the next far more often than one who sat an earlier week. It is a level of the model (
matchedOnlistslast). tier— trailing PPR points per game over his last eight games played (T1≈ top-12,T2≈ top-36,T3;nafor kickers). Separates the starter from the backup whose garbage-time snaps also make a stat row. For a defender it is his trailing defensive snap share instead (T1≥ 70%,T2≥ 40%,T3).expectedMissed— the number subtracted;reportWeek— the report it was read from (this week's, or last week's when this week's is not out yet, in which case the curve is read from its second game andbasissays so);basis— the most specific cell with evidence, and how much.
Machine-readable provenance. The same facts basis states in prose ride as fields, so a pipeline never parses the sentence:
status—MODELED(the exact cell of the hierarchy answered),FALLBACK(a shallower cell answered because the exact one had no evidence —matchedOnlists the features that were matched,cellDepthismatched/total, e.g.4/6),INSUFFICIENT_DATA(only the all-players root had evidence),REPORT_UNAVAILABLE(no injury report for this or last week, or the report feed failed — the schedule numbers stand, the expected ones are null),NOT_APPLICABLE(the position is not modeled — team defenses — or the player has no team). This is the availability counterpart ofstatLineCoverage.modelVersion(availability-v1),position(the modeled group),ageBand(u25/26to29/30plus),sampleSize+sampleUnit(player-weeks in the cell that answered),reportStale(last week's report was used),preseasonPrior(the draft-time prior, no report yet).GET /sources.availabilityModelstates the model once: version, training seasons and rows, the hierarchy's levels, shrinkage, horizon buckets, fit date, the held-out validation (select 2024 / confirm 2025, MAE in games per player-week against the schedule-only and designation-only controls), and the field list — so an integration can record exactly which model produced a release.
Healthy players are adjusted too: even a top-tier regular is expected to miss a fraction of a game per month to injuries and rest, and that base rate is what keeps a roster-wide sum honest. The number is an EXPECTATION — most regulars miss zero games and a few miss many — so sum it across a roster rather than reading it as a prediction for one player.
expectedSeasonGamesPlayed and expectedSeasonGamesAvailable (every horizon, top level) are the same arithmetic over your whole league season: games already on the books plus the expectation for what is left, next to seasonGames. expectedSeasonTotal is seasonPointsToDate plus the season per-game reading × the available games still ahead. Before week 1's report it is the draft-time prior: nobody is listed, and the number comes from last season's role and production, position and age (availability.basis ends in "preseason: no report yet, role and tier from last season") — on held-out seasons it is a better guess than 17 for the players a draft prices, by about a game and a half of RMSE. The line between the books and the games ahead is drawn per game, not per week: a game is on the books once its stats post, so on a Saturday a Thursday game is actual and Sunday's are expected. Team defenses and IDP get the season fields too (no availability model for either: a defense plays every game; a defender's games ahead are the schedule count). These fields are null whenever the stats or the schedule are unavailable — they fail soft, never the call. ros is the window from the week you asked for, so its finished games are in both seasonPointsToDate and ros.pointsToDate: expectedSeasonTotal = seasonPointsToDate − ros.pointsToDate + ros.expectedTotal. Backtest, method and tables: the GTM spec, §19.
Freshness, and zero vs. missing
Every response says what it was built from:
generatedAt— when this board was computed (boards are cached; this is the compute time, not the request time).dataThroughWeek— the last current-season week of stats in the blend. Not alwaysweek − 1: nflverse publishes on Tuesday, so a Monday call for week 3 may read1.nullmeans the board rests on the prior season alone (preseason).dataSeasons— the seasons the blend actually read, newest first ([2026, 2025]normally;[2026]before the prior season's file exists, or when it could not be fetched).degraded— present only when something was missing. The season files come from an upstream that can fail; when one the API expects is absent it still serves the board, but says so:{"missingSeasons": ["prior"], "detail": "…"}. A degraded board holds far fewer players than usual — roughly 40% fewer when the prior season is the one missing — and its numbers are internally self-consistent, so a reconciliation check will pass on it. This field is the only way to tell. If you fail closed, gate on its absence. A degraded board is also cached for minutes rather than hours, so retrying shortly after usually gets a complete one.statLineCoverage— perstatLinecomponent,modeled(its source column exists in every season the blend used),partial(in some — the number is real but built from less data than its neighbors), orunavailable(in none). A0underunavailableis the absence of a column, not a projection of zero.statLinevalues are always numbers, nevernull, so your arithmetic never has to branch — check coverage once per response instead. It covers every position's line: the skill components, the kicker buckets (fgAtt…/fgMade…/fgMissed…per distance,patAtt/patMade/patMissed,fgMadeYds), the defense components (defSacks…defTacklesForLoss,pointsAllowed,yardsAllowed) and the IDP line (tacklesSolo…safeties), so one rule applies everywhere:modeled+0is a projected zero;unavailableis no usable evidence.defTdsis a key on both the DST and the IDP line; its verdict is the two combined (partialwhen they differ). Today everything ismodeledfor 2025 and 2026; kicking, return yards and first downs areunavailable/partialfor historical calls that reach into the ≤2024 files, andtacklesForLossisunavailablewhen play-by-play could not be read (the value is then 0, not a projection).
Rookies
Rookies are always projected. Before their first NFL snap they get a draft-capital prior: the average rookie-season per-game stat line for their position and draft slot (1st round, 2nd, 3rd, later/undrafted), fitted from the last four rookie classes and priced under your league's scoring — e.g. a 1st-round QB opens around 15 PPG, a 1st-round WR around 11 PPR PPG. These rows are flagged with breakdown.rookiePrior and show gamesSampled: {current: 0, prior: 0} so you can treat them with appropriate uncertainty. The moment a rookie plays a real game, his actual usage takes over and the prior is dropped.
Notes
- Players are sorted by
projectedPPGdescending. - "All active" = everyone the model can project on an NFL active roster: players with NFL data this season or last, plus the current draft class via rookie priors. Practice-squad and off-roster players join with
includeInactive=true. The Free plan receives the top 100 of that list (see Plans below). - Positions: QB, RB, WR, TE, K, DST (K and DST since v1.1;
positions=narrows). DST rows carry a syntheticids.gsisofDST-<team>. weekuses only data available before that week — historical calls (e.g.season=2025&week=10) return what the model *would have said*, which is also how we backtest it.- Responses are gzip-compressed when the request sends
Accept-Encoding: gzip(every mainstream HTTP client does by default). The all-active payload is ~500 KB raw; send the header and you move a fraction of that.
GET /players
The whole projected pool's identity and id crosswalk — no projections. Every plan, never truncated, one call. Ids are free on purpose: join once against your own roster (by name or by the id you already have), cache ids.gsis, and call /projections by id from then on. The numbers are the product; the crosswalk is the on-ramp.
| param | default | meaning |
|---|---|---|
season | current season | which season's pool |
{
"season": 2026, "count": 650,
"players": [
{ "gsisId": "00-0034857", "name": "Josh Allen", "position": "QB", "team": "BUF",
"ids": { "gsis": "00-0034857", "espn": "3918298", "sleeper": "4984",
"mfl": "13593", "yahoo": "30977", "cbs": "2071510", "sportradar": "…" } }
],
"attribution": "JerryGM. Player ids derived from openly licensed nflverse data (nflverse.com)."
}
Sorted by name. Missing ids in a namespace are null, never omitted. Individual defenders carry position as the group (DL/LB/DB) and positionDetail as the fine spelling.
GET /sources
Lists projection sources, the preset scoring weight objects, valid weight keys, and valid idTypes. Also season and nflWeek — the week week=current resolves to right now (null in the off-season).
Errors
| code | meaning |
|---|---|
| 400 | bad param — message names the offending key/value |
| 401 | missing/invalid/revoked key |
| 402 | not included in your plan (a source, custom scoring weights, a prior season, or more ids/names than the plan allows per request) — body names requiredPlan and an upgradeUrl |
| 429 | per-minute rate limit (retry after Retry-After seconds) or monthly quota (body carries resetAt) |
| 503 | key verification temporarily unavailable |
Plans, licensing & attribution
| Free | Starter | Pro | Partner | |
|---|---|---|---|---|
| Calls / month | 1,000 | 50,000 | 500,000 | unmetered |
| Requests / minute | 60 | 120 | 600 | as agreed |
| Sources | jgm_baseline | all | all | all |
Custom scoring weights | — | ✓ | ✓ | ✓ |
longPlayRates (report-only explosive-play rates) | ✓ | ✓ | ✓ | ✓ |
| Prior seasons (point-in-time) | — | ✓ | ✓ | ✓ |
All-active call (no ids/names) | top 100 by projectedPPG | every player | every player | every player |
ids + names per request | 25 | unlimited | unlimited | unlimited |
GET /players (id crosswalk, no numbers) | every player | every player | every player | every player |
| Active keys | 1 | 3 | 10 | — |
| Attribution required | ✓ | ✓ | waived | as agreed |
| Commercial redistribution | — | — | ✓ | as agreed |
A call is one HTTP request, whatever it returns — the all-active call counts once. On the Free plan that call returns the top 100 players by projectedPPG; the response then carries truncated: true, total (how many the model projects) and a truncatedNote. Requests that name players by ids or names are never truncated on any plan, but on Free one request may name at most 25 (a 402 names the plan without that limit); GET /players lists every id regardless. Past your allowance, requests return 429 until the month rolls over (UTC); there is no overage billing. Plans are bought and managed in the app under Settings → System → JerryGM API; a canceled or lapsed plan drops to Free limits and keys are never revoked.
Attribution. Every response carries an attribution string. On Free and Starter, show it wherever numbers derived from this API are displayed. Pro waives it and licenses display of JerryGM-derived numbers inside your own commercial product. No plan permits reselling or redistributing the raw feed, presenting the output as your own model, or using it to train a competing projection product. Full terms: jerrygm.com/terms §11.
Stability & deprecation policy
- Additive within a version. Under
/api/ext/v1, fields and parameters may be *added* at any time. Nothing is removed, renamed, or re-typed, and existing defaults do not change. Parse responses accordingly: ignore fields you do not know. - Breaking changes get a new path. Anything that would break a correct v1 client ships as
/api/ext/v2. Holders of active keys get at least 90 days' notice by email before v1 is retired, and v1 keeps serving through that window. - Limits. A reduction to an existing paid plan's limits takes effect no sooner than 30 days after notice. Increases are immediate.
- Announcements land in the changelog below and, for anything you must act on, in email to the address on your JerryGM account.
Changelog
34 entries
- [RELEASE DATE] —
projectedPPGprices the depth chart. Until now every player was priced as if he started. Tyrod Taylor, Green Bay's QB2, was served at 9.35 points a game and 159 for the season while Jordan Love was healthy.projectedPPGis now what a player is expected to score in his role on his team's live NFL depth chart — his rate if he starts × his share of a starter's game — the same reading the JerryGM app uses. For week 6 Tyrod reads 0.53 (4% of his rate) and 13.3 if he starts; his season fields, which also price the games Love is expected to miss, read 2.7 a game (expectedSeasonTotal23.6).
On a weekly call: - A healthy or Questionable starter reads his full rate. A Questionable starter's chance to sit is in pPlay, and he promotes nobody. - A starter ruled out this week — listed Out or Doubtful, or on a reserve list (IR, PUP, NFI, suspended) — reads 0 for the week, line included. His rate stays in potentialPPG, and his season fields keep his season reading. - The player behind a ruled-out starter (or one off an NFL roster) steps into his spot at the full rate of that spot — one player per spot, never a player ruled out himself — with his chance to start beside it in promotion.pStart: 1 outside a QB room; for a QB, 0.93 when he started his team's last game, 0.70 for a fresh absence, 0.19 when someone else started it. - A new or contested QB1 reads his full starting rate, with his chance to start in roleState.pStart. - Editor-confirmed news is read for the upcoming week or later. A story that the coach named, kept or benched a starter, or has not decided, sets roleState (source: "news") before the chart moves. An injury story the report does not carry yet moves pPlay and the expected-games fields — never projectedPPG or injuryStatus, which stay with the official report. A past week or season reads no news. - Backups carry a measured share of a starter's game: a QB2 behind a starter who is not ruled out 4% (QB2s threw about 4% of their teams' passes in 2023-25), a QB3 1%. Running backs, receivers and tight ends by slot, measured on 2025: RB3 37%, RB4 3%, WR4 75%, TE3 50%, TE4 15%, fifth or lower RB 1%, WR 19%, TE 3%. WR3 and TE2 keep the full rate. - On a practice squad or off an NFL roster: 0 (rosterStatus says which). An off-roster row keeps his last team, and on a weekly call injuryStatus reads Released, Retired or Exempt unless a designation ruled him out. These rows carry no injury read: pPlay null, expectedPPG 0, the expected-games fields null, and expectedSeasonTotal his points to date. A rostered running back, receiver or tight end the live chart does not list is priced behind his room's last man. - Never negative. A player the depth chart does not price — off a roster, on a practice squad, or a rate at or under 0 — reads 0, line included. - No two players share a spot. A tie on the source charts goes to the live chart's player first, then the better projection; gaps stay, so each player ruled out ahead of a man moves him up exactly one spot.
Season and rest-of-season calls keep each player's per-game rate; the games he misses come out through the expected-games fields. A backup's season reading prices, game by game, the games the players ahead of him are expected to miss — a healthy starter misses about 15% of the rest of a season. seasonTotal and expectedSeasonTotal read the season reading on every call, weekly included. Past seasons are unchanged: the raw model rate.
New fields: potentialPPG (the rate if he starts; for a backup QB, per full game of playing time, pulled toward what fill-in starters score league-wide), potentialSeasonTotal (seasonPointsToDate + potentialPPG × his team's games left), depthChart ({ "position": "QB", "rank": 2, "label": "QB2" }), promotion (ahead, pMiss, pStart on every position, and for a QB pStartIfOut), roleState (state, job, why, source, and for a new or contested QB1 pStart), seasonPointsToDate (points actually scored this season under your scoring), expectedSeasonGamesAvailable (games he is healthy and active for, injury alone), expectedSeasonTotal (seasonPointsToDate + the season reading × the available games still ahead), rosterStatus (active / practiceSquad / offRoster), and in ros, expectedGamesAvailable and pointsToDate. New parameter: includeInactive — the all-players list now leaves out practice-squad and off-roster players unless it is true, and inactiveExcluded counts the rows left out; ids= and names= always return who was asked for.
Changed meaning: - projectedPPG is per game in his role, not per game played; statLine and the ros totals scale with it, and seasonTotal and expectedSeasonTotal with the season reading. - pPlay reads injury alone, as for a regular — his role is already in projectedPPG, so a backup is not discounted twice. Tyrod: pPlay 0.97. expectedPPG is projectedPPG × pPlay × his chance to start (promotion.pStart or roleState.pStart; 1 without either). expectedSeasonGamesPlayed keeps its meaning (appearances, from his own history — not starts: about 5 for Tyrod, 15 available). - ros.expectedTotal is ros.pointsToDate + projectedPPG × ros.expectedGamesAvailable. - ros counts only games not played yet. Mid-week, a team that has already played this week has one fewer ros.gamesRemaining (and projectedTotal, statLine totals, window games); that game's actual points ride in ros.pointsToDate. A call for a week no team has played yet is unchanged. - The line between actual and expected is drawn per game: a Thursday game is on the books by Saturday. A traded player's past weeks read the team he was on that week. - Team defenses and individual defenders now carry the season fields and ros.expectedGamesPlayed / expectedGamesAvailable / expectedTotal (were null). - injuryStatus gains Reserve (on IR, PUP, NFI or a suspension list without an Out or Doubtful this week) and, off an NFL roster, Released, Retired or Exempt. A weekly 0 always carries its reason: when the designation that ruled him out is not on the injury report yet, injuryStatus and injuryNote carry it.
- 2026-10-19 — Reserve lists read the NFL's weekly roster. A player on injured reserve, PUP, NFI or a suspension list at his team's last game is out for its next one, and a player placed on IR, PUP or NFI sits out the league's 4-game minimum. Before, those games were certain misses only for players our game-day injury feed listed, which leaves out most defensive depth. A player designated to return from IR is not affected. On 2016-2026, a player on one of these lists at his last game missed the next one 96% of the time.
pPlayand the expected-games fields move for those players only.
- 2026-10-19 — Expected games read his last game. The availability model now knows whether a player played his team's previous game, not just how many of the last four: a healthy part-timer who sat last week sits the next one about two times in three, one who sat an earlier week about one in seven. Held out on 2025 and 2026, the next-game miss probability's log loss improves 10% and games-missed error 4%, offense and defense alike.
expectedGamesPlayed,pPlayand the season fields move for most players;availability.cellDepthcounts one more level.
- 2026-10-19 — Defenders get expected games played.
DL,LBandDBrows now carryros.expectedGamesPlayed,expectedTotalandexpectedSeasonGamesPlayed(werenull, withros.availabilityNOT_APPLICABLE), and an injury-awarepPlay/expectedPPG(were always 1 and the full projection). A defender listedOutorDoubtfulno longer projects a full schedule. Fit on 2016-2025 defenders; held out on 2025 and 2026, aQuestionabledefender is predicted to miss his next game 32% of the time and missed it 32%. Offensive players' reads are unchanged by this. Additive.
- 2026-10-03 — A short board says so. New
dataSeasons(which seasons the blend read) anddegraded(present only when one the API expects was unavailable upstream, with which and a plain-English detail). The season files are fetched fail-soft, so before this a board missing an entire season came back200with ~40% fewer players and every provenance field —statLineCoverageincluded — identical to a healthy one; a reconciliation check passes on it because its numbers are self-consistent. It has happened in production (2026-09-15, the prior season returned 500). A degraded board is now also cached for minutes rather than the usual 12 hours.statLineCoveragekeeps its existing per-field meaning. Additive — if you fail closed, gate ondegradedbeing absent.
- 2026-10-03 — Every fumble, and special-teams fumble events. New weights
fumble(Sleeper'sfum: every fumble, charged on top offumbleLost, as Sleeper does),stFumbleForcedandstFumbleRecovery(st_ff/st_fum_recon kickoffs and punts;def_st_ff/def_st_fum_recunder the same names insidescoring.idp).fumblesis a real per-player projection; the two special-teams events are league-rate expectations likefumbleRecoveryTd— 32 and 61 league-wide in all of 2025. All three ride everystatLinewithcomponentSemanticsentries and coverage verdicts. Additive. Asked by an integrator mapping a full Sleeper rule set.
- 2026-09-21 —
week=current.GET /projections?week=currentresolves to the upcoming week (Tuesday-morning turnover after MNF, or as soon as the week's games are all scored) and echoes the number asweekwithweekAlias: "current". Works withhorizon=ros.GET /sourcesgainsseasonandnflWeek. Asked for by an integrator whose weekly script had to compute the week itself. - 2026-09-17 — Headline always scores the line. Players with a near-zero trailing line (a QB3, a fullback: trailing PPG ≤ 0.5) were being shrunk toward the position mean while their
statLinewas left as is, soprojectedPPG(8) did not reconcile with the scored components (0.3) — 70 of 679 skill players on the week-2 board. Such players are no longer shrunk (a near-zero line is evidence the player is not drawing from the position's pool), and the cofactor source rescales the line once from the baseline instead of after each cofactor, which had compounded rounding on small TD rates. ScoringstatLineunder your weights now reproducesprojectedPPGwithin rounding for every player, every source. Starters are unchanged.ros.availabilityalso gainsroleLabel(plain words forrole). Reported by a beta integrator's contract check.
- 2026-09-17 — Provenance you can parse.
ros.availabilitygains astatus(MODELED/FALLBACK/INSUFFICIENT_DATA/REPORT_UNAVAILABLE/NOT_APPLICABLE) and structured fields —modelVersion,position,ageBand,sampleSize,matchedOn,cellDepth,reportStale,preseasonPrior— beside the humanbasis.GET /sourcesgainsavailabilityModel(the model stated once, with its held-out validation) andcomponentSemantics: for everystatLinefield on every position, its unit and the events it includes and excludes (defTacklesForLoss→disjointWith: ["defSacks"],defBlocks→ FG + PAT + punt,stTds→ kick + punt return TDs, excluding fumble-recovery and defensive scores), mirroring the prose tables. Additive.
- 2026-09-15 — v1.3: individual defensive players.
DL,LBandDBjoin the pool (positions=IDPfor all three; fine spellings fold to their group), each row carryingpositionDetail. Twelve-componentstatLine(tacklesSolo,tacklesAssist,tacklesTotal,tacklesForLoss,sacks,qbHits,interceptions,passesDefended,fumblesForced,fumblesRecovered,defTds,safeties) with the event sets stated in a table —tacklesForLossexcludes sacks, like the team line.scoring.idpweights (GET /sources→idpWeightKeys,idpDefaults,idpGroups); presets score defenders under typical IDP weights. Projected from a snap-share-anchored trailing blend, shrunk toward what a defender with his snap share produces; beats a naive trailing average by 3-5% MAE in every group on held-out 2024 and 2025.statLineCoveragecovers the IDP line./playerslists defenders. - 2026-09-15 —
defTacklesForLossexcludes sacks; coverage for K/DST; event sets documented.statLine.defTacklesForLossis now tackle-for-loss credits on non-sack plays only (from play-by-play), sodefSacksanddefTacklesForLossare disjoint — before this it was nflverse's raw total, which credits a TFL on ~81% of sacks, and a league pricing both would have scored most sacks twice.statLineCoveragenow covers the kicker and defense lines. A table in the K/DST section states what every defensive and special-teams field counts and does not count. - 2026-09-15 — Draft-time prior.
expectedSeasonGamesPlayedis no longernullbefore the season: ahead of week 1's report it is last season's role and production, position and age, andavailability.basissays so. - 2026-09-15 — Expected games played.
ros.expectedGamesPlayedandros.expectedTotal(injury-adjusted, beside the schedule-onlygamesRemaining/projectedTotal, which are unchanged) withros.availabilityexplaining the adjustment;seasonGamesandexpectedSeasonGamesPlayedon every row. Empirical model on 2016-2025 injury reports; beats the schedule by 48% and a designation-only lookup by 29% (MAE, held-out 2025). Fails soft tonull. - 2026-09-14 —
ros.longPlays. Withhorizon=ros, each requested long-play rule also reportsevents(perGame × gamesRemaining) andpoints_total, so explosive plays total over the same games asros.statLine. Docs now state thatgamesRemainingis the schedule, not an availability prediction. - 2026-09-14 — Console: run a URL, build scoring visually. The console (
/api/ext/v1/console) takes a pasted URL and runs it with your stored key (or loads it into the form), has fields forhorizon,regularSeasonWeeks,playoffWeeksandlongPlayRates, a row builder for long-play rates, and a collapsed scoring builder (weights, bonuses, long plays, kicker, defense, tier ladders) that reads its vocabulary fromGET /sourcesand rewrites the JSON as you go. Results show freshness, coverage, league calendar, ROS and long-play columns. - 2026-09-14 — Your league's calendar.
regularSeasonWeeks+playoffWeeksdefine the fantasy season:seasonTotalcounts only the team's games in it, androssplits intoregularSeason/playoffsand names abyeWeekstill ahead. Omitted → the old 17-game behavior. The response echoesleagueWeeks. - 2026-09-13 — Rest of season, freshness, coverage.
horizon=rosadds a per-playerrosblock (games remaining from the schedule, projected total, statLine totals). Every response now carriesgeneratedAt,dataThroughWeekandstatLineCoverage(per component: modeled / partial / unavailable), so a0on the line can be told from a column that does not exist for that season. Additive; response cache key bumped so every board carries its own metadata. - 2026-09-13 —
longPlayRates, on every plan. Report-only explosive-play rates as a query parameter (longPlayRates=40orrec:40,rush:50), beside anyscoringvalue including presets. Expected counts land inbreakdown.longPlayswithpoints: 0;projectedPPGis untouched. A projected count is component data, so it is not custom scoring and is not plan-gated. Asked by the same Yahoo integrator. - 2026-09-12 — Tackles for loss, return-TD split, report-only long plays.
statLine.defTacklesForLoss+defense.tfl;statLine.defReturnTds+defense.retTd(omit it anddefTdcovers every DST touchdown, as before);longPlays[].pointsmay be0to getperGameexpectations without scoring them.fumbleRecoveryTd(offensive fumble-recovery TD) joins the weights so a full rule set never 400s; projected at the league rate. Additive. From the same Yahoo integrator. - 2026-09-11 — Completions, sacks taken, return yards, pick-sixes. Four new linear weights:
passCmp,sackTaken,retYd,pickSix;statLinegainspassCmp(already there),sacks,retYds,pickSix, andbreakdown.pickSixexplains the shrunk rate behind that last one. Sacks and pick-sixes are also valid bonus stats. Additive; a scoring object without them is scored exactly as before. Built for a Yahoo league in beta with 1/completion, −2/sack, −6/pick-six, 1 per 20 return yards. - 2026-09-11 — Defenses match by any name.
names=resolves a team defense from its full franchise name, nickname, city, or any platform's code, with or without aD/ST-style marker —Los Angeles Chargers D/STandLAC D/STare the same row. Ambiguous cities alone stay unmatched. Reported by a Yahoo integrator during beta.positions=acceptsDefenseandPKtoo.
- 2026-09-08 — OpenAPI spec, and the API root no longer lists internal routes.
GET /api/ext/v1/openapi.jsondescribes the public endpoints, generated from the server's own constants. The service root now returns identity, docs links and how to sign up, instead of enumerating every registered route; 404s no longer return the route list either. All those routes were auth-gated, so nothing was readable — but publishing the map had no upside. - 2026-09-08 — Tier bands (
max), and unknown keys are now errors.bonusesandlongPlaysrules take an optionalmaxclosing the band, so a league paying 4 for a 40-49 yard completion and 5 for a 50-59 pays a 55-yarder once.999normalizes to open. Overlapping rules still stack, and the response now carriesscoringWarningswhen they do. Breaking for malformed input only: a rule key we do not recognize is a 400 instead of being dropped — previously a straymaxwas discarded and the tiers scored cumulatively, which is a wrong answer with no error. - 2026-09-08 — Bonuses can be scoped by position.
bonuses[].positions(e.g.["TE"]) prices a rule for those positions only, for leagues that tier the same stat differently. Omitted means everyone, as before.
- 2026-09-08 — v1.2: long plays.
scoring.longPlaysprices a single play by its length —[{type, min, points}]overpassCmp,passTd,rec,recTd,rush,rushTd, at any threshold from 20 yards up, counted from nflverse play-by-play.breakdown.longPlaysexplains each rule.GET /sourcesgainslongPlayTypesandlongPlayMinYards. Additive: a scoring object withoutlongPlaysis scored exactly as before.
- 2026-09-08 — v1.1: kickers and team defenses.
KandDSTrows join the pool (all sources exceptnflverse_naive), projected as the stat line their scoring needs — FG attempts/makes per distance bucket with shrunk make rates; defensive stats plus a points-allowed distribution whose tier score is an expectation over the spread.scoring.kicker/scoring.defenseweight objects;positions=filter;/sourcesgainspositions,kickerDefaults,defenseDefaults. Additive: existing clients see new rows with newpositionvalues; filter withpositions=if you only want skill players. - 2026-09-08 — Points per first down. New weights
passFd,rushFd,recFd;statLinegains the projected per-game first downs under the same names (also usable as bonus stats). Additive; rows without first-down data read 0. - 2026-09-08 — Threshold bonuses.
scoring.bonuses = [{stat, min, points}]— any threshold on anystatLinestat (plusrushRecYds). Scored aspoints × P(stat ≥ min)from each player's game-to-game spread; the per-rule probability and expected points land inbreakdown.bonusesand their sum inbreakdown.expectedBonusPPG, already insideprojectedPPG.GET /sourcesgainsbonusStatsandbonusExample. Additive. Long-play (40+ yard) bonuses are not yet covered. - 2026-09-07 — Free plan per-request cap. A Free request may name at most 25 players across
idsandnames; beyond that the response is a 402 carryingmaxPerRequest,requestedandrequiredPlan. Paid plans unchanged. - 2026-09-07 —
GET /players. New endpoint: the projected pool's identity + 8-way id crosswalk with no projections — every plan, never truncated, one call. Additive. - 2026-09-07 — Free plan all-active cap. A call with no
ids/nameson the Free plan returns the top 100 players byprojectedPPG, withtruncated: true,totalandtruncatedNoteadded to the response. Paid plans unchanged; requests naming players unchanged on every plan. Additive fields; Free-plan clients reading the whole board should move to Starter. - 2026-09-07 — Plans and quotas. Self-serve plans (Free / Starter / Pro). Monthly allowance surfaced as
X-Quota-Limit/X-Quota-Remaining/X-Quota-Reset; 429 for an exhausted allowance carriesresetAtandupgradeUrl. New 402 for parameters outside your plan (source, customscoring, prior seasons), namingrequiredPlan.GET /sourcesechoes yourplanandlimits. All responses gzip-encoded. Additive; no v1 client changes required. - 2026-08-29 — JerryGM release (breaking, pre-policy). Source ids
jt_baseline/jt_cofactorbecamejgm_baseline/jgm_cofactor(ajt_*source returns 400 naming its replacement); keys were re-issued asjgmk_…andjtk_keys return 401 with instructions. This was the last change of its kind — the policy above applies from this date. - 2026-08 — v1 launch.
GET /projections(weekly + full-season, presets and custom weights, name and id matching, injury fields, rookie draft-capital priors, 8-way id crosswalk),GET /sources, this docs page, and the interactive console.