API Documentation
Getting started
The Stats Guy Fantasy API serves player and pick values derived from real dynasty and redraft trades across thousands of Sleeper leagues. Values are recalculated on a regular run cadence (roughly daily), and historical snapshots are archived per run. See our methodology for how values are computed.
All endpoints are served over HTTPS from a single base URL. There is no authentication and no API key. Publicly distributed products that display the data must include a visible credit linking back to Stats Guy Fantasy; see the terms of service.
https://api.statsguyfantasy.com/api/v1
Fetch the top of the superflex dynasty board:
curl "https://api.statsguyfantasy.com/api/v1/rankings?format=sf_dynasty&limit=5"
The API root (GET https://api.statsguyfantasy.com/api/v1) is a discovery endpoint that lists every endpoint and its parameters as JSON. CORS is open, so you can call the API directly from browser apps.
This reference is also available as a single plain-markdown file at /developers/docs.md for use with AI coding agents and LLM tools; a site index for LLMs lives at /llms.txt.
Rate limits & caching
Requests are rate limited per IP address, currently 60 requests per minute. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (unix seconds) headers. When you exceed the limit you get a 429 response with a Retry-After header. Back off until then.
Responses are gzip-compressed and may be served from a cache for up to an hour, so don't depend on sub-hour freshness. Because values only change when a calculation run completes (roughly daily), polling faster than that gains nothing anyway. For bulk consumers, one GET /players sync per day is plenty. If you need more headroom than the per-IP limit, email support@leaguebeat.com.
League formats
Values are calculated independently for four league formats:
| Format | Description |
|---|---|
sf_dynasty | Superflex dynasty (default) |
non_sf_dynasty | Single-QB dynasty |
sf_redraft | Superflex redraft |
non_sf_redraft | Single-QB redraft |
List endpoints are format-scoped: ?format= (or the format body field) picks the lens. Entity endpoints are format-complete: player detail and picks return value (and related fields) as per-format maps, so one call covers every league type.
Player & pick IDs
Player IDs are Sleeper player IDs, returned by every endpoint as id and accepted anywhere a player ID is expected (path parameters, trade-evaluate sides) — pass IDs straight from Sleeper rosters and transactions.
curl "https://api.statsguyfantasy.com/api/v1/players/7564"
Rookie draft picks use structured IDs in three forms:
| Pick ID | Description |
|---|---|
pick:2027:1 | Round-only: a 2027 1st, valued as a generic pick (slot unknown) |
pick:2027:1:early | Early/mid/late variant of a round-only pick |
pick:2026:2.08 | Slot-specific (current draft class): 2026 pick 2.08 |
Player metadata (names, teams, positions, ages) is synced from the Sleeper API; position eligibility (fantasyPositions) follows Sleeper's classification. Values and rankings are Stats Guy Fantasy's own trade-derived data.
Dates & history
Dates are always strict YYYY-MM-DD; other spellings are rejected with 400. Historical lookups resolve to the latest snapshot on or before the requested date, looking back at most 14 days; the snapshot date actually used is echoed back as asOf. Daily snapshots go back to 2025-09-01 for all formats; rankings requests with no snapshot in range return an empty board with an earliestAvailable hint.
The asOf / valuesAsOf fields on current-value responses are the timestamp of the value-calculation run that produced the data (per format), not the request time. Formats update independently: dynasty values recompute year-round, while redraft values only recompute during the fantasy season and freeze at their last computed level through the offseason — so a redraft asOf can trail dynasty by months. That gap is deliberate (there is no meaningful redraft trade market in the offseason), not a data outage.
Errors
Errors are JSON with the shape { "error": "...", "message": "..." }:
| Status | Description |
|---|---|
400 | Bad input: invalid parameters, malformed dates, or malformed JSON bodies |
404 | Not found: unknown player or route |
413 | Request body over 100kb |
429 | Rate limit exceeded; respect the Retry-After header |
500 | Internal server error |
Rankings
/api/v1/rankingsPlayer rankings for a format, current or historical. Use /rankings for display views, slices (position, pagination), and historical boards; use /players when you want the complete current dataset in one call.
| Query parameters | Description |
|---|---|
format | League format (default sf_dynasty) |
position | QB, RB, WR, or TE |
date | YYYY-MM-DD. Historical board (omit for current). Resolves to the latest snapshot on or before this date (14-day lookback); asOf is the snapshot date served. |
limit | 1-1000, default 500 |
offset | Pagination offset |
curl "https://api.statsguyfantasy.com/api/v1/rankings?format=sf_dynasty&position=WR&limit=1"
{
"format": "sf_dynasty",
"asOf": "2026-08-04T13:01:35.221Z",
"total": 147,
"limit": 1,
"offset": 0,
"rankings": [
{
"rank": 3,
"id": "7564",
"name": "Ja'Marr Chase",
"team": "CIN",
"position": "WR",
"positionRank": 1,
"value": 9466,
"age": 26.4,
"yearsExperience": 5
}
]
}
rank and positionRank rank players against other players only, computed from the served values. With ?date=, values (and the ranks computed from them) come from the historical snapshot, but player metadata (name, team, age) reflects the current player record.
All players (bulk sync)
/api/v1/playersAll players with calculated values, as format-complete cards (same shape as GET /players/:id), sorted by sf_dynasty value. This is the bulk sync endpoint: one call gives you a complete Sleeper-ID-keyed values map for local caching.
{
"total": 397,
"valuesAsOf": {
"sf_dynasty": "2026-08-04T13:01:35.221Z",
"non_sf_dynasty": "2026-08-04T13:01:35.221Z",
"sf_redraft": "2026-08-04T13:01:35.221Z",
"non_sf_redraft": "2026-08-04T13:01:35.221Z"
},
"players": [
{
"id": "9509",
"name": "Bijan Robinson",
"team": "ATL",
"position": "RB",
"value": {
"sf_dynasty": 10000,
"non_sf_dynasty": 10000,
"sf_redraft": 7458,
"non_sf_redraft": 9823
}
}
]
}
Use valuesAsOf to decide when to resync: if it hasn't changed, neither have the values.
Player detail
/api/v1/players/:idA player card across all formats: values, ranks, and value changes (7- and 30-day windows) as per-format maps. There is no format parameter; one call covers every league type. The :id path takes the Sleeper player ID.
curl "https://api.statsguyfantasy.com/api/v1/players/7564"
{
"id": "7564",
"name": "Ja'Marr Chase",
"team": "CIN",
"position": "WR",
"fantasyPositions": ["WR"],
"age": 26.4,
"yearsExperience": 5,
"rookieYear": 2021,
"dataUpdatedAt": "2026-08-04T18:00:02.821Z",
"value": {
"sf_dynasty": 9466,
"non_sf_dynasty": 9117,
"sf_redraft": 9794,
"non_sf_redraft": 8680
},
"rank": {
"sf_dynasty": 3,
"non_sf_dynasty": 3,
"sf_redraft": 2,
"non_sf_redraft": 3
},
"positionRank": {
"sf_dynasty": 1,
"non_sf_dynasty": 1,
"sf_redraft": 1,
"non_sf_redraft": 1
},
"valueChange": {
"sf_dynasty": { "days7": -148, "days30": -80 },
"non_sf_dynasty": { "days7": -17, "days30": -626 },
"sf_redraft": { "days7": 543, "days30": 543 },
"non_sf_redraft": { "days7": 35, "days30": 35 }
},
"valuesAsOf": {
"sf_dynasty": "2026-08-04T13:01:35.221Z",
"non_sf_dynasty": "2026-08-04T13:01:35.221Z",
"sf_redraft": "2026-08-04T13:01:35.221Z",
"non_sf_redraft": "2026-08-04T13:01:35.221Z"
}
}
valuesAsOf is when each format's values were computed; dataUpdatedAt only tracks the player's metadata record (name, team, injury status), which syncs daily year-round.
Value history
/api/v1/players/:id/value-historyFull value history for a player in one format, newest first.
| Query parameters | Description |
|---|---|
format | League format (default sf_dynasty) |
window | Max number of history entries (default: all) |
since | YYYY-MM-DD. Only entries on or after this date (for incremental sync) |
curl "https://api.statsguyfantasy.com/api/v1/players/7564/value-history?format=sf_dynasty&window=2"
{
"id": "7564",
"name": "Ja'Marr Chase",
"format": "sf_dynasty",
"history": [
{ "date": "2026-08-04", "value": 9466 },
{ "date": "2026-08-03", "value": 9545 }
]
}
Draft picks
/api/v1/picksRookie draft pick values across all formats. Every year returns early/mid/late variants per round (variant); years with slot-specific data (the current draft class) additionally return one entry per slot (slot), and their variants are computed from the real slot values.
| Query parameters | Description |
|---|---|
year | Filter by draft year (e.g. 2027) |
{
"total": 120,
"valuesAsOf": {
"sf_dynasty": "2026-08-04T13:01:35.221Z",
"non_sf_dynasty": "2026-08-04T13:01:35.221Z",
"sf_redraft": "2026-08-04T13:01:35.221Z",
"non_sf_redraft": "2026-08-04T13:01:35.221Z"
},
"picks": [
{
"id": "pick:2026:2.08",
"year": 2026,
"round": 2,
"slot": 8,
"value": {
"sf_dynasty": 246,
"non_sf_dynasty": 316
}
},
{
"id": "pick:2027:1:early",
"year": 2027,
"round": 1,
"variant": "early",
"value": {
"sf_dynasty": 3762,
"non_sf_dynasty": 3658
}
}
]
}
Pick values exist for dynasty formats only — redraft keys are omitted from value, not returned as 0. (Rookie draft picks are a dynasty asset; redraft trades involving picks are excluded from the value engine.)
Evaluate a trade
/api/v1/trades/evaluateCompare the total value of two trade sides. Sides accept Sleeper player IDs and pick IDs (any pick ID form), with up to 20 assets per side. Response entries are positional (same order as the request arrays) and echo the canonical id.
An optional date (YYYY-MM-DD) evaluates the trade using that date's values, for retro trade grading. Each asset resolves to its latest snapshot on or before the date (14-day lookback); assets with no snapshot in that window come back found: false. Historical entries include asOf, the snapshot date actually used.
Early/mid/late variants price from the round's real slot values once a draft year has slot data (current and historical evaluations alike); for years without slots they apply the variant shape table to the round-only base value. A bare round-only ID prices a generic pick of unknown slot; pass :mid when you just mean a typical pick.
curl -X POST "https://api.statsguyfantasy.com/api/v1/trades/evaluate" \
-H "Content-Type: application/json" \
-d '{
"format": "sf_dynasty",
"sideA": ["7564", "pick:2027:1:early"],
"sideB": ["4984", "pick:2026:2.08"]
}'
{
"format": "sf_dynasty",
"asOf": "2026-08-04T13:01:35.221Z",
"sideA": {
"assets": [
{
"id": "7564",
"type": "player",
"name": "Ja'Marr Chase",
"position": "WR",
"team": "CIN",
"value": 9466,
"found": true
},
{
"id": "pick:2027:1:early",
"type": "pick",
"name": "2027 Round 1 (early)",
"value": 3762,
"found": true
}
],
"totalValue": 13228
},
"sideB": {
"assets": [
{
"id": "4984",
"type": "player",
"name": "Josh Allen",
"position": "QB",
"team": "BUF",
"value": 9246,
"found": true
},
{
"id": "pick:2026:2.08",
"type": "pick",
"name": "2026 Pick 2.08",
"value": 246,
"found": true
}
],
"totalValue": 9492
},
"differential": 3736
}
Unknown IDs come back with found: false and value: 0 (they still count toward the totals as zero) — as do picks in redraft formats, which carry no pick values. The response reports side totals and the differential only.
Evaluate trades in batch
/api/v1/trades/evaluate/batchEvaluate up to 25 trades in one request. A batch counts as a single request against the rate limit. The body is { "trades": [ ... ] } where each entry has the same shape as a single evaluate body (its own format, date, sideA, sideB). Results return positionally.
curl -X POST "https://api.statsguyfantasy.com/api/v1/trades/evaluate/batch" \
-H "Content-Type: application/json" \
-d '{
"trades": [
{
"format": "sf_dynasty",
"date": "2026-03-01",
"sideA": ["7564"],
"sideB": ["4984", "pick:2027:1"]
},
{
"format": "non_sf_dynasty",
"sideA": ["pick:2026:1.04"],
"sideB": ["9509"]
}
]
}'
{
"total": 2,
"results": [
{
"format": "sf_dynasty",
"date": "2026-03-01",
"sideA": { "assets": [ ... ], "totalValue": 9479 },
"sideB": { "assets": [ ... ], "totalValue": 12052 },
"differential": -2573
},
{
"format": "non_sf_dynasty",
"asOf": "2026-08-04T13:01:35.221Z",
"sideA": { "assets": [ ... ], "totalValue": 2501 },
"sideB": { "assets": [ ... ], "totalValue": 10000 },
"differential": -7499
}
]
}
Validation is atomic: any invalid entry fails the whole request with a 400 that includes the offending index. Unresolvable assets within a valid entry still come back per-asset as found: false.
Most-traded players
/api/v1/trades/most-tradedTop 10 most-traded players per league type over a trailing window. Aggregate counts only.
| Query parameters | Description |
|---|---|
days | Trailing window, 1-365 (default 30) |
{
"days": 30,
"mostTraded": {
"dynasty_sf": [
{
"id": "11631",
"name": "Brian Thomas",
"team": "JAX",
"position": "WR",
"tradeCount": 604
}
],
"dynasty_non_sf": [ ... ],
"redraft_sf": [ ... ],
"redraft_non_sf": [ ... ]
}
}
Questions, bug reports, or need higher rate limits? Email support@leaguebeat.com. For update and breaking-change notices, join the update list. To understand what the numbers mean, read the methodology articles. API usage is covered by the terms of service.
