0.0
The project is in a healthy, maintained state
A comprehensive Ruby gem for interacting with Sleeper's fantasy football API. Features include league management, user profiles, draft data, matchups, transactions, player data with caching, and comprehensive error handling. Built with performance and reliability in mind for production applications.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 13.0
~> 3.13
~> 1.80
~> 0.21
~> 3.14

Runtime

~> 0.21
 Project Readme

SleeperApi

Gem Version

A comprehensive Ruby gem for interacting with Sleeper's fantasy football API. Built with performance, reliability, and developer experience in mind.

Features

  • Complete API Coverage - Users, leagues, drafts, players, matchups, transactions

  • Performance Optimized - Smart caching, connection pooling, rate limiting

  • Robust Error Handling - Automatic retries, timeout management, detailed error messages

  • Well Tested - 90%+ test coverage with RSpec

  • Highly Configurable - Custom timeouts, retries, logging

  • Production Ready - Type signatures, CI/CD, code quality tools

Installation

Add this line to your application's Gemfile:

gem 'sleeper_api'

Quick Start

require 'sleeper_api'

# Basic usage with default configuration
client = SleeperApi.client
league = client.league("123456789012345678")

# Access league information
puts league.name          # "My Fantasy League"
puts league.total_rosters # 12
puts league.status        # "in_season"

# Get rosters
rosters = league.rosters
rosters.each do |roster|
  puts "#{roster[:owner_display_name]}#{roster[:wins]}-#{roster[:losses]}"
end

Configuration

Customize the gem's behavior:

SleeperApi.configure do |config|
  config.timeout = 45    # Request timeout in seconds (10-60)
  config.retries = 5     # Number of retries on failure (0-5)
  config.logger = Logger.new(STDOUT)  # Custom logger
end

# Configuration is applied to all subsequent client instances
client = SleeperApi.client

API Coverage

Users

Get user information and their leagues/drafts:

# Find a user by username
user = SleeperApi.client.user('johndoe')

# Basic profile
user.username                  # => "johndoe"
user.display_name              # => "John Doe"
user.avatar_url                # => "https://sleepercdn.com/avatars/abc123"
user.is_bot                    # => false

# All leagues for current season
leagues = user.leagues
leagues.each do |league|
  status = league[:status] == "finished" ? "🏆" : league[:status]
  puts "#{status} #{league[:name]} (#{league[:total_rosters]} teams)"
end

# Specific season
old_leagues = user.leagues(2023)
championships = old_leagues.select { |l| l[:status] == "finished" }

# Get all my rosters across all leagues
rosters = user.rosters
rosters.each do |roster|
  puts "#{roster[:league_name]}: #{roster[:team_name]} (#{roster[:wins]}-#{roster[:losses]})"
end

# Best performing team this season
best_roster = user.rosters.max_by { |r| r[:wins].to_i - r[:losses].to_i }
puts "Top team: #{best_roster[:team_name]} (#{best_roster[:wins]}-#{best_roster[:losses]})"

# All drafts with enhanced info
drafts = user.drafts(2024)
drafts.each do |draft|
  status = draft[:status] == "complete" ? "✅" : draft[:status]
  puts "#{status} #{draft[:league_name]} - #{draft[:type]} (#{draft[:total_teams]} teams)"
end

# Live drafts only
live_drafts = user.drafts.select { |d| d[:status] == "live" }

# Season summary
summary = user.summary
puts "#{summary[:total_leagues]} leagues this season"
puts "Winning record in #{summary[:winning_record].length} leagues"
puts "Overall: #{summary[:total_wins]}-#{summary[:total_losses]}"

Leagues

Access league data, rosters, matchups, and transactions:

league = SleeperApi.client.league("123456")

# Basic league info
league.name                    # => "My Fantasy League"
league.season                  # => "2024"
league.total_rosters           # => 12
league.status                  # => "regular"
league.avatar_url              # => "https://sleepercdn.com/avatars/abc123"

# Formatted rosters (with team names and records)
rosters = league.rosters
roster = rosters.first
roster[:owner_display_name]    # => "John Doe"
roster[:team_name]             # => "Gridiron Gang"
roster[:wins]                  # => 7
roster[:losses]                # => 2
roster[:remaining_faab]        # => 45
roster[:starters]              # => ["player_id_1", "player_id_2", ...]
roster[:bench]                 # => ["player_id_4", "player_id_5", ...]

