A type-safe Go library and HTTP API server for accessing NBA statistics from stats.nba.com. Features 100% endpoint coverage with complete feature parity to the Python nba_api library.
World's first complete NBA API implementation in Go!
- ✅ 141 endpoints (all NBA Stats API endpoints plus international schedule)
- ✅ All categories complete: Box Scores, Player, Team, League, Game, Advanced, International
- ✅ Dual access: Native Go SDK + HTTP REST API for any language
- ✅ Type-safe: generated, endpoint-specific request/response types; see CHANGELOG.md for the project's actual defect and fix history
- ✅ Complete feature parity with Python nba_api + additional features
What "100% coverage" means here: all 141 endpoints are implemented, type-safe, and covered by generated parsing tests against fixtures synthesized from each endpoint's own metadata - it does not currently mean all 141 are reachable against live
stats.nba.comtraffic. A full reachability sweep on 2026-07-22 (raw requests, no SDK, one per endpoint) found only 5 of 141 endpoints respond at all, from every network tested so far (a residential/business ISP and GitHub Actions runners alike) - the other 136 hang to a hard timeout. This looks like upstream bot-defense on NBA.com's side, not a bug in this library, but it means most endpoints won't return real data against the live API right now, regardless of client. Seetests/integration/README.mdfor the full methodology and results.
See Migration Guide to migrate from Python nba_api to Go.
As of v2.0.0, all three documented type-defect families below are fully fixed (verified by scanning every committed field against all three patterns - zero remaining instances). If you're upgrading from v1.3.0 or earlier, this is a source-breaking change to the affected struct fields; see CHANGELOG.md's [2.0.0] section for the full migration guide before upgrading. If you're still on a tagged release at v1.3.0 or earlier, everything below still applies to you.
The generator used to infer a Go type for each field from its NBA.com column name (e.g. names containing common stat abbreviations were typed float64), and that heuristic got some fields wrong - silently, not as an error:
- Display-name fields typed
float64.DISPLAY_FIRST_LAST,DISPLAY_LAST_COMMA_FIRST,DISPLAY_FI_LAST,PLAYER_NAME_LAST_FIRSTcontain a name string (e.g."Nikola Jokić"); typedfloat64, parsing a name string produced0, not an error. AffectedCommonPlayerInfoV2,CommonAllPlayers,CommonAllPlayersV2, andPlayerDashPtShots. - Textual range-bucket fields typed
int.SHOT_CLOCK_RANGE,DRIBBLE_RANGE,CLOSE_DEF_DIST_RANGE,TOUCH_TIME_RANGE,SHOT_DIST_RANGE,SHOT_ZONE_RANGEhold text buckets (e.g."24-22","Very Tight"); typedint, parsing text produced0. AffectedPlayerDashPtShots,TeamDashPtShots,PlayerVsPlayer,ShotChartLineupDetail, andShotChartDetail. - Decimal percentage/rating fields typed
string.FG_PCT_RA,FG_PCT_IN_PAINT,FG_PCT_ABOVE_BREAK_3,FG_PCT_BACKCOURT,FG_PCT_LEFT_CORNER_3,FG_PCT_RIGHT_CORNER_3were declaredstringand formatted with%.0f, so a value like0.357was stored as"0"- decimal precision lost, not merely stringified. AffectedLeagueDashPlayerShotLocationsandLeagueDashTeamShotLocations.
This isn't a claim that generated code is now defect-free everywhere - it's specifically these three, previously-documented, previously-confirmed families. tools/generator/fieldtypes.json/fieldtype_overrides.json cover 854 fields with varying levels of verification confidence (see CHANGELOG.md's [2.0.0] notes). videoevents.go's unrelated field-naming issue (not a type defect - see [2.0.0]'s notes on it) is fixed on main post-v2.0.0 via tools/generator/fieldname_overrides.json; see CHANGELOG.md's [Unreleased] section. See the maintainability assessment for the analysis that started this fix.
- 🏆 141 Stats API endpoints - Complete feature parity with Python nba_api + international schedule
- Go SDK - Type-safe library for direct Go integration
- HTTP API Server - Complete REST API exposing all endpoints for any language
- Docker/Podman Ready - Multi-stage containerization (< 20MB images)
- Live API - Real-time game data and scoreboards
- Static Data - Pre-loaded player and team datasets with search (5,135 players, 30 teams)
- Type Safety - Automatic type inference from field names to Go types
- Context Support - Full support for cancellation and timeouts
- Zero Frameworks - HTTP API uses stdlib only (net/http, encoding/json)
- Code Generation - Advanced tooling for type-safe endpoint generation
- ⚡ Rate Limiting - Per-IP rate limiting (100 req/s, burst 200) to prevent abuse
- 📊 Metrics & Monitoring - Built-in
/metricsendpoint with request stats, response times, error rates - 🏥 Health Checks -
/healthendpoint with NBA API connectivity status and build info - 🔒 CORS Support - Configurable cross-origin resource sharing
- 📝 Request Logging - Structured logging with response times
- 💾 Low Memory - < 100MB typical memory usage
- 🔄 Graceful Shutdown - Safe shutdown with connection draining (10s timeout)
Requires Go 1.26.5 or later (the go directive in go.mod; older toolchains cannot build this module).
go get github.com/n-ae/nba-api-go/v3# Docker/Podman
podman pull nba-api-go:latest # or build locally
# From source
go build -o nba-api-server ./cmd/nba-api-serverThis project provides two ways to access NBA data:
Best for: Type-safety, performance, direct Go integration
import "github.com/n-ae/nba-api-go/v3/pkg/stats"Best for: Python, JavaScript, Ruby, or any language that can make HTTP requests
# Start server
docker run -p 8080:8080 nba-api-go
# Use from any language
curl "http://localhost:8080/api/v1/stats/playergamelog?PlayerID=2544&Season=2023-24"See API Usage Documentation for complete HTTP API guide with Python/JavaScript examples.
package main
import (
"context"
"fmt"
"log"
"github.com/n-ae/nba-api-go/v3/pkg/stats"
"github.com/n-ae/nba-api-go/v3/pkg/stats/endpoints"
"github.com/n-ae/nba-api-go/v3/pkg/stats/parameters"
)
func main() {
client := stats.NewDefaultClient()
req := endpoints.PlayerCareerStatsRequest{
PlayerID: "203999", // Nikola Jokić
PerMode: parameters.PerModePerGame,
LeagueID: parameters.LeagueIDNBA,
}
resp, err := endpoints.PlayerCareerStats(context.Background(), client, req)
if err != nil {
log.Fatal(err)
}
for _, season := range resp.Data.SeasonTotalsRegularSeason {
fmt.Printf("Season %s: %.1f PPG, %.1f RPG, %.1f APG\n",
season.SeasonID, season.PTS, season.REB, season.AST)
}
}req := endpoints.PlayerGameLogRequest{
PlayerID: "203999",
Season: parameters.NewSeason(2023),
SeasonType: parameters.SeasonTypeRegular,
}
resp, err := endpoints.PlayerGameLog(context.Background(), client, req)
if err != nil {
log.Fatal(err)
}
for _, game := range resp.Data.PlayerGameLog {
fmt.Printf("%s | %s | %d pts, %d reb, %d ast\n",
game.GameDate, game.Matchup, game.PTS, game.REB, game.AST)
}req := endpoints.LeagueLeadersRequest{
Season: parameters.NewSeason(2023),
SeasonType: parameters.SeasonTypeRegular,
StatCategory: parameters.StatCategoryPoints,
PerMode: parameters.PerModePerGame,
}
resp, err := endpoints.LeagueLeaders(context.Background(), client, req)
if err != nil {
log.Fatal(err)
}
for _, leader := range resp.Data.LeagueLeaders {
fmt.Printf("%d. %s (%s) - %.1f PPG\n",
leader.Rank, leader.Player, leader.Team, leader.PTS)
}package main
import (
"context"
"fmt"
"log"
"github.com/n-ae/nba-api-go/v3/pkg/live"
"github.com/n-ae/nba-api-go/v3/pkg/live/endpoints"
)
func main() {
client := live.NewDefaultClient()
resp, err := endpoints.Scoreboard(context.Background(), client)
if err != nil {
log.Fatal(err)
}
for _, game := range resp.Data.Scoreboard.Games {
fmt.Printf("%s @ %s - %s\n",
game.AwayTeam.TeamTricode,
game.HomeTeam.TeamTricode,
game.GameStatusText)
}
}package main
import (
"fmt"
"log"
"github.com/n-ae/nba-api-go/v3/pkg/stats/static"
)
func main() {
players, err := static.SearchPlayers("lebron")
if err != nil {
log.Fatal(err)
}
for _, player := range players {
fmt.Printf("%s (ID: %d)\n", player.FullName, player.ID)
}
player, err := static.FindPlayerByID(203999)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found: %s\n", player.FullName)
teams, err := static.SearchTeams("lakers")
if err != nil {
log.Fatal(err)
}
for _, team := range teams {
fmt.Printf("%s (%s)\n", team.FullName, team.Abbreviation)
}
}req := endpoints.CommonPlayerInfoRequest{
PlayerID: "203999", // Nikola Jokić
}
resp, err := endpoints.CommonPlayerInfo(context.Background(), client, req)
if err != nil {
log.Fatal(err)
}
info := resp.Data.CommonPlayerInfo[0]
dob, err := info.DateOfBirth()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s was born on %s\n", info.DisplayFirstLast, dob.Format("January 2, 2006"))DateOfBirth() parses the endpoint's raw birthdate string into a time.Time. It's also
available on CommonPlayerInfoV2CommonPlayerInfo, CommonTeamRosterCommonTeamRoster, and
CommonTeamRosterV2CommonTeamRoster — each parses that endpoint's specific date format.
The library is organized into the following packages:
- pkg/client - Core HTTP client with middleware support
- pkg/client/middleware - Built-in middleware (retry, rate limiting, headers, logging)
- pkg/stats - NBA Stats API client and endpoints
- pkg/live - NBA Live Data API client and endpoints
- pkg/models - Common data structures and error types
- pkg/stats/static - Static player and team data with search
- pkg/stats/parameters - Type-safe parameter definitions
The client supports composable middleware for cross-cutting concerns. The
seam itself — client.Middleware, client.RoundTripper,
client.RoundTripperFunc, and client.Chain — lives in the importable
pkg/client package, so you can write your own middleware without
depending on anything internal to this module. The built-in constructors
(WithRetry, RetryConfig/DefaultRetryConfig, WithPerHostRateLimit,
WithHeaders, WithUserAgent, WithReferer, WithAccept, and the
WithLogging family) live in the importable pkg/client/middleware
package too, so you can reconfigure them - not just add alongside them:
import (
"context"
"log"
"net/http"
"time"
"github.com/n-ae/nba-api-go/v3/pkg/client"
"github.com/n-ae/nba-api-go/v3/pkg/client/middleware"
"github.com/n-ae/nba-api-go/v3/pkg/stats"
)
// A custom middleware - written entirely with exported types.
func withRequestID(id string) client.Middleware {
return func(next client.RoundTripper) client.RoundTripper {
return client.RoundTripperFunc(func(ctx context.Context, req *http.Request) (*http.Response, error) {
req.Header.Set("X-Request-ID", id)
return next.RoundTrip(ctx, req)
})
}
}
// AdditionalMiddlewares layers your own middleware on top of the default
// chain (retry, the headers NBA.com's API expects, and a per-host rate
// limit) without replacing it - no need to call DefaultMiddlewares()
// yourself.
statsClient, err := stats.NewClient(stats.Config{
AdditionalMiddlewares: []client.Middleware{withRequestID("abc123")},
})
if err != nil {
log.Fatal(err)
}
// To reconfigure a built-in instead of just adding to it - e.g. a
// stricter retry budget - set Middlewares explicitly (this REPLACES the
// default chain, so re-add anything else you still want from it):
retryConfig := middleware.DefaultRetryConfig()
retryConfig.MaxRetries = 1
statsClient, err = stats.NewClient(stats.Config{
Middlewares: []client.Middleware{
middleware.WithRetry(retryConfig),
middleware.WithPerHostRateLimit(3, 5),
},
})
if err != nil {
log.Fatal(err)
}pkg/live has the equivalent live.DefaultMiddlewares() and
AdditionalMiddlewares.
The library includes embedded static data for all NBA players and teams:
- 5,135 players with search by name, ID, or regex
- 30 teams with search by name, abbreviation, or city
- Accent-insensitive search for international player names
- Active/inactive filtering for players
All NBA API parameters are strongly typed with validation:
import "github.com/n-ae/nba-api-go/v3/pkg/stats/parameters"
// Season types
parameters.SeasonTypeRegular
parameters.SeasonTypePlayoffs
parameters.SeasonTypeAllStar
// Per-mode calculations
parameters.PerModeTotals
parameters.PerModePerGame
parameters.PerModePer36
// League IDs
parameters.LeagueIDNBA
parameters.LeagueIDABA
// Create seasons
season := parameters.NewSeason(2023) // "2023-24"See the examples directory for complete working examples:
- player_stats - Fetch player career statistics
- game_log - Get player game-by-game stats
- league_leaders - View statistical leaders
- scoreboard - Get today's games and scores
- player_search - Search players and teams
Run examples:
go run examples/player_stats/main.go
go run examples/game_log/main.go
go run examples/league_leaders/main.go
go run examples/scoreboard/main.go
go run examples/player_search/main.go# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Test example programs compile
make test-examples
# Run benchmarks
go test -bench=. -benchmem ./...
# Run integration tests (requires INTEGRATION_TESTS=1)
INTEGRATION_TESTS=1 go test -tags=integration ./...See BENCHMARKS.md for detailed performance analysis.
The HTTP server includes built-in monitoring endpoints:
Health Check (/health):
curl http://localhost:8080/healthReturns:
- Server status
- NBA API connectivity status (operational/degraded)
- Build information (Go version, build time, git commit)
- Endpoint counts
- Timestamp
Metrics (/metrics):
curl http://localhost:8080/metricsReturns:
- Uptime
- Total requests & errors
- Requests by status code
- Requests by path
- Response time statistics (avg, min, max)
See DEPLOYMENT.md for complete deployment guide including:
- Systemd service setup
- Docker/Podman deployment
- Cloud platform deployment (Fly.io, Railway, etc.)
- Reverse proxy configuration (Nginx, Caddy)
- SSL/TLS setup
- Monitoring with Prometheus/UptimeRobot
- Cost estimates and recommendations
Quick Deploy:
# Build with version info
go build \
-ldflags="-X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ) -X main.gitCommit=$(git rev-parse --short HEAD)" \
-o nba-api-server \
./cmd/nba-api-server
# Run
./nba-api-serverRecommended Setup (Solo Maintainer):
- Hetzner VPS ($5/month)
- Systemd service
- Caddy reverse proxy (auto SSL)
- UptimeRobot (free monitoring)
- Total: $5/month, ~1 hour maintenance/month
Based on the ADR, the library is being developed in phases:
- Phase 1: Foundation (HTTP client, middleware, models)
- Phase 2: Core Stats API (initial endpoints)
- Phase 3: Live API (Scoreboard endpoint)
- Phase 4: Additional Endpoints - ✅ COMPLETE (141 endpoints including international schedule)
- Phase 5: Performance Optimization (benchmarks complete)
- Code generation tooling (completed)
- HTTP API Server - ✅ COMPLETE (all endpoints exposed)
- Migration guide from Python nba_api - ✅ COMPLETE (887 lines)
- HTTP API client examples - ✅ COMPLETE (Python, JavaScript, Bash)
- Integration test suite - ✅ COMPLETE (60+ tests)
- CLI tool (optional - low priority)
Contributions are welcome! Please see the ADR for architectural decisions and development guidelines.
MIT License - see LICENSE file for details.
This project is inspired by and aims for feature parity with the Python nba_api library by Swar Patel.
This library is not affiliated with or endorsed by the NBA. All data is publicly available from NBA.com. Please review the NBA.com Terms of Use before using this library.