sleeper_api 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/CLAUDE.md +3 -1
- data/README.md +108 -90
- data/lib/sleeper_api/client.rb +109 -19
- data/lib/sleeper_api/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6fd4df8d68adb46808ea692bbf8c715f2218a07a8f93212f5efddfe404741a30
|
|
4
|
+
data.tar.gz: cd3b09acb61407c634f04d76f165a1d2e0b7b840b3564117136ecc9343bc286c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a63777ce6255da38e963034bf9b016ca327d85dcaefc723ad508c85d58c9674939474ee00bd55b33b007e3268a043e81f8c175f06276cb77ae80b6a619871431
|
|
7
|
+
data.tar.gz: aed8fa84f731ddb1e8a01ef2cfb8987305826f665f338954e990fcb8c2c946c3d33f73edf510217ecee1923412bc6470a56931dbd9c60ba7369230c2c847627c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
## [1.3.0] - 2026-08-27
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- `Client#stats(season, week:, season_type:, sport:)` and `Client#projections(...)` — per-player weekly statistics and projections, both undocumented, both under `/v1`. Found by probing on 2026-08-27; the consuming app's backlog had recorded "Sleeper has no projections endpoint and no player-stats endpoint" as settled fact, and several of its cards were blocked on that.
|
|
6
|
+
|
|
7
|
+
Each returns an object keyed by player id — plus `TEAM_XXX` keys for team-level rows — holding raw counting stats (`rec`, `rush_yd`, `off_snp`, `rec_rz_tgt`, …) alongside Sleeper's canned `pts_ppr` / `pts_half_ppr` / `pts_std`. 228 distinct fields were observed across one week of 2025. Omitting `week` requests season totals, which is a shorter path and a different resource rather than a default of week 1.
|
|
8
|
+
|
|
9
|
+
Three things a caller has to know, all confirmed live:
|
|
10
|
+
|
|
11
|
+
- **Nothing 404s.** An unplayed week (`regular/2026/1`), a week out of range (`regular/2025/99`) and an unrecognised season type (`banana`) all answer 200 with `{}`. An empty body is indistinguishable from a typo, so validate arguments rather than trusting emptiness.
|
|
12
|
+
- **A row count is not evidence of a projection.** `projections` for a season Sleeper has not projected still returns a full set of entries — 9,386 for 2030 — every one holding only `{"adp_dd_ppr" => 1000.0}` and no `pts_ppr`. Filter on the field you want.
|
|
13
|
+
- **`pre` and `post` restart week numbering at 1**, exactly as `#schedule` does, so rows from different season types must never be pooled.
|
|
14
|
+
|
|
15
|
+
`adp_dd_ppr` 1000.0 and `pos_rank_*` 999.0 are "unknown" sentinels rather than values.
|
|
16
|
+
|
|
17
|
+
Path segments are escaped, unlike the older `#get_user` — an unescaped segment can walk out of the endpoint entirely, which the consuming app had to work around at its own boundary.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- README.md contained 272 non-breaking spaces across 90 lines, 58 of them **inside ````ruby` code fences** — so the documented examples raised a syntax error when copy-pasted. `lib/` and `spec/` were unaffected; only the documentation was broken. Replaced with ordinary spaces.
|
|
22
|
+
|
|
23
|
+
## [1.2.0] - 2026-08-23
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `Client#schedule(season, season_type:, sport:)` — a season's game list, each entry `{status, date, home, away, week, game_id}`. A team's bye week is the week it appears in no game, which derives exactly: checked against 2024, 2025 and 2026, every one gives exactly one bye per team. Note `pre` (weeks 1-3) and `post` (weeks 1-4) restart week numbering, so games from different season types must never be pooled, and a season Sleeper has not scheduled yet answers 200 with `[]` rather than 404.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **`base_uri` is now the bare host, `https://api.sleeper.app`, and every path carries its own `/v1`.** The schedule endpoint is served from the host root, so while the version lived in `base_uri` it was unreachable at any path a caller could pass to `make_request`. Moving the version into the paths means one mechanism for every endpoint rather than an escape hatch for the exceptions.
|
|
32
|
+
|
|
33
|
+
**This changes the text of `SleeperApi::Error`**, which quotes the path it failed on: `"Failed to fetch /user/x: 404"` is now `"Failed to fetch /v1/user/x: 404"`. Anything matching on that string needs updating — though matching on it was never sound, since the same error covers a missing user, a 5xx and a timeout alike.
|
|
34
|
+
|
|
1
35
|
## [1.1.0] - 2026-08-22
|
|
2
36
|
|
|
3
37
|
### Fixed
|
data/CLAUDE.md
CHANGED
|
@@ -26,7 +26,9 @@ CI (`.github/workflows/ci.yml`) runs `bundle exec rake ci` on Ruby 3.2 only. The
|
|
|
26
26
|
Four layers, with a deliberate split between HTTP and modeling:
|
|
27
27
|
|
|
28
28
|
- **`SleeperApi`** (`lib/sleeper_api.rb`) — module-level config + memoized global `SleeperApi.client`. `Configuration` validates `timeout` (10–60) and `retries` (0–5), raising `SleeperApi::Error` outside those bounds.
|
|
29
|
-
- **`Client`** — the only thing that talks HTTP. `include HTTParty` with `base_uri "https://api.sleeper.app
|
|
29
|
+
- **`Client`** — the only thing that talks HTTP. `include HTTParty` with `base_uri "https://api.sleeper.app"` — **the bare host; the `/v1` lives in each path**. That is deliberate and load-bearing: not every Sleeper endpoint is versioned. `/schedule/{sport}/{season_type}/{season}` is served from the host root, and while `base_uri` carried the version that endpoint was unreachable at any path a caller could pass in. A new endpoint spells out where it lives; do not move the version back into `base_uri` to shorten the paths.
|
|
30
|
+
|
|
31
|
+
Every call funnels through the private `make_request`, which handles retry-on-timeout, logging, and converts non-2xx into `SleeperApi::Error`. **That error quotes the path**, so the path prefix is part of a public string — v1.2.0 changed it from `"Failed to fetch /user/x: 404"` to `"Failed to fetch /v1/user/x: 404"`. `Client` also owns the 24-hour in-memory player cache.
|
|
30
32
|
- **`League` / `User` / `Draft`** — resource objects. Each takes `(id, client)`, fetches eagerly in the constructor, memoizes into ivars, and exposes formatted hashes.
|
|
31
33
|
- **`Helpers`** — mixed into all four. `deep_symbolize_keys` plus `player_details`, which reaches through `@client` — so any class including it must define `@client`.
|
|
32
34
|
|
data/README.md
CHANGED
|
@@ -2,71 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/rb/sleeper_api)
|
|
4
4
|
|
|
5
|
-
A comprehensive Ruby gem
|
|
5
|
+
A comprehensive Ruby gem for interacting with [Sleeper's fantasy football API](https://docs.sleeper.com/). Built with performance, reliability, and developer experience in mind.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- Complete API Coverage
|
|
9
|
+
- Complete API Coverage - Users, leagues, drafts, players, matchups, transactions
|
|
10
10
|
|
|
11
|
-
- Performance Optimized
|
|
11
|
+
- Performance Optimized - Smart caching, connection pooling, rate limiting
|
|
12
12
|
|
|
13
|
-
- Robust Error Handling
|
|
13
|
+
- Robust Error Handling - Automatic retries, timeout management, detailed error messages
|
|
14
14
|
|
|
15
|
-
- Well Tested
|
|
15
|
+
- Well Tested - 90%+ test coverage with RSpec
|
|
16
16
|
|
|
17
|
-
- Highly Configurable
|
|
17
|
+
- Highly Configurable - Custom timeouts, retries, logging
|
|
18
18
|
|
|
19
|
-
- Production
|
|
19
|
+
- Production Ready - Type signatures, CI/CD, code quality tools
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
23
|
Add this line to your application's Gemfile:
|
|
24
24
|
|
|
25
25
|
```ruby
|
|
26
|
-
gem
|
|
26
|
+
gem 'sleeper_api'
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
## Quick Start
|
|
30
30
|
|
|
31
31
|
```ruby
|
|
32
|
-
require
|
|
32
|
+
require 'sleeper_api'
|
|
33
33
|
|
|
34
|
-
#
|
|
35
|
-
client
|
|
36
|
-
league
|
|
34
|
+
# Basic usage with default configuration
|
|
35
|
+
client = SleeperApi.client
|
|
36
|
+
league = client.league("123456789012345678")
|
|
37
37
|
|
|
38
|
-
#
|
|
39
|
-
puts
|
|
40
|
-
puts
|
|
41
|
-
puts
|
|
38
|
+
# Access league information
|
|
39
|
+
puts league.name # "My Fantasy League"
|
|
40
|
+
puts league.total_rosters # 12
|
|
41
|
+
puts league.status # "in_season"
|
|
42
42
|
|
|
43
|
-
#
|
|
44
|
-
rosters
|
|
45
|
-
rosters.each
|
|
46
|
-
|
|
43
|
+
# Get rosters
|
|
44
|
+
rosters = league.rosters
|
|
45
|
+
rosters.each do |roster|
|
|
46
|
+
puts "#{roster[:owner_display_name]}: #{roster[:wins]}-#{roster[:losses]}"
|
|
47
47
|
end
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
## Configuration
|
|
51
51
|
|
|
52
|
-
Customize the gem's
|
|
52
|
+
Customize the gem's behavior:
|
|
53
53
|
|
|
54
54
|
```ruby
|
|
55
|
-
SleeperApi.configure
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
SleeperApi.configure do |config|
|
|
56
|
+
config.timeout = 45 # Request timeout in seconds (10-60)
|
|
57
|
+
config.retries = 5 # Number of retries on failure (0-5)
|
|
58
|
+
config.logger = Logger.new(STDOUT) # Custom logger
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
#
|
|
62
|
-
client
|
|
61
|
+
# Configuration is applied to all subsequent client instances
|
|
62
|
+
client = SleeperApi.client
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
## API Coverage
|
|
66
66
|
|
|
67
67
|
### Users
|
|
68
68
|
|
|
69
|
-
Get user information and
|
|
69
|
+
Get user information and their leagues/drafts:
|
|
70
70
|
|
|
71
71
|
```ruby
|
|
72
72
|
# Find a user by username
|
|
@@ -118,7 +118,7 @@ puts "Overall: #{summary[:total_wins]}-#{summary[:total_losses]}"
|
|
|
118
118
|
|
|
119
119
|
### Leagues
|
|
120
120
|
|
|
121
|
-
Access league
|
|
121
|
+
Access league data, rosters, matchups, and transactions:
|
|
122
122
|
|
|
123
123
|
```ruby
|
|
124
124
|
league = SleeperApi.client.league("123456")
|
|
@@ -180,7 +180,7 @@ matchup[:winner_owner] # => "Team Beta"
|
|
|
180
180
|
|
|
181
181
|
### Drafts
|
|
182
182
|
|
|
183
|
-
Access draft information, picks, and
|
|
183
|
+
Access draft information, picks, and traded picks:
|
|
184
184
|
|
|
185
185
|
```ruby
|
|
186
186
|
# Get a specific draft
|
|
@@ -237,19 +237,19 @@ team_3_rounds = draft.team_picks[3] # Team 3's picks by round
|
|
|
237
237
|
|
|
238
238
|
### Players
|
|
239
239
|
|
|
240
|
-
Access player data with automatic
|
|
240
|
+
Access player data with automatic caching:
|
|
241
241
|
|
|
242
242
|
```ruby
|
|
243
|
-
#
|
|
244
|
-
players
|
|
243
|
+
# Get all players (cached for 24 hours)
|
|
244
|
+
players = client.get_players
|
|
245
245
|
|
|
246
|
-
#
|
|
247
|
-
player
|
|
248
|
-
puts
|
|
246
|
+
# Find specific player
|
|
247
|
+
player = client.get_player_by_id("1234")
|
|
248
|
+
puts "#{player['first_name']} #{player['last_name']} - #{player['position']}"
|
|
249
249
|
|
|
250
|
-
#
|
|
251
|
-
trending_adds
|
|
252
|
-
trending_drops
|
|
250
|
+
# Get trending players
|
|
251
|
+
trending_adds = client.trending_players(type: "add", limit: 10)
|
|
252
|
+
trending_drops = client.trending_players(type: "drop", limit: 10)
|
|
253
253
|
```
|
|
254
254
|
|
|
255
255
|
### Player Helper
|
|
@@ -313,103 +313,121 @@ rosters.each do |roster|
|
|
|
313
313
|
end
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
-
### Additional
|
|
316
|
+
### Additional Endpoints
|
|
317
317
|
|
|
318
318
|
```ruby
|
|
319
|
-
#
|
|
320
|
-
state
|
|
321
|
-
puts
|
|
319
|
+
# Get NFL state
|
|
320
|
+
state = client.get_nfl_state
|
|
321
|
+
puts "Current week: #{state['week']}"
|
|
322
|
+
|
|
323
|
+
# Per-player weekly stats and projections (undocumented endpoints).
|
|
324
|
+
# Keyed by player id, plus TEAM_XXX keys for team-level rows.
|
|
325
|
+
week_stats = client.stats(2025, week: 1)
|
|
326
|
+
puts week_stats["4046"]["pts_ppr"] # => 21.4
|
|
327
|
+
puts week_stats["4046"]["off_snp"] # raw counting stats too
|
|
328
|
+
|
|
329
|
+
projected = client.projections(2026, week: 1)
|
|
330
|
+
|
|
331
|
+
# Omit the week for season totals — a different resource, not a default.
|
|
332
|
+
season_stats = client.stats(2025)
|
|
333
|
+
|
|
334
|
+
# Nothing here 404s: an unplayed week, a week out of range and an unknown
|
|
335
|
+
# season type all answer 200 with {}. And a projections call for a season
|
|
336
|
+
# Sleeper has not projected still returns thousands of entries carrying only
|
|
337
|
+
# an `adp_dd_ppr` sentinel — a row count is not evidence of a projection, so
|
|
338
|
+
# filter on the field you actually want.
|
|
339
|
+
real = projected.parsed_response.select { |_id, row| row.key?("pts_ppr") }
|
|
322
340
|
|
|
323
|
-
#
|
|
324
|
-
winners_bracket
|
|
325
|
-
losers_bracket
|
|
341
|
+
# Get playoff brackets
|
|
342
|
+
winners_bracket = client.get_league_playoff_bracket("league_id")
|
|
343
|
+
losers_bracket = client.get_league_toilet_bowl("league_id")
|
|
326
344
|
|
|
327
|
-
#
|
|
328
|
-
league_drafts
|
|
345
|
+
# Get league drafts
|
|
346
|
+
league_drafts = client.get_league_drafts("league_id")
|
|
329
347
|
|
|
330
|
-
#
|
|
331
|
-
traded_picks
|
|
348
|
+
# Get traded picks
|
|
349
|
+
traded_picks = client.get_league_traded_picks("league_id")
|
|
332
350
|
```
|
|
333
351
|
|
|
334
|
-
## Error
|
|
352
|
+
## Error Handling
|
|
335
353
|
|
|
336
354
|
The gem provides comprehensive error handling:
|
|
337
355
|
|
|
338
356
|
```ruby
|
|
339
357
|
begin
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
rescue
|
|
343
|
-
|
|
344
|
-
rescue
|
|
345
|
-
|
|
358
|
+
league = client.league("invalid_id")
|
|
359
|
+
# Process league data
|
|
360
|
+
rescue SleeperApi::Error => e
|
|
361
|
+
puts "API Error: #{e.message}"
|
|
362
|
+
rescue ArgumentError => e
|
|
363
|
+
puts "Invalid parameter: #{e.message}"
|
|
346
364
|
end
|
|
347
365
|
```
|
|
348
366
|
|
|
349
367
|
### Error Types
|
|
350
368
|
|
|
351
|
-
- SleeperApi::Error
|
|
369
|
+
- SleeperApi::Error - API-related errors (404, 500, timeouts, etc.)
|
|
352
370
|
|
|
353
|
-
- ArgumentError
|
|
371
|
+
- ArgumentError - Invalid parameters passed to methods
|
|
354
372
|
|
|
355
373
|
## Performance Considerations
|
|
356
374
|
|
|
357
375
|
### Caching
|
|
358
376
|
|
|
359
|
-
- Player data
|
|
377
|
+
- Player data is cached for 24 hours to reduce API calls
|
|
360
378
|
|
|
361
|
-
- League/User/Draft data
|
|
379
|
+
- League/User/Draft data is cached per instance
|
|
362
380
|
|
|
363
|
-
- Cache files are stored
|
|
381
|
+
- Cache files are stored in the current working directory
|
|
364
382
|
|
|
365
|
-
### Rate
|
|
383
|
+
### Rate Limiting
|
|
366
384
|
|
|
367
|
-
- Be mindful of Sleeper's rate limits: stay under
|
|
385
|
+
- Be mindful of Sleeper's rate limits: stay under 1000 API calls per minute
|
|
368
386
|
|
|
369
|
-
- The
|
|
387
|
+
- The gem automatically handles timeouts and retries
|
|
370
388
|
|
|
371
389
|
- Consider caching frequently accessed data in your application
|
|
372
390
|
|
|
373
391
|
### Memory Usage
|
|
374
392
|
|
|
375
|
-
- Large datasets
|
|
393
|
+
- Large datasets (like all players) are cached to disk
|
|
376
394
|
|
|
377
|
-
- League rosters and matchups
|
|
395
|
+
- League rosters and matchups are fetched lazily
|
|
378
396
|
|
|
379
|
-
- Use
|
|
397
|
+
- Use no_data: true when initializing leagues if you don't need immediate data
|
|
380
398
|
|
|
381
399
|
## Testing
|
|
382
400
|
|
|
383
401
|
The gem includes comprehensive tests:
|
|
384
402
|
|
|
385
403
|
```shellscript
|
|
386
|
-
#
|
|
387
|
-
bundle
|
|
388
|
-
#
|
|
389
|
-
bundle
|
|
390
|
-
#
|
|
391
|
-
bundle
|
|
404
|
+
# Run all tests
|
|
405
|
+
bundle exec rspec
|
|
406
|
+
# Run with coverage
|
|
407
|
+
bundle exec rspec --coverage
|
|
408
|
+
# Run specific test file
|
|
409
|
+
bundle exec rspec spec/sleeper_api/client_spec.rb
|
|
392
410
|
```
|
|
393
411
|
|
|
394
412
|
### Setup
|
|
395
413
|
|
|
396
414
|
```shellscript
|
|
397
|
-
git
|
|
398
|
-
cd
|
|
399
|
-
bundle
|
|
415
|
+
git clone https://github.com/eruity1/sleeper_api.git
|
|
416
|
+
cd sleeper_api
|
|
417
|
+
bundle install
|
|
400
418
|
```
|
|
401
419
|
|
|
402
420
|
### Code Quality
|
|
403
421
|
|
|
404
422
|
```shellscript
|
|
405
|
-
#
|
|
406
|
-
bundle
|
|
423
|
+
# Run all checks (tests + linting)
|
|
424
|
+
bundle exec rake ci
|
|
407
425
|
|
|
408
|
-
#
|
|
409
|
-
bundle
|
|
426
|
+
# Run RuboCop
|
|
427
|
+
bundle exec rubocop
|
|
410
428
|
|
|
411
|
-
#
|
|
412
|
-
bundle
|
|
429
|
+
# Auto-fix RuboCop issues
|
|
430
|
+
bundle exec rubocop -a
|
|
413
431
|
```
|
|
414
432
|
|
|
415
433
|
### Contributing
|
|
@@ -426,24 +444,24 @@ bundle exec rubocop -a
|
|
|
426
444
|
|
|
427
445
|
### Development Dependencies
|
|
428
446
|
|
|
429
|
-
- rspec
|
|
447
|
+
- rspec - Testing framework
|
|
430
448
|
|
|
431
|
-
- rubocop
|
|
449
|
+
- rubocop - Code style and quality
|
|
432
450
|
|
|
433
|
-
- simplecov
|
|
451
|
+
- simplecov - Test coverage
|
|
434
452
|
|
|
435
|
-
- webmock
|
|
453
|
+
- webmock - HTTP request mocking
|
|
436
454
|
|
|
437
455
|
## Requirements
|
|
438
456
|
|
|
439
457
|
- Ruby 2.6.0 or higher
|
|
440
458
|
|
|
441
|
-
- No external dependencies
|
|
459
|
+
- No external dependencies (HTTParty is bundled)
|
|
442
460
|
|
|
443
461
|
## License
|
|
444
462
|
|
|
445
|
-
The gem is available as open source
|
|
463
|
+
The gem is available as open source under the terms of the MIT License.
|
|
446
464
|
|
|
447
465
|
## Changelog
|
|
448
466
|
|
|
449
|
-
See
|
|
467
|
+
See CHANGELOG.md for version history and updates.
|
data/lib/sleeper_api/client.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "httparty"
|
|
2
2
|
require "json"
|
|
3
|
+
require "erb"
|
|
3
4
|
|
|
4
5
|
module SleeperApi
|
|
5
6
|
# HTTP client for Sleeper API requests.
|
|
@@ -15,7 +16,11 @@ module SleeperApi
|
|
|
15
16
|
include Helpers
|
|
16
17
|
include HTTParty
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
# The host only. The API version belongs in the paths because not every
|
|
20
|
+
# endpoint has one: /schedule is served from the root, and while base_uri
|
|
21
|
+
# carried "/v1" that endpoint was unreachable at any path a caller could
|
|
22
|
+
# pass to make_request.
|
|
23
|
+
base_uri "https://api.sleeper.app"
|
|
19
24
|
|
|
20
25
|
# @param config [SleeperApi::Configuration] Client configuration
|
|
21
26
|
def initialize(config)
|
|
@@ -57,7 +62,7 @@ module SleeperApi
|
|
|
57
62
|
# @return [Hash] Raw user data
|
|
58
63
|
# @see https://docs.sleeper.com/#user
|
|
59
64
|
def get_user(identifier)
|
|
60
|
-
make_request("/user/#{identifier}")
|
|
65
|
+
make_request("/v1/user/#{identifier}")
|
|
61
66
|
end
|
|
62
67
|
|
|
63
68
|
# Get leagues for a user in a specific season.
|
|
@@ -68,7 +73,7 @@ module SleeperApi
|
|
|
68
73
|
# @return [Array<Hash>] League data
|
|
69
74
|
# @see https://docs.sleeper.com/#get-all-leagues-for-user
|
|
70
75
|
def get_user_leagues(user_id, sport: "nfl", season: Time.now.year)
|
|
71
|
-
make_request("/user/#{user_id}/leagues/#{sport}/#{season}")
|
|
76
|
+
make_request("/v1/user/#{user_id}/leagues/#{sport}/#{season}")
|
|
72
77
|
end
|
|
73
78
|
|
|
74
79
|
# Get drafts for a user in a specific season.
|
|
@@ -79,7 +84,7 @@ module SleeperApi
|
|
|
79
84
|
# @return [Array<Hash>] Draft data
|
|
80
85
|
# @see https://docs.sleeper.com/#get-all-drafts-for-user
|
|
81
86
|
def get_user_drafts(user_id, sport: "nfl", season: Time.now.year)
|
|
82
|
-
make_request("/user/#{user_id}/drafts/#{sport}/#{season}")
|
|
87
|
+
make_request("/v1/user/#{user_id}/drafts/#{sport}/#{season}")
|
|
83
88
|
end
|
|
84
89
|
|
|
85
90
|
# Fetch league details.
|
|
@@ -88,7 +93,7 @@ module SleeperApi
|
|
|
88
93
|
# @return [Hash] League metadata
|
|
89
94
|
# @see https://docs.sleeper.com/#get-a-specific-league
|
|
90
95
|
def get_league(league_id)
|
|
91
|
-
make_request("/league/#{league_id}")
|
|
96
|
+
make_request("/v1/league/#{league_id}")
|
|
92
97
|
end
|
|
93
98
|
|
|
94
99
|
# Get all rosters in a league.
|
|
@@ -97,7 +102,7 @@ module SleeperApi
|
|
|
97
102
|
# @return [Array<Hash>] Roster data
|
|
98
103
|
# @see https://docs.sleeper.com/#getting-rosters-in-a-league
|
|
99
104
|
def get_league_rosters(league_id)
|
|
100
|
-
make_request("/league/#{league_id}/rosters")
|
|
105
|
+
make_request("/v1/league/#{league_id}/rosters")
|
|
101
106
|
end
|
|
102
107
|
|
|
103
108
|
# Get all users in a league.
|
|
@@ -106,7 +111,7 @@ module SleeperApi
|
|
|
106
111
|
# @return [Array<Hash>] User data
|
|
107
112
|
# @see https://docs.sleeper.com/#getting-users-in-a-league
|
|
108
113
|
def get_league_users(league_id)
|
|
109
|
-
make_request("/league/#{league_id}/users")
|
|
114
|
+
make_request("/v1/league/#{league_id}/users")
|
|
110
115
|
end
|
|
111
116
|
|
|
112
117
|
# Get matchups for a specific week.
|
|
@@ -116,7 +121,7 @@ module SleeperApi
|
|
|
116
121
|
# @return [Array<Hash>] Matchup data
|
|
117
122
|
# @see https://docs.sleeper.com/#getting-matchups-in-a-league
|
|
118
123
|
def get_league_matchups(league_id, week)
|
|
119
|
-
make_request("/league/#{league_id}/matchups/#{week}")
|
|
124
|
+
make_request("/v1/league/#{league_id}/matchups/#{week}")
|
|
120
125
|
end
|
|
121
126
|
|
|
122
127
|
# Get playoff winners bracket.
|
|
@@ -125,7 +130,7 @@ module SleeperApi
|
|
|
125
130
|
# @return [Array<Hash>] Bracket matchups
|
|
126
131
|
# @see https://docs.sleeper.com/#getting-the-playoff-bracket
|
|
127
132
|
def get_playoff_bracket(league_id)
|
|
128
|
-
make_request("/league/#{league_id}/winners_bracket")
|
|
133
|
+
make_request("/v1/league/#{league_id}/winners_bracket")
|
|
129
134
|
end
|
|
130
135
|
|
|
131
136
|
# Get toilet bowl (losers bracket).
|
|
@@ -134,7 +139,7 @@ module SleeperApi
|
|
|
134
139
|
# @return [Array<Hash>] Bracket matchups
|
|
135
140
|
# @see https://docs.sleeper.com/#getting-the-playoff-bracket
|
|
136
141
|
def get_toilet_bowl(league_id)
|
|
137
|
-
make_request("/league/#{league_id}/losers_bracket")
|
|
142
|
+
make_request("/v1/league/#{league_id}/losers_bracket")
|
|
138
143
|
end
|
|
139
144
|
|
|
140
145
|
# Get transactions for a specific week.
|
|
@@ -144,7 +149,7 @@ module SleeperApi
|
|
|
144
149
|
# @return [Array<Hash>] Transaction data
|
|
145
150
|
# @see https://docs.sleeper.com/#get-transactions
|
|
146
151
|
def get_transactions(league_id, week)
|
|
147
|
-
make_request("/league/#{league_id}/transactions/#{week}")
|
|
152
|
+
make_request("/v1/league/#{league_id}/transactions/#{week}")
|
|
148
153
|
end
|
|
149
154
|
|
|
150
155
|
# Get league drafts.
|
|
@@ -153,7 +158,7 @@ module SleeperApi
|
|
|
153
158
|
# @return [Array<Hash>] Draft data
|
|
154
159
|
# @see https://docs.sleeper.com/#get-all-drafts-for-a-league
|
|
155
160
|
def get_league_drafts(league_id)
|
|
156
|
-
make_request("/league/#{league_id}/drafts")
|
|
161
|
+
make_request("/v1/league/#{league_id}/drafts")
|
|
157
162
|
end
|
|
158
163
|
|
|
159
164
|
# Get traded draft picks for a league.
|
|
@@ -162,7 +167,7 @@ module SleeperApi
|
|
|
162
167
|
# @return [Array<Hash>] Traded picks
|
|
163
168
|
# @see https://docs.sleeper.com/#get-traded-picks-in-a-draft
|
|
164
169
|
def get_league_traded_picks(league_id)
|
|
165
|
-
make_request("/league/#{league_id}/traded_picks")
|
|
170
|
+
make_request("/v1/league/#{league_id}/traded_picks")
|
|
166
171
|
end
|
|
167
172
|
|
|
168
173
|
# Fetch draft details.
|
|
@@ -171,7 +176,7 @@ module SleeperApi
|
|
|
171
176
|
# @return [Hash] Draft metadata
|
|
172
177
|
# @see https://docs.sleeper.com/#get-a-specific-draft
|
|
173
178
|
def get_draft(draft_id)
|
|
174
|
-
make_request("/draft/#{draft_id}")
|
|
179
|
+
make_request("/v1/draft/#{draft_id}")
|
|
175
180
|
end
|
|
176
181
|
|
|
177
182
|
# Get draft picks.
|
|
@@ -180,7 +185,7 @@ module SleeperApi
|
|
|
180
185
|
# @return [Array<Hash>] Pick data
|
|
181
186
|
# @see https://docs.sleeper.com/#get-all-picks-in-a-draft
|
|
182
187
|
def get_draft_picks(draft_id)
|
|
183
|
-
make_request("/draft/#{draft_id}/picks")
|
|
188
|
+
make_request("/v1/draft/#{draft_id}/picks")
|
|
184
189
|
end
|
|
185
190
|
|
|
186
191
|
# Get traded draft picks for a draft.
|
|
@@ -189,7 +194,7 @@ module SleeperApi
|
|
|
189
194
|
# @return [Array<Hash>] Traded picks
|
|
190
195
|
# @see https://docs.sleeper.com/#get-traded-picks-in-a-draft
|
|
191
196
|
def get_draft_traded_picks(draft_id)
|
|
192
|
-
make_request("/draft/#{draft_id}/traded_picks")
|
|
197
|
+
make_request("/v1/draft/#{draft_id}/traded_picks")
|
|
193
198
|
end
|
|
194
199
|
|
|
195
200
|
# Get NFL state (week, season status).
|
|
@@ -198,13 +203,84 @@ module SleeperApi
|
|
|
198
203
|
# @return [Hash] State data
|
|
199
204
|
# @see https://docs.sleeper.com/#get-nfl-state
|
|
200
205
|
def get_nfl_state(sport = "nfl")
|
|
201
|
-
nfl_state = make_request("/state/#{sport}")
|
|
206
|
+
nfl_state = make_request("/v1/state/#{sport}")
|
|
202
207
|
nfl_state.each_with_object({}) do |(k, v), result|
|
|
203
208
|
key = k.is_a?(String) ? k.to_sym : k
|
|
204
209
|
result[key] = v
|
|
205
210
|
end
|
|
206
211
|
end
|
|
207
212
|
|
|
213
|
+
# Get per-player statistics for one week, or for a whole season.
|
|
214
|
+
#
|
|
215
|
+
# Undocumented. Returns an object keyed by player id — plus `TEAM_XXX` keys
|
|
216
|
+
# for team-level rows — each holding raw counting stats (`rec`, `rush_yd`,
|
|
217
|
+
# `off_snp`, `rec_rz_tgt`…) alongside Sleeper's three canned point totals
|
|
218
|
+
# `pts_ppr` / `pts_half_ppr` / `pts_std`. 228 distinct fields were observed
|
|
219
|
+
# across one week of 2025.
|
|
220
|
+
#
|
|
221
|
+
# **Nothing here 404s.** An unplayed week, a week out of range, and an
|
|
222
|
+
# unrecognised season type all answer 200 with `{}`, so an empty result is
|
|
223
|
+
# a legitimate answer and is indistinguishable from a typo. Validate the
|
|
224
|
+
# arguments before you trust an empty body.
|
|
225
|
+
#
|
|
226
|
+
# Omitting `week` requests season totals, which is a different resource at
|
|
227
|
+
# a shorter path rather than a default of week 1.
|
|
228
|
+
#
|
|
229
|
+
# `pre` and `post` restart week numbering at 1, exactly as #schedule does,
|
|
230
|
+
# so rows from different season types must never be pooled.
|
|
231
|
+
#
|
|
232
|
+
# @param season [Integer, String] Season year, e.g. 2025
|
|
233
|
+
# @param week [Integer, String, nil] Week number, or nil for season totals
|
|
234
|
+
# @param season_type [String] "regular" (default), "pre", or "post"
|
|
235
|
+
# @param sport [String] Sport code (default: "nfl")
|
|
236
|
+
# @return [HTTParty::Response] Player id to stats mapping
|
|
237
|
+
def stats(season, week: nil, season_type: "regular", sport: "nfl")
|
|
238
|
+
make_request(weekly_path("stats", sport, season_type, season, week))
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Get per-player projections for one week, or for a whole season.
|
|
242
|
+
#
|
|
243
|
+
# Same shape and same caveats as #stats, with one of its own: for a season
|
|
244
|
+
# Sleeper has not projected, this still returns a full set of entries —
|
|
245
|
+
# 9,386 of them for 2030 — every one holding only `{"adp_dd_ppr" => 1000.0}`
|
|
246
|
+
# and no `pts_ppr` at all. **A row count is not evidence of a projection.**
|
|
247
|
+
# Filter on the field you actually want.
|
|
248
|
+
#
|
|
249
|
+
# `adp_dd_ppr` 1000.0 and `pos_rank_*` 999.0 are "unknown" sentinels rather
|
|
250
|
+
# than values.
|
|
251
|
+
#
|
|
252
|
+
# @param season [Integer, String] Season year, e.g. 2026
|
|
253
|
+
# @param week [Integer, String, nil] Week number, or nil for season totals
|
|
254
|
+
# @param season_type [String] "regular" (default), "pre", or "post"
|
|
255
|
+
# @param sport [String] Sport code (default: "nfl")
|
|
256
|
+
# @return [HTTParty::Response] Player id to projections mapping
|
|
257
|
+
def projections(season, week: nil, season_type: "regular", sport: "nfl")
|
|
258
|
+
make_request(weekly_path("projections", sport, season_type, season, week))
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Get a season's game schedule.
|
|
262
|
+
#
|
|
263
|
+
# Undocumented, and served from the host root rather than /v1 — hence the
|
|
264
|
+
# version living in the paths rather than in base_uri.
|
|
265
|
+
#
|
|
266
|
+
# Returns a flat array of games, each `{status, date, home, away, week,
|
|
267
|
+
# game_id}`. A team's bye week is the week it appears in no game; that
|
|
268
|
+
# derives exactly, but only within one season type — `pre` (weeks 1-3) and
|
|
269
|
+
# `post` (weeks 1-4) restart week numbering, so games from different season
|
|
270
|
+
# types must never be pooled.
|
|
271
|
+
#
|
|
272
|
+
# A season Sleeper has not scheduled yet answers 200 with an empty array
|
|
273
|
+
# rather than 404, so an empty result is a legitimate answer and not an
|
|
274
|
+
# error.
|
|
275
|
+
#
|
|
276
|
+
# @param season [Integer, String] Season year, e.g. 2026
|
|
277
|
+
# @param season_type [String] "regular" (default), "pre", or "post"
|
|
278
|
+
# @param sport [String] Sport code (default: "nfl")
|
|
279
|
+
# @return [HTTParty::Response] Array of games
|
|
280
|
+
def schedule(season, season_type: "regular", sport: "nfl")
|
|
281
|
+
make_request("/schedule/#{sport}/#{season_type}/#{season}")
|
|
282
|
+
end
|
|
283
|
+
|
|
208
284
|
# Get trending players.
|
|
209
285
|
#
|
|
210
286
|
# @param sport [String] Sport code (default: "nfl")
|
|
@@ -214,7 +290,7 @@ module SleeperApi
|
|
|
214
290
|
# @return [Array<Hash>] Trending players
|
|
215
291
|
# @see https://docs.sleeper.com/#trending-players
|
|
216
292
|
def trending_players(sport = "nfl", type: "add", lookback_hours: 24, limit: 25)
|
|
217
|
-
make_request("/players/#{sport}/trending/#{type}?lookback_hours=#{lookback_hours}&limit=#{limit}")
|
|
293
|
+
make_request("/v1/players/#{sport}/trending/#{type}?lookback_hours=#{lookback_hours}&limit=#{limit}")
|
|
218
294
|
end
|
|
219
295
|
|
|
220
296
|
# Get all player data (cached for 24 hours).
|
|
@@ -225,7 +301,7 @@ module SleeperApi
|
|
|
225
301
|
def get_players(sport = "nfl")
|
|
226
302
|
return @players_cache if @players_cache && @cache_timestamp && (Time.now - @cache_timestamp) < (3600 * 24)
|
|
227
303
|
|
|
228
|
-
response = make_request("/players/#{sport}")
|
|
304
|
+
response = make_request("/v1/players/#{sport}")
|
|
229
305
|
@players_cache = response.parsed_response
|
|
230
306
|
@cache_timestamp = Time.now
|
|
231
307
|
|
|
@@ -244,6 +320,20 @@ module SleeperApi
|
|
|
244
320
|
|
|
245
321
|
private
|
|
246
322
|
|
|
323
|
+
# Path for #stats and #projections. Segments are escaped because they are
|
|
324
|
+
# interpolated into a URI path: an unescaped one can walk out of the
|
|
325
|
+
# endpoint entirely, which is the bug the consuming app had to work around
|
|
326
|
+
# for #get_user.
|
|
327
|
+
#
|
|
328
|
+
# A nil week drops the segment rather than defaulting, because the shorter
|
|
329
|
+
# path is season totals.
|
|
330
|
+
def weekly_path(resource, sport, season_type, season, week)
|
|
331
|
+
segments = [resource, sport, season_type, season, week].compact
|
|
332
|
+
escaped = segments.map { |segment| ERB::Util.url_encode(segment.to_s) }
|
|
333
|
+
|
|
334
|
+
"/v1/#{escaped.join("/")}"
|
|
335
|
+
end
|
|
336
|
+
|
|
247
337
|
# Make an HTTP request with retry logic and logging.
|
|
248
338
|
#
|
|
249
339
|
# @param path [String] API endpoint path
|
data/lib/sleeper_api/version.rb
CHANGED