API Reference
REST endpoints for accessing player/team data and ML feature vectors. Designed for direct integration with ML pipelines, analytics dashboards, and automated workflows.
Base URL
https://philsdata.com
Response Format
All endpoints return JSON. Successful responses use 200 with the result body. Errors use 4xx with { "error": "..." }.
Authentication
Vector endpoints require an authenticated session (cookie-based, 30-day persistence). Sign up via /signup or log in via /login. The browser session cookie is automatically attached to API requests from the same domain.
Feature Vectors
Position-specific ML-ready vectors. QBs get 13 dimensions, RBs get 12, WR/TE get 11, teams get 10. Each vector includes both normalized and raw values plus metadata.
Normalization
Values are normalized using sensible per-feature scales (e.g., passer_rating / 158.3 gives a 0–1 score). Most values fall in [0, 1] but exceptional outliers can exceed 1.
Sample Size
Always check meta.sampleSize (games played in season). Vectors built from <3 games are high-variance — consider downweighting in your model.
Cosine Similarity
Two players' vectors can be compared via cosine similarity. The in-app Compare view computes this automatically for selected players.
/api/signupCreate Account
Register a new user with email and password. Password is hashed with bcrypt (12 rounds) before storage.
Request Body
| Name | Type | Description |
|---|---|---|
emailrequired | string | Unique email address. Used as login identifier. |
passwordrequired | string | Minimum 8 characters. Hashed before storage. |
name | string | Display name (optional). |
Response 200
{
"user": {
"id": "01H8X2Z...",
"email": "you@example.com"
}
}Error Responses
400Missing email/password or password < 8 chars409Account with this email already exists500Server error during account creationCode Examples
curl -X POST https://philsdata.com/api/signup \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com",
"password": "your-secure-password",
"name": "Your Name"
}'const res = await fetch("https://philsdata.com/api/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "you@example.com",
password: "your-secure-password",
name: "Your Name",
}),
});
const data = await res.json();/api/auth/callback/credentialsSign In (Credentials)
Authenticate via email/password. Returns a JWT session cookie valid for 30 days. Managed by Auth.js v5.
Request Body
| Name | Type | Description |
|---|---|---|
emailrequired | string | Account email |
passwordrequired | string | Account password |
csrfTokenrequired | string | Obtain via GET /api/auth/csrf |
Response 200
// Sets HttpOnly cookie: __Secure-authjs.session-token
// Redirects to callback URL on success
{
"url": "/app"
}signIn() helper from next-auth/react rather than calling this endpoint directly. See the Auth.js docs for details./api/vectors/players/{playerId}Auth requiredPlayer Feature Vector
Returns a position-specific ML-ready feature vector for a player in a given season. Vectors are normalized to roughly [0, 1] and include raw values + metadata.
Path Parameters
| Name | Type | Description |
|---|---|---|
playerIdrequired | uuid | The player's UUID. Find these in the URL when viewing a player's detail page. |
Query Parameters
| Name | Type | Description |
|---|---|---|
season | integer | Season year (1950-2026) Default: 2026 |
Response 200
{
"player": {
"id": "abc12345-...",
"name": "Patrick Mahomes",
"position": "QB",
"team": "..."
},
"vector": {
"features": [
"pass_ypg", "pass_td_rate", "int_rate", "rush_ypg",
"td_int_ratio", "passer_rating", "pass_consistency",
"pass_trend_slope", "best_yards", "worst_yards",
"fumble_rate", "sack_rate", "games_played"
],
"values": [
0.815, 0.733, 0.433, 0.512, 0.692, 0.658, 0.834,
0.012, 0.978, 0.234, 0.176, 0.211, 1.000
],
"raw": [
285.3, 2.2, 0.65, 25.0, 3.38, 104.2, 0.834,
0.4, 489, 117, 0.18, 1.06, 17
],
"meta": {
"subject": "Patrick Mahomes",
"subjectId": "abc12345-...",
"season": 2026,
"position": "QB",
"sampleSize": 17
}
}
}Response Fields
| Name | Type | Description |
|---|---|---|
vector.features | string[] | Position-specific feature names. 13 for QB, 12 for RB, 11 for WR/TE. |
vector.values | number[] | Normalized values (typically [0, 1] using sensible per-feature scales). |
vector.raw | number[] | Pre-normalization values in original units (yards, TDs/game, etc.). |
vector.meta.sampleSize | integer | Number of games played in this season (use to weight reliability). |
Error Responses
401Not authenticated404Player not found OR no data for given seasonCode Examples
curl https://philsdata.com/api/vectors/players/abc12345-...?season=2026const res = await fetch(
"https://philsdata.com/api/vectors/players/abc12345-...?season=2026"
);
const { player, vector } = await res.json();
// vector.values is your ML model input
console.log(vector.values);import requests
resp = requests.get(
"https://philsdata.com/api/vectors/players/abc12345-...",
params={"season": 2026},
)
data = resp.json()
features = data["vector"]["features"]
values = data["vector"]["values"]
# Feed into your model
import numpy as np
X = np.array(values).reshape(1, -1)/api/vectors/teams/{teamId}Auth requiredTeam Feature Vector
Returns a 10-dimensional feature vector for a team in a given season. Includes win%, points/game, point differential, yards splits, turnover rate, third-down/red-zone efficiency.
Path Parameters
| Name | Type | Description |
|---|---|---|
teamIdrequired | uuid | The team's UUID. Find these in the URL when viewing a team's detail page. |
Query Parameters
| Name | Type | Description |
|---|---|---|
season | integer | Season year (1950-2026) Default: 2026 |
Response 200
{
"team": {
"id": "def67890-...",
"name": "Kansas City Chiefs",
"abbreviation": "KC"
},
"vector": {
"features": [
"win_pct", "ppg", "papg", "point_diff_pg",
"ypg", "pass_yards_pct", "rush_yards_pct",
"turnovers_pg", "3rd_down_pct", "red_zone_pct"
],
"values": [
0.882, 0.834, 0.501, 0.580, 0.812,
0.602, 0.310, 0.350, 0.452, 0.621
],
"raw": [
0.882, 29.2, 17.5, 11.7, 365.4,
0.602, 0.310, 1.05, 45.2, 62.1
],
"meta": {
"teamId": "def67890-...",
"teamAbbr": "KC",
"season": 2026
}
}
}Error Responses
401Not authenticated404Team not foundCode Examples
curl https://philsdata.com/api/vectors/teams/def67890-...?season=2026const res = await fetch(
"https://philsdata.com/api/vectors/teams/def67890-...?season=2026"
);
const { team, vector } = await res.json();import requests
resp = requests.get(
"https://philsdata.com/api/vectors/teams/def67890-...",
params={"season": 2026},
)
data = resp.json()
print(data["team"]["name"], data["vector"]["values"])/api/syncDatabase Sync (Admin)
Reseed the database with the latest historical data. Idempotent by default — pass force=true to wipe and regenerate everything. Used internally for data refreshes.
Query Parameters
| Name | Type | Description |
|---|---|---|
force | boolean | If true, wipe all data first. Otherwise, only inserts missing rows. Default: false |
seasons | string | Comma-separated list of seasons to process. Defaults to all 1950-2026. |
standingsOnly | boolean | Skip per-week stats generation. Faster for standings-only refresh. Default: false |
Response 200
{
"success": true,
"message": "Database wiped and reseeded",
"results": {
"cleared": true,
"teams": 32,
"players": 462,
"standings": 2464,
"seasonsProcessed": 77
}
}Error Responses
500Server error during syncCode Examples
curl -X POST "https://philsdata.com/api/sync?force=true"npm run seed:force to bypass the timeout entirely.Most users won't need to hit the API directly. The same data powers these in-app views:
Backed by PostgreSQL via Neon. 8 tables (4 auth + 4 sports data).
-- Auth tables (managed by Auth.js Drizzle adapter)
users (id, name, email, hashed_password, created_at)
accounts (user_id, type, provider, provider_account_id)
sessions (session_token, user_id, expires)
verification_tokens (identifier, token, expires)
-- Sports data
teams (id, name, abbreviation, conference, division,
primary_color, secondary_color, logo_url)
players (id, team_id, name, position, jersey_number,
height, weight, college, status, birth_year,
rookie_year, retired_year, image_url, bio, era)
games (id, season, week, home_team_id, away_team_id,
home_score, away_score, date, venue, status)
player_stats (id, player_id, game_id, passing_yards,
passing_tds, interceptions, rushing_yards,
rushing_tds, receiving_yards, receiving_tds,
receptions, targets, fumbles, sacks, tackles)
team_stats (id, team_id, game_id, total_yards, passing_yards,
rushing_yards, turnovers, penalties, penalty_yards,
third_down_conv, red_zone_conv, time_of_possession)
standings (id, team_id, season, wins, losses, ties,
points_for, points_against, division_rank,
conference_rank)