Anmelden API-Schlüssel holen
Tutorials

How to Build a Player Career Stats Page Across Multiple Leagues

Illustration of a basketball player career stats page spanning multiple leagues

Learn how to fetch a basketball player's career stats across competitions and seasons — including NBA and Euroleague history — using one endpoint.

Auf dieser Seite

A player who's played in both the NBA and Euroleague has two separate statistical histories — and most APIs make you already know the league and season ID before you can query either. Live Basketball API's /player/statistics endpoint flips that: send just a player ID, and it auto-resolves their current competition and season, while also handing you a full catalogue of every other league and season they have stats for.

The Auto-Resolve Pattern

Send only player_id and the endpoint picks the player's primary competition and most recent season for you — no need to already know their league_id or season_id:

import requests

response = requests.get(
    'https://live-basketball-api.com/api/v1/player/statistics',
    params={'api_key': 'YOUR_KEY', 'player_id': 861608}
).json()

print(response['mode'])  # "auto"
print(f"{response['league']['name']} — {response['season']['name']}")
print(response['regular_season']['points_per_game'])

Reading the Stat Block

The active split (regular_season, playoffs, or overall) contains totals, per-game averages, and shooting splits in one object:

function StatSummary({ stats }) {
  return (
    <div className="stat-grid">
      <Stat label="PPG" value={stats.points_per_game} />
      <Stat label="RPG" value={stats.rebounds.per_game} />
      <Stat label="APG" value={stats.assists_per_game} />
      <Stat label="FG%" value={stats.field_goals.pct} />
      <Stat label="3P%" value={stats.three_pointers.pct} />
      <Stat label="FT%" value={stats.free_throws.pct} />
    </div>
  );
}

Check available_types before rendering tabs for regular season vs. playoffs — not every season has both:

const tabs = response.available_types; // e.g. ["regular_season", "playoffs"]

The Competitions Catalogue: Cross-League History

The real value here is the competitions array, which lists every league and season the player has recorded stats for — including leagues completely different from the one just returned:

response['competitions'].forEach(comp => {
  console.log(`${comp.league_name} (${comp.country})`);
  comp.seasons.forEach(s => {
    console.log(`  ${s.name} — types: ${s.types.join(', ')}`);
  });
});

// NBA (USA)
//   NBA 25/26 — types: regular_season
//   NBA 24/25 — types: regular_season, playoffs
// Euroleague (International)
//   Euroleague 17/18 — types: regular_season, playoffs

This is how you'd build a "career history" dropdown letting a user pick, say, Luka Dončić's Euroleague years at Real Madrid instead of his current NBA season — all from IDs already in hand, no separate search needed.

Fetching a Specific Past Season

Once you have a league_id/season_id pair from the catalogue, request it directly:

response = requests.get(
    'https://live-basketball-api.com/api/v1/player/statistics',
    params={
        'api_key': 'YOUR_KEY',
        'player_id': 861608,
        'league_id': 138,     # Euroleague, from competitions[]
        'season_id': 13537    # 17/18 season
    }
).json()

print(response['mode'])  # "league"

The season_id-Only Shortcut

If you've stored a season_id from a previous response but not its matching league_id, you don't need to look it up — the endpoint resolves the competition for you:

response = requests.get(
    'https://live-basketball-api.com/api/v1/player/statistics',
    params={'api_key': 'YOUR_KEY', 'player_id': 861608, 'season_id': 65360}
).json()

print(response['mode'])  # "season_lookup"
print(response['league']['name'])  # resolved automatically

Building a League/Season Switcher

function CareerExplorer({ playerId }) {
  const [data, setData] = useState(null);
  const [selectedSeason, setSelectedSeason] = useState(null);

  useEffect(() => {
    fetchPlayerStats(playerId, selectedSeason).then(setData);
  }, [playerId, selectedSeason]);

  if (!data) return <Loading />;

  return (
    <div>
      <select onChange={e => setSelectedSeason(JSON.parse(e.target.value))}>
        {data.competitions.flatMap(comp =>
          comp.seasons.map(s => (
            <option
              key={s.season_id}
              value={JSON.stringify({ league_id: comp.league_id, season_id: s.season_id })}
            >
              {comp.league_name} — {s.name}
            </option>
          ))
        )}
      </select>

      <StatSummary stats={data.regular_season || data.overall} />
    </div>
  );
}

Using the League-Wide Ranking Data

The ranking object tells you where a player stood league-wide that season — useful for "top 5 scorer" style callouts without fetching every player's stats to compute a rank yourself:

const pointsRank = response.regular_season.ranking.points;
console.log(`Ranked #${pointsRank.rank_per_game} of ${pointsRank.out_of} in PPG`);

Frequently Asked Questions

What does mode: "auto" vs "league" vs "season_lookup" actually change?

It only reflects which parameters you sent — auto means neither league_id nor season_id was given, league means you sent league_id, and season_lookup means you sent only season_id and the endpoint resolved the league itself. The response shape is identical in all three cases.

Why are playoffs and overall sometimes null?

Not every competition or season has playoff data (many domestic regular seasons don't), and overall is only populated for splits that track combined season+playoff totals. Check available_types before assuming a split exists.

Can I get a player's full career totals across every league combined?

Not in a single call — each request returns one league/season's stats. To build an all-time combined total, you'd iterate the competitions array and sum across calls yourself.

Does this endpoint work for retired players?

Yes, as long as the player has recorded stats in the underlying data source — check /player/search's retired flag first if you need to confirm career status before displaying it.

Artikel teilen:
← Zurück zum Blog