Tutorial · 12 min · endpoints verified 2026-09-14

How to build an NCAAF fantasy football app

Rank the current FBS season with a licensed player leaderboard and a transparent standard-PPR formula. The useful first release is a board, not a full roster platform: identity, injuries and games-played coverage still impose real limits.

API coverage and endpoint overview: Explore the NCAAF API

Why this is hard

College fantasy data needs an honest minimum product

A season-total leader board is buildable now. Draft-room headshots, injury filtering, per-game qualification and reliable player drill-down are not all available, so the first version should be scoped around the board the endpoint can support.

  • The licensed NCAAF leader route currently carries season 2026 only.
  • fantasy_points_ppr is computed by Big Balls from served box-score totals; the route also exposes the component passing, rushing and receiving fields.
  • The source does not provide games_played, so there is no min_games filter and totals cannot be converted honestly to per-game averages here.
  • player.id is a canonical Big Balls UUID when resolved, but it is nullable. Production measurement on 2026-09-14 found 0 of the default top 20 bridged, so name-based drill-down is not an acceptable fallback.

Path 1 · recommended

Have your AI agent build it

The leaderboard has no MCP tool, so use REST for the board. MCP can inspect coverage and find canonical players, but a name match must never overwrite a null id from the leaderboard.

1. Connect the MCP server

Add the server in your MCP client and sign in with your Big Balls account. Your client registers itself and handles the token exchange — there is no key to copy.

MCP client configjson
{
  "mcpServers": {
    "bigballs-sports-data": {
      "url": "https://mcp.bigballsdata.com/mcp"
    }
  }
}

2. Tools your agent gets

  • find_players

    Find canonical player IDs by name for use with player-stat tools.

  • get_player_stats

    Per-player career or per-season totals and per-game rates.

  • get_coverage

    Machine-readable map of what we hold and what we do not.

3. Ask for what you want

Paste this at your agent. It is written to make the model check coverage before it designs anything, which is what stops it inventing a field we do not serve.

Prompttext
Build a college fantasy football season leaderboard.

Use get_coverage for american_football, then call
GET /v1/ncaaf/leaders?stat=fantasy_points_ppr&season=2026&limit=100 directly.
Display rank, player name, position, team and fantasy_points_ppr.

Do not calculate points per game because this surface has no games_played.
Do not add injuries or headshots; neither has a licensed served NCAAF surface.
player.id can be null. Enable a player-history link only when it is non-null,
and never guess an id by taking the first same-name result from find_players.

What the agent cannot reach

There is no MCP tool for /v1/ncaaf/leaders. The board is 2026-only today, has no games-played qualifier, no licensed headshots and no NCAAF injury feed. Canonical player ids are nullable and currently absent from the default top 20.

Path 2 · hand-coded

Build it yourself

The build stops at a defensible season-total board. It uses no invented game counts, images, injuries or player joins.

  1. 01

    Request the standard-PPR leader board

    Keep season explicit. The route’s default stat is fantasy_points_ppr, but naming it makes the product rule auditable.

    GET /v1/ncaaf/leadersbash
    curl -s "https://api.bigballsdata.com/v1/ncaaf/leaders?season=2026&stat=fantasy_points_ppr&limit=100" \
      -H "x-api-key: $BBS_API_KEY"
  2. 02

    Render season totals without a games filter

    Use value or fantasy_points_ppr as the season total. Do not divide by an assumed games count.

    Leaderboard view modeljavascript
    const board = response.data.leaders.map((row) => ({
      rank: row.rank,
      playerId: row.player.id,
      name: row.player.name,
      position: row.player.position,
      team: row.team.name,
      ppr: row.fantasy_points_ppr,
    }));
  3. 03

    Handle unresolved player identity

    A null id means the bridge has not resolved that athlete. Disable drill-down rather than matching the first identical name.

    Null-safe player linkjavascript
    function playerHref(row) {
      return row.playerId ? `/players/${row.playerId}` : null;
    }
  4. 04

    Offer alternate statistical boards

    The same route can rank allowed passing, rushing, receiving and defensive totals. Keep the selected stat in the heading.

    Receiving-yards boardbash
    curl -s "https://api.bigballsdata.com/v1/ncaaf/leaders?season=2026&stat=receiving_yards&limit=50" \
      -H "x-api-key: $BBS_API_KEY"

Reference

Every endpoint this tutorial uses

All on the gateway at api.bigballsdata.com. Verified against production on 2026-09-14.

MethodPathReturnsWhy you need itPlan
GET/v1/ncaaf/leaders?season=2026&stat=fantasy_points_pprSeason-total PPR ranking plus component passing, rushing, receiving and defensive totalsThe primary board. Big Balls computes standard PPR over the licensed raw totals. Read the endpoint guide.Only 2026 is populated today. There is no games_played field or min_games parameter.Free
GET/v1/players/:id/stats?sport=american_football&season=Per-player season or game stat rows when a canonical player id is knownOptional drill-down only for leader rows whose player.id is non-null. Review authenticated calls.Do not fall back to a name join. On the default board, production measurement found 0 of 20 ids resolved on 2026-09-14.Free

Pricing, honestly

The free tier is enough; canonical identity is the constraint

Both the current leaderboard and historical player-stat queries are Free. A drill-down works only when a leader row carries a canonical player id; the current identity gap, not a paid tier, is the constraint.

Free leaderboard

texttext
GET /v1/ncaaf/leaders?season=2026&stat=fantasy_points_ppr&limit=100

Free drill-down (resolved ids only)

texttext
GET /v1/players/{resolvedPlayerId}/stats?sport=american_football&season={historicalSeason}
  • Free builds the complete current season-total ranking returned by the leaderboard.
  • Historical player drill-down is also Free, but useful only for rows with a non-null canonical id.
  • An upgrade does not create games-played, injuries or licensed headshots; those remain data requirements.

More tutorials

Other build guides

Known gaps

What the fantasy board cannot promise yet

These gaps define the first release and the data work required for a fuller roster product.

  • Canonical identity needs broader bridge coverage. On 2026-09-14, 42.0% of distinct 2026 source athletes bridged overall, but 0 of the default top 20 did.
  • The season surface has no games_played field, so per-game averages and minimum-game qualification are not supportable.
  • There is no served NCAAF injury feed and no licensed college headshot source.
  • Only season 2026 is populated in the licensed leaderboard today.

Questions

Is fantasy_points_ppr supplied by the source?
No. Big Balls computes standard PPR from licensed passing, rushing and receiving totals: 0.04 per passing yard, 4 per passing touchdown, -2 per interception, 0.1 per rushing or receiving yard, 6 per rushing or receiving touchdown, and 1 per reception.
Can I rank players by points per game?
Not from this leaderboard. The source carries no games-played field, so dividing by a guessed count would be misleading.
Can every leader open a player detail page?
No. player.id is nullable, and the default top 20 had no resolved ids in the 2026-09-14 production measurement. Disable the link when the id is null.
Are NCAAF injuries and headshots available?
No. There is no served NCAAF injury feed, and the college headshots held internally are not licensed for redistribution.
Which seasons does the NCAAF leaderboard cover?
Season 2026 only today. The route resolves an omitted season to the newest populated season, but this guide keeps 2026 explicit.

Ship the honest season-total board

Start with the free 2026 leaderboard and make every unavailable enrichment visibly unavailable instead of guessed.