Developers

API Documentation

Reference for the Stats Guy Fantasy dynasty and redraft values API

Getting started

Free and open, no API key required
View as Markdown

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. Every endpoint is open to everyone, including for commercial use. Publicly distributed products that display the data must include a visible credit linking back to Stats Guy Fantasy; see the terms of service.

Base URL
https://api.statsguyfantasy.com/api/v1

Fetch the top of the superflex dynasty board:

Quick start
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:

FormatDescription
sf_dynastySuperflex dynasty (default)
non_sf_dynastySingle-QB dynasty
sf_redraftSuperflex redraft
non_sf_redraftSingle-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.

Fetch a player by Sleeper ID
curl "https://api.statsguyfantasy.com/api/v1/players/7564"

Rookie draft picks use structured IDs in three forms:

Pick IDDescription
pick:2027:1Round-only: a 2027 1st
pick:2027:1:earlyEarly/mid/late variant of a round-only pick
pick:2026:2.08Slot-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.

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.

Errors

Errors are JSON with the shape { "error": "...", "message": "..." }:

StatusDescription
400Bad input: invalid parameters, malformed dates, or malformed JSON bodies
404Not found: unknown player or route
413Request body over 100kb
429Rate limit exceeded; respect the Retry-After header
500Internal server error

Rankings

GET
/api/v1/rankings

Player 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 parametersDescription
formatLeague format (default sf_dynasty)
positionQB, RB, WR, or TE
dateYYYY-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.
limit1-1000, default 500
offsetPagination offset
Request
curl "https://api.statsguyfantasy.com/api/v1/rankings?format=sf_dynasty&position=WR&limit=1"
Response
{
  "format": "sf_dynasty",
  "asOf": "2026-07-24T13:23:13.724Z",
  "total": 214,
  "limit": 1,
  "offset": 0,
  "rankings": [
    {
      "rank": 1,
      "id": "7564",
      "name": "Ja'Marr Chase",
      "team": "CIN",
      "position": "WR",
      "positionRank": 1,
      "value": 10874,
      "age": 26.4,
      "yearsExperience": 5
    }
  ]
}

With ?date=, values and ranks come from the historical snapshot, but player metadata (name, team, age) reflects the current player record.

All players (bulk sync)

GET
/api/v1/players

All 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.

Response (truncated)
{
  "total": 376,
  "valuesAsOf": {
    "sf_dynasty": "2026-07-24T13:23:13.724Z",
    "non_sf_dynasty": "2026-07-24T13:20:41.238Z",
    "sf_redraft": "2026-07-24T13:26:02.881Z",
    "non_sf_redraft": "2026-07-24T13:28:19.454Z"
  },
  "players": [
    {
      "id": "7564",
      "name": "Ja'Marr Chase",
      "team": "CIN",
      "position": "WR",
      "value": {
        "sf_dynasty": 10874,
        "non_sf_dynasty": 10422,
        "sf_redraft": 9100,
        "non_sf_redraft": 8944
      }
    }
  ]
}

Use valuesAsOf to decide when to resync: if it hasn't changed, neither have the values.

Player detail

GET
/api/v1/players/:id