# My roster only
my_roster = league.rosters(user_id: my_user_id)

# Week-specific matchups
matchups = league.matchups_by_week(week: 5)
matchup = matchups.first
team = matchup[:rosters].first
team[:points]                  # => 142.5
team[:starter_points]          # => [{ "player_id" => 18.2 }, ...]

# Transactions
transactions = league.transactions(week: 5)
tx = transactions.first
tx[:type]                      # => "tr"
tx[:adds]                      # => [{ player_id: "123", roster_id: 1 }]
tx[:drops]                     # => [{ player_id: "456", roster_id: 1 }]
tx[:waiver_bid]                # => 12

# Users
users = league.users
user = users.find { |u| u[:commissioner] }
user[:display_name]            # => "Commish McGee"
user[:team_name]               # => "Champions"

# Playoffs
playoff_matchups = league.playoff_bracket
matchup = playoff_matchups.first
matchup[:team1_owner]          # => "Team Alpha"
matchup[:winner_owner]         # => "Team Alpha"

# Tourlet Bowl / Losers Bracket
toilet_bowl_matchups = league.toilet_bowl
matchup = toilet_bowl_matchups.first
matchup[:team1_owner]          # => "Team Beta"
matchup[:winner_owner]         # => "Team Beta"

Drafts

Access draft information, picks, and traded picks:

# Get a specific draft
draft = SleeperApi.client.draft("draft_123456")

# Basic draft info
draft.type                     # => "snake" or "auction"
draft.status                   # => "pre_draft", "live", "complete"
draft.season                   # => "2024"
draft.start_time              # => "2024-08-27T20:00:00.000Z"
draft.league.name             # => "My Fantasy League"

# All picks (180 picks for 15-round, 12-team draft)
picks = draft.picks
puts "#{picks.length} total picks made"

# Filter picks
round_1 = draft.picks(round: 1)
team_3_picks = draft.picks(roster_id: 3)

round_1.each do |pick|
  player = SleeperApi.client.get_player_by_id(pick[:player_id])
  puts "#{pick[:pick_no]}. #{player['full_name']} (#{player['position']}) - Team #{pick[:roster_id]}"
end

# Who drafted a specific player?
picker = draft.picked_by("4036") # User 1

# Traded picks
trades = draft.traded_picks
team_3_trades = draft.traded_picks(original_owner_id: 3)

team_3_trades.each do |trade|
  puts "Team 3 traded away #{trade[:round]}.#{trade[:draft_slot]} to Team #{trade[:owner_id]}"
end

# Live draft - next pick info
if draft.status == "live"
  next_pick = draft.next_pick
  puts "Next up: Round #{next_pick[:round]}, Pick #{next_pick[:pick_no]} (slot #{next_pick[:draft_slot]})"
end

# Quick summary with top picks
summary = draft.summary
puts "#{summary[:total_picks]} picks in #{summary[:total_rounds]} rounds"
summary[:top_picks].each do |pick|
  puts "#{pick[:pick_no]}. #{pick[:player_name]} (#{pick[:position]}, #{pick[:team]}) - #{pick[:roster_id]}"
end

# Grouped views
rounds = draft.rounds # { 1 => [picks], 2 => [picks], ... }
team_3_rounds = draft.team_picks[3] # Team 3's picks by round

Players

Access player data with automatic caching:

# Get all players (cached for 24 hours)
players = client.get_players

# Find specific player
player = client.get_player_by_id("1234")
puts "#{player['first_name']} #{player['last_name']} - #{player['position']}"

# Get trending players
trending_adds = client.trending_players(type: "add", limit: 10)
trending_drops = client.trending_players(type: "drop", limit: 10)

Player Helper

All resource classes (League, User, Draft) include a player_details helper method that returns formatted Sleeper player data. This eliminates repetitive player lookups and ensures consistent symbolization.

# In any resource (League, User, Draft)
league = SleeperApi.client.league("123456")

