Bottom line: Sleeper exposes more league data more cleanly via a public API. ESPN retains more historical depth (back to the 2000s for some leagues) but requires cookie-based auth for private leagues and has split modern/legacy API surfaces. Both work well for archiving once you know the quirks. Below is the platform-by-platform breakdown.
Historical depth — how far back can you go?
Sleeper: Founded in 2017. Most leagues go back to 2018–2019. Every season is reachable via previous_league_id chain. No fall-off.
ESPN: Has run fantasy football since 2003. Modern API covers 2018+ via lm-api-reads.fantasy.espn.com; pre-2018 seasons live in a legacy leagueHistory archive at a different endpoint. Both are accessible, but the legacy API has gaps (some seasons missing playoffTierType, some missing final rank assignments).
Verdict: ESPN wins on depth (15+ year leagues are possible). Sleeper wins on consistency (every season is structured the same way).
API surface — what data is exposed
Sleeper: Public, unauthenticated. League settings, users, rosters, matchups (by week), playoffs bracket, drafts, draft picks, transactions. Returns clean JSON. Zero rate limit issues for our use case.
ESPN: Similar coverage — settings, members, schedule, scoring, drafts, transactions. Modern endpoint returns rich data including positional matchups and player IDs. Legacy endpoint returns less detail and occasionally misidentifies consolation games as championship-bracket.
Verdict:Sleeper is friendlier for tooling. ESPN's data is comparable in detail but requires more handling.
Authentication — public vs private leagues
Sleeper: Public by default. League ID is enough to read any data. No auth required for archiving.
ESPN: Public leagues work with just the league ID. Private leagues require two browser cookies (SWID and espn_s2) that you grab from a logged-in ESPN tab. The cookies expire every few months and need refreshing.
Verdict:Sleeper is dramatically simpler. ESPN's cookie auth is workable but adds friction.
Live-season sync
Sleeper:Matchup data updates within minutes of games completing. Standings refresh during the week. Sleeper's API is fast and rarely rate-limits.
ESPN: Similar speed. Modern API is reliable. Private-league cookies occasionally rate-limit if you sync too aggressively, but a weekly cron is fine.
Verdict: Tied for practical purposes.
Drafts
Sleeper: Draft picks come with round, pick number, roster ID, player ID, and timestamp. Player names need a separate API call but Sleeper exposes that too.
ESPN: Drafts available via the same league endpoint with full pick history. Player names resolved via kona_player_info batch lookup.
Verdict: Both fully exposed. Sleeper is easier to parse; ESPN is more verbose but equally complete.
Playoff bracket detection
Sleeper: Winners bracket exposed as a separate endpoint with placement (p) values that distinguish championship-bracket games from consolation. Clean.
ESPN: Modern API tags games with playoffTierType (WINNERS_BRACKET, LOSERS_CONSOLATION_LADDER, etc). Older seasons sometimes miss this tag, requiring a seed-based fallback to identify real playoff games vs placement ladders.
Verdict:Sleeper's is cleaner. ESPN's requires more parsing logic but works once handled.
If your league has lived on both
Many long-running leagues started on ESPN (2008–2015) and migrated to Sleeper (2017–present). You don't have to pick one — see our migration guide. The Sunday Chronicle supports multiple sources per league archive, so an ESPN history + Sleeper present can live under one almanac.
Both work. Which should you archive first?
If your league has a longer ESPN tail, start there — the legacy archive is the harder data to recover later. Sleeper is well-documented and stable; you can always add it as a second source. The Sunday Chronicle handles either as the primary or both together.