A player card across all formats: values, ranks, and change metrics (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.

Request
curl "https://api.statsguyfantasy.com/api/v1/players/7564"
Response
{
  "id": "7564",
  "name": "Ja'Marr Chase",
  "team": "CIN",
  "position": "WR",
  "fantasyPositions": ["WR"],
  "age": 26.4,
  "yearsExperience": 5,
  "rookieYear": 2021,
  "dataUpdatedAt": "2026-07-24T18:00:03.162Z",
  "value": {
    "sf_dynasty": 10874,
    "non_sf_dynasty": 10422,
    "sf_redraft": 9100,
    "non_sf_redraft": 8944
  },
  "rank": {
    "sf_dynasty": 1,
    "non_sf_dynasty": 1,
    "sf_redraft": 4,
    "non_sf_redraft": 5
  },
  "positionRank": {
    "sf_dynasty": 1,
    "non_sf_dynasty": 1,
    "sf_redraft": 1,
    "non_sf_redraft": 1
  },
  "rankChange": {
    "sf_dynasty": { "days7": 0, "days30": -1 },
    "non_sf_dynasty": { "days7": 0, "days30": 0 },
    "sf_redraft": { "days7": 1, "days30": 2 },
    "non_sf_redraft": { "days7": 0, "days30": 1 }
  },
  "positionRankChange": {
    "sf_dynasty": { "days7": 0, "days30": 0 },
    "non_sf_dynasty": { "days7": 0, "days30": 0 },
    "sf_redraft": { "days7": 0, "days30": 0 },
    "non_sf_redraft": { "days7": 0, "days30": 0 }
  },
  "valueChange": {
    "sf_dynasty": { "days7": 121, "days30": 340 },
    "non_sf_dynasty": { "days7": 98, "days30": 287 },
    "sf_redraft": { "days7": 45, "days30": 122 },
    "non_sf_redraft": { "days7": 51, "days30": 130 }
  }
}

Value history

GET
/api/v1/players/:id/value-history

Full value history for a player in one format, newest first.

Query parametersDescription
formatLeague format (default sf_dynasty)
windowMax number of history entries (default: all)
sinceYYYY-MM-DD. Only entries on or after this date (for incremental sync)
Request
curl "https://api.statsguyfantasy.com/api/v1/players/7564/value-history?format=sf_dynasty&window=2"
Response
{
  "id": "7564",
  "name": "Ja'Marr Chase",
  "format": "sf_dynasty",
  "history": [
    { "date": "2026-07-24", "value": 10874, "rank": 1, "positionRank": 1 },
    { "date": "2026-07-23", "value": 10851, "rank": 1, "positionRank": 1 }
  ]
}

Draft picks

GET
/api/v1/picks

Rookie draft pick values across all formats. Years with slot-specific data (the current draft class) return one entry per slot (slot); future years return early/mid/late variants per round (variant).

Query parametersDescription
yearFilter by draft year (e.g. 2027)
Response (truncated)
{
  "total": 63,
  "picks": [
    {
      "id": "pick:2026:2.08",
      "year": 2026,
      "round": 2,
      "slot": 8,
      "value": {
        "sf_dynasty": 2100,
        "non_sf_dynasty": 1900,
        "sf_redraft": 0,
        "non_sf_redraft": 0
      }
    },
    {
      "id": "pick:2027:1:early",
      "year": 2027,
      "round": 1,
      "variant": "early",
      "value": {
        "sf_dynasty": 5400,
        "non_sf_dynasty": 4800,
        "sf_redraft": 0,
        "non_sf_redraft": 0
      }
    }
  ]
}

Evaluate a trade

POST
/api/v1/trades/evaluate

Compare 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. Note: early/mid/ late pick variants apply the current variant shape table to the historical base value, an approximation for past dates.

Request
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"]
  }'
Response
{
  "format": "sf_dynasty",
  "sideA": {
    "assets": [
      {
        "id": "7564",
        "type": "player",
        "name": "Ja'Marr Chase",
        "position": "WR",
        "team": "CIN",
        "value": 10874,
        "found": true
      },
      {
        "id": "pick:2027:1:early",
        "type": "pick",
        "name": "2027 Round 1 (early)",
        "value": 5400,
        "found": true
      }
    ],
    "totalValue": 16274
  },
  "sideB": {
    "assets": [
      {
        "id": "4984",
        "type": "player",
        "name": "Josh Allen",
        "position": "QB",
        "team": "BUF",
        "value": 9950,
        "found": true
      },
      {
        "id": "pick:2026:2.08",
        "type": "pick",
        "name": "2026 Pick 2.08",
        "value": 2100,
        "found": true
      }
    ],
    "totalValue": 12050
  },
  "differential": 4224
}

Unknown IDs come back with found: false and value: 0 (they still count toward the totals as zero). The response reports side totals and the differential only.

Evaluate trades in batch

POST
/api/v1/trades/evaluate/batch

Evaluate 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.

Request
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"]
      }
    ]
  }'
Response (truncated)
{
  "total": 2,
  "results": [
    {
      "format": "sf_dynasty",
      "date": "2026-03-01",
      "sideA": { "assets": [ ... ], "totalValue": 10534 },
      "sideB": { "assets": [ ... ], "totalValue": 13890 },
      "differential": -3356
    },
    {
      "format": "non_sf_dynasty",
      "sideA": { "assets": [ ... ], "totalValue": 3450 },
      "sideB": { "assets": [ ... ], "totalValue": 3120 },
      "differential": 330
    }
  ]
}

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

GET
/api/v1/trades/most-traded

Top 10 most-traded players per league type over a trailing window. Aggregate counts only.

Query parametersDescription
daysTrailing window, 1-365 (default 30)
Response (truncated)
{
  "days": 30,
  "mostTraded": {
    "dynasty_sf": [
      {
        "id": "7564",
        "name": "Ja'Marr Chase",
        "team": "CIN",
        "position": "WR",
        "tradeCount": 412
      }
    ],
    "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.