# Get player details with one method call
player = league.player_details("4036") # Christian McCaffrey
if player
  puts "#{player[:full_name]} (#{player[:position]}, #{player[:team]})"
  puts "Bye week: #{player[:bye]} | Injury status: #{player[:injury_status]}"
end

# Works in matchups
matchups = league.matchups_by_week(week: 5)
matchup = matchups.first
team = matchup[:rosters].first

# Get starter names and positions
team[:starters].each do |player_id|
  player = league.player_details(player_id)
  if player
    puts "#{player[:full_name]} (#{player[:position]}) - #{team[:starter_points].find { |p| p.keys.first == player_id }&.values&.first || 0} pts"
  end
end

# Works in rosters
roster = league.rosters.first
roster[:starters].each_with_index do |player_id, index|
  player = league.player_details(player_id)
  position = player ? player[:position] : "?"
  points = roster[:starter_points]&.dig(index, player_id) || 0
  puts "#{position}: #{player ? player[:full_name] : 'Unknown'} (#{points} pts)"
end

# Works in drafts
draft = SleeperApi.client.draft("draft_123")
round_1 = draft.picks(round: 1)

round_1.each do |pick|
  player = draft.player_details(pick[:player_id])
  if player
    puts "#{pick[:pick_no]}. #{player[:full_name]} (#{player[:position]}, #{player[:team]})"
  end
end

# Works across user's leagues
user = SleeperApi.client.user('johndoe')
rosters = user.rosters

rosters.each do |roster|
  puts "\n#{roster[:team_name]} starters:"
  roster[:starters].each do |player_id|
    player = roster.player_details(player_id) # Note: uses the League instance under the hood
    puts "  • #{player ? player[:full_name] : 'Unknown'} (#{player[:position] if player})"
  end
end

Additional Endpoints

# Get NFL state
state = client.get_nfl_state
puts "Current week: #{state['week']}"

# Get playoff brackets
winners_bracket = client.get_league_playoff_bracket("league_id")
losers_bracket = client.get_league_toilet_bowl("league_id")

# Get league drafts
league_drafts = client.get_league_drafts("league_id")

# Get traded picks
traded_picks = client.get_league_traded_picks("league_id")

Error Handling

The gem provides comprehensive error handling:

begin
  league = client.league("invalid_id")
  # Process league data
rescue SleeperApi::Error => e
  puts "API Error: #{e.message}"
rescue ArgumentError => e
  puts "Invalid parameter: #{e.message}"
end

Error Types

  • SleeperApi::Error - API-related errors (404, 500, timeouts, etc.)

  • ArgumentError - Invalid parameters passed to methods

Performance Considerations

Caching

  • Player data is cached for 24 hours to reduce API calls

  • League/User/Draft data is cached per instance

  • Cache files are stored in the current working directory

Rate Limiting

  • Be mindful of Sleeper's rate limits: stay under 1000 API calls per minute

  • The gem automatically handles timeouts and retries

  • Consider caching frequently accessed data in your application

Memory Usage

  • Large datasets (like all players) are cached to disk

  • League rosters and matchups are fetched lazily

  • Use no_data: true when initializing leagues if you don't need immediate data

Testing

The gem includes comprehensive tests:

# Run all tests
bundle exec rspec
# Run with coverage
bundle exec rspec --coverage
# Run specific test file
bundle exec rspec spec/sleeper_api/client_spec.rb

Setup

git clone https://github.com/eruity1/sleeper_api.git
cd sleeper_api
bundle install

Code Quality

# Run all checks (tests + linting)
bundle exec rake ci

# Run RuboCop
bundle exec rubocop

# Auto-fix RuboCop issues
bundle exec rubocop -a

Contributing

  1. Fork the repository

  2. Create a feature branch (git checkout -b feature/amazing-feature)

  3. Commit your changes (git commit -m 'Add amazing feature')

  4. Push to the branch (git push origin feature/amazing-feature)

  5. Open a Pull Request

Development Dependencies

  • rspec - Testing framework

  • rubocop - Code style and quality

  • simplecov - Test coverage

  • webmock - HTTP request mocking

Requirements

  • Ruby 2.6.0 or higher

  • No external dependencies (HTTParty is bundled)

License

The gem is available as open source under the terms of the MIT License.

Changelog

See CHANGELOG.md for version history and updates.