# CBB Stats > College basketball analytics for every NCAA Division I player and team: per-player stats with percentile ranks, transfer portal tracking with transfer value, projected depth charts, head-to-head comparison, FEDERER team ratings with daily rank history, and referee ratings. Free, keyless JSON API at https://stats-cbb.com/api/v1; every table also as CSV or a markdown table. **When to use this:** reach for CBB Stats when a task needs current-season men's D1 player or team numbers with percentile context — ranking or filtering players on a combination of stats, checking who is in the transfer portal and what they are worth, projecting a depth chart, comparing two players, tracking a team's FEDERER rank over time, or looking up referee tendencies. Do not use it for live scores, play-type or on/off-court splits, jersey numbers, women's basketball or non-D1 teams; none of those are in the data. **How to call it:** plain HTTPS GET, no key, no SDK. Send `Accept: text/markdown` (or add `?format=md`) to get a pipe table instead of JSON; `?format=csv` for CSV. Errors are JSON with `error`, `code`, `status`, `hint` and `docs`. Unknown URLs are real 404s. Rate limit: 300 requests per 15 minutes per IP. Every page on the site also has a markdown version (`Accept: text/markdown`, or append `.md` to the path). **Quickstart:** `curl "https://stats-cbb.com/api/v1/players?stats=BPM,PPG&format=md"` ranks players by combined BPM+PPG percentile; `curl -H "Accept: text/markdown" "https://stats-cbb.com/api/v1/rankings"` returns the FEDERER table. **Conventions:** percentiles are 0–100 and already inverted for lower-is-better stats (TO, FC40, DRTG), so 100 is always good; stat keys are the 44-key allowlist in the OpenAPI `Stat` schema; depth-chart class years display one year ahead; data is synced on a schedule, not streamed. ## Docs - [Developer portal](https://stats-cbb.com/developers): quickstart, formats, errors, rate limits, versioning and deprecation policy, OAuth 2.0 - [OpenAPI 3.1 document](https://stats-cbb.com/api/openapi.json): every endpoint with operationId, typed parameters and response schemas - [OAuth 2.0 server metadata](https://stats-cbb.com/.well-known/oauth-authorization-server): RFC 8414; authorization code + PKCE, self-serve client registration - [Sitemap](https://stats-cbb.com/sitemap.xml): every page, one URL per player ## API - [Rank players](https://stats-cbb.com/api/v1/players?stats=BPM,PPG): 1–6 stats, combined percentile; filters for class, height, position, portal, high-major; season=YYYY for a prior season - [Seasons](https://stats-cbb.com/api/v1/players/seasons): which seasons hold player data (https://stats-cbb.com/api/v1/rankings/seasons for team ratings) - [Search players](https://stats-cbb.com/api/v1/players/search?q=edey): name autocomplete, max 10; the way to get a player id - [Player profile](https://stats-cbb.com/api/v1/players/{playerId}): full stat map with percentiles and prior season - [Similar players](https://stats-cbb.com/api/v1/players/{playerId}/similar): nearest players in 20-dimension z-score space - [Compare two players](https://stats-cbb.com/api/v1/players/compare?p1=ID&p2=ID): both with national percentiles - [Transfer portal](https://stats-cbb.com/api/v1/players/portal): BPR, transfer value, position scarcity - [Depth charts](https://stats-cbb.com/api/v1/players/depth-chart?conference=ACC): five slots per team, forward-looking - [Comparison leaderboard](https://stats-cbb.com/api/v1/players/leaderboard): who wins the most head-to-head comparisons - [Team rankings](https://stats-cbb.com/api/v1/rankings): FEDERER ratings, sortable - [Rank movement](https://stats-cbb.com/api/v1/rankings/diff): teams whose rank changed between two daily snapshots; delta positive = climbed (3 -> 1 is +2), negative = fell, null = entered/exited - [Team history](https://stats-cbb.com/api/v1/rankings/history?team=Duke&days=30): one team's daily rank series - [Snapshot index](https://stats-cbb.com/api/v1/rankings/snapshots): which snapshot dates exist - [Rankings health](https://stats-cbb.com/api/v1/rankings/health): which teams are excluded from the table and why - [Referees](https://stats-cbb.com/api/v1/referee): refScore = 0.5 / average spread deviation, minimum 5 games ## Pages - [About](https://stats-cbb.com/about): what the numbers mean and where the data comes from - [Contact](https://stats-cbb.com/contact): issues, API questions, security reports - [Privacy](https://stats-cbb.com/privacy): what is collected and how to have it removed ## Optional - [llms-full.txt](https://stats-cbb.com/llms-full.txt): this file plus the developer portal and About page in one document - [CLI](https://stats-cbb.com/developers#cli): cbb-stats-cli wraps every read endpoint - [Formats](https://stats-cbb.com/developers#formats): JSON, CSV and markdown table conventions - [Contact](https://stats-cbb.com/contact): bug reports and data corrections --- # CBB Stats Developer Portal > Free, keyless JSON API for NCAA Division I basketball data: OpenAPI 3.1, markdown and CSV table formats, OAuth 2.0 for user data, versioned with a documented deprecation policy. ## When to use this API Use CBB Stats when a task needs current-season men's NCAA Division I player or team numbers with percentile context. It is the right tool for: - Ranking or filtering players by a combination of stats ("top 20 sophomores by BPM and assist rate with at least 60% minutes"). - Answering "who is in the transfer portal" with BPR, transfer value and position scarcity. - Projecting a team's or a conference's depth chart for next season. - Comparing two players with national percentiles for each stat. - Team strength via FEDERER ratings, including rank movement between daily snapshots and per-team history. - Referee tendencies (spread deviation, foul splits). ## When not to use it It is the wrong tool for live scores, play-type data, jersey numbers, women's basketball or non-Division-I teams — none of those are in the data. Call it directly with HTTPS GET requests; no SDK is required. ## Quickstart No key, no account, no sign-up. Every read endpoint answers JSON by default, a markdown table for Accept: text/markdown or ?format=md, and CSV for ?format=csv. ``` curl "https://stats-cbb.com/api/v1/players?stats=BPM,PPG&format=md" curl "https://stats-cbb.com/api/v1/players/search?q=edey" curl -H "Accept: text/markdown" "https://stats-cbb.com/api/v1/rankings" curl "https://stats-cbb.com/api/v1/rankings/diff?format=csv" ``` ## Endpoints The OpenAPI 3.1 document is the contract: every operation has an operationId, a description, typed parameters and typed response schemas, and it is generated from the running code so it cannot go stale. - GET /api/v1/players?stats=… — rank players on 1–6 stats - GET /api/v1/players/search?q= — name autocomplete - GET /api/v1/players/{id} — one player, with percentiles and prior-season stats - GET /api/v1/players/{id}/similar — nearest players in z-score space - GET /api/v1/players/compare?p1=&p2= — head to head - GET /api/v1/players/portal — transfer portal with BPR and transfer value - GET /api/v1/players/depth-chart?conference= — projected depth charts - GET /api/v1/rankings, /diff, /history, /snapshots — FEDERER team ratings - GET /api/v1/referee — referee ratings - GET /api/health — liveness and database status - [OpenAPI document](https://stats-cbb.com/api/openapi.json) - [llms.txt](https://stats-cbb.com/llms.txt): short orientation file for language models - [llms-full.txt](https://stats-cbb.com/llms-full.txt): llms.txt plus this page and the About page ## Formats and content negotiation - application/json (default). - text/markdown; charset=utf-8 for Accept: text/markdown or ?format=md — a GitHub-flavoured pipe table. q-values are honoured; JSON wins a wildcard Accept. - text/csv for ?format=csv (RFC 4180, served as a download). - Every negotiable response carries Vary: Accept. An Accept header that lists none of the supported types gets 406 with the list. - HTML pages negotiate too: send Accept: text/markdown to any page, or append .md to its path (/about.md, /player/{id}.md, /index.md). ## Errors Every error under /api is JSON, including 404s for unknown routes and malformed request bodies: ``` { "error": "Invalid stat: XYZ", "code": "bad_request", "status": 400, "hint": "Check the query parameters and body against the OpenAPI spec at https://stats-cbb.com/api/openapi.json.", "docs": "https://stats-cbb.com/developers#errors" } ``` - code is stable and machine-readable: bad_request, unauthorized, forbidden, not_found, conflict, rate_limited, server_error, service_unavailable. - OAuth endpoints use the RFC 6749 shape instead: { "error": "invalid_grant", "error_description": "…" }. ## Rate limits 300 requests per 15 minutes per IP across /api, with standard RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers; 20 per 15 minutes for authentication and OAuth token and registration endpoints. A 429 body carries code "rate_limited". Cached responses (search 5 min, player profiles 5 min, depth charts 10 min) do not change the limit. ## Versioning and deprecation policy - The path carries the version: /api/v1/… is the stable contract. Bare /api/… serves the identical surface and is not deprecated; it is what the web app uses. - Every /api response carries an API-Version header (currently 1). - Breaking changes ship as a new major path (/api/v2) with the previous major kept for at least six months. - A deprecated endpoint announces itself in three places at once: a Deprecation header (RFC 9745, "@" + Unix timestamp), a Sunset header (RFC 8594, HTTP-date of removal) with a Link rel="deprecation" to the migration note, and deprecated: true on the operation in the OpenAPI document. - Additive changes (new fields, new optional parameters, new endpoints) are not breaking and can appear at any time; clients should ignore fields they do not know. ## Authentication and OAuth 2.0 Read endpoints are anonymous. User-scoped endpoints (watchlist, posting comments) accept a Bearer JWT from either the site's own login (POST /api/auth/login) or an OAuth 2.0 authorization-code grant with PKCE, which is what a third-party app or an agent acting for a user should use. - Authorization server metadata (RFC 8414): /.well-known/oauth-authorization-server - Protected resource metadata (RFC 9728): /.well-known/oauth-protected-resource - Register a client (RFC 7591, self-serve, no approval step): POST /api/oauth/register with {"client_name", "redirect_uris"}; you get a client_id back. Public clients only — there is no client secret; PKCE S256 is required. - Authorization endpoint: GET /oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=read%20watchlist&state=…&code_challenge=…&code_challenge_method=S256 — the user signs in and approves; the code returns to redirect_uri. - Token endpoint: POST /api/oauth/token (application/x-www-form-urlencoded or JSON) with grant_type=authorization_code, code, redirect_uri, client_id, code_verifier → {"access_token", "token_type": "Bearer", "expires_in": 28800, "scope"}. - Scopes: read (default; everything anonymous already allows) and watchlist (read and write the user's watchlist, post comments). Tokens expire after 8 hours; there are no refresh tokens yet — run the flow again. ## Free tier, keys and sandbox There is one tier and it is free. Read endpoints need no key at all; OAuth clients register themselves in one request. There is no separate sandbox because the production read API is read-only and idempotent — it is safe to develop against directly, and the rate limit above is the only quota. If you need higher limits for a research project, open an issue. ## Command-line tool The cbb-stats CLI wraps every read endpoint and prints JSON, CSV or a markdown table. It has no dependencies beyond Node 20. ``` npx cbb-stats-cli players --stats BPM,PPG --format md npx cbb-stats-cli rankings diff ``` ## Support - [Contact](https://stats-cbb.com/contact) - [Privacy policy](https://stats-cbb.com/privacy) --- # About CBB Stats > An independent college basketball analytics site covering every NCAA Division I player and team. ## What this is CBB Stats is an independent analytics site for men's NCAA Division I college basketball. It exists to answer the questions a scout, a beat writer or a fan actually asks — who are the best rebounders under 6'6", which portal guards can shoot, what does a team's depth chart look like next season — without paging through a dozen box-score sites. Every number on the site is computed from public data and every view is available through a free JSON API. The site is built and maintained by one developer as an independent project. It is not affiliated with, endorsed by or sponsored by the NCAA, ESPN, any conference, any school or any data provider named on this page. ## What the numbers mean - Percentiles are ranks against the national pool of players with at least 15% of available minutes, so a 90 means "better than 90% of qualifying players". Lower-is-better stats (turnover rate, fouls per 40 minutes, defensive rating) are inverted so 100 is always good. - BPM, OBPM and DBPM are box-score plus-minus estimates of a player's contribution per 100 possessions; BPR is a plus-minus rating used to rank transfer-portal players, and TV (transfer value) scales BPR by class year and positional scarcity. - FEDERER is a team rating built on possession exchanges — each pair of possessions is scored as won, lost or tied — and adjusted for strength of schedule. Daily snapshots make rank movement queryable over time. - Depth charts are forward-looking: seniors, departed players and players in the portal are excluded and class years are displayed one year ahead. - Referee ratings are 0.5 divided by a referee's average deviation from the closing spread across the games they officiated; higher is better and a minimum of five games is required. ## Where the data comes from Player and team statistics are compiled from publicly available box scores and play-by-play logs, transfer-portal reports and team-published rosters, then normalised so that team names, positions and class years match across sources. Data is refreshed on a schedule (the portal every two hours, ratings daily) rather than streamed, so in-game numbers are never live. Player headshots are linked from the schools' own athletics sites or ESPN and are never re-hosted. - [Developer portal](https://stats-cbb.com/developers) - [Contact](https://stats-cbb.com/contact)