{"openapi":"3.1.0","info":{"title":"FSM Scouting API","version":"1.0.0","description":"Read-only FRC scouting metrics for the 2026 season: FSM ratings, component breakdowns, defense scores and model-backed match predictions.\n\nAll numeric metrics are JSON numbers rounded to 2 decimals. Team keys are returned in TBA form (frc846); both '846' and 'frc846' are accepted as input."},"servers":[{"url":"/","description":"Same origin"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Server-to-server key. Request one at /developers."}},"schemas":{"TeamMetrics":{"type":"object","properties":{"team_key":{"type":"string","example":"frc846"},"team_number":{"type":["integer","null"],"example":846},"rank":{"type":["integer","null"],"description":"Event rank; null before ranking exists."},"fsm":{"type":"number"},"fuel":{"type":"number"},"climb":{"type":"number"},"auto":{"type":"number"},"penalty":{"type":"number","description":"2026: own-alliance foul points. Higher is worse; subtracted in the FSM blend."},"defense_score":{"type":["number","null"],"description":"Quals only. Points suppressed from opponents vs prediction, likelihood-weighted. null when no matches played."},"predicted":{"type":"boolean","description":"true when the FSM is a carryover forecast, not a measurement."}}},"Alliance":{"type":"object","properties":{"teams":{"type":"array","items":{"type":"string"}},"predicted_score":{"type":["number","null"]},"actual_score":{"type":["number","null"],"description":"null when unplayed."}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}},"paths":{"/api/v1/rankings":{"get":{"summary":"2026 FSM leaderboard","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"page_size","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"country","in":"query","schema":{"type":"string"}},{"name":"state_prov","in":"query","schema":{"type":"string"}},{"name":"min_fsm","in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"Ranked teams"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}}}}},"/api/v1/teams/{team}":{"get":{"summary":"One team's 2026 season profile","parameters":[{"name":"team","in":"path","required":true,"schema":{"type":"string"},"description":"Team number or TBA key (846 or frc846)."}],"responses":{"200":{"description":"Team profile"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}}}}},"/api/v1/events":{"get":{"summary":"All 2026 events with strength metrics","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"page_size","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"week","in":"query","schema":{"type":"integer"}},{"name":"district","in":"query","schema":{"type":"string"}},{"name":"country","in":"query","schema":{"type":"string"}},{"name":"state_prov","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Events"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}}}}},"/api/v1/events/{event}":{"get":{"summary":"One 2026 event","parameters":[{"name":"event","in":"path","required":true,"schema":{"type":"string"},"description":"Event code, with or without the 2026 prefix (casj or 2026casj)."}],"responses":{"200":{"description":"Event"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}}}}},"/api/v1/events/{event}/teams":{"get":{"summary":"Scouting table for an event","description":"Every attending team with FSM, components, penalty and defense_score.","parameters":[{"name":"event","in":"path","required":true,"schema":{"type":"string"},"description":"Event code, with or without the 2026 prefix (casj or 2026casj)."},{"name":"sort","in":"query","schema":{"type":"string","enum":["rank","fsm","defense_score","team_number"],"default":"rank"}}],"responses":{"200":{"description":"Team metrics","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"event_key":{"type":"string"},"played_matches":{"type":"integer"},"defense_available":{"type":"boolean"},"teams":{"type":"array","items":{"$ref":"#/components/schemas/TeamMetrics"}}}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}}}}},"/api/v1/events/{event}/matches":{"get":{"summary":"Match predictions for an event","parameters":[{"name":"event","in":"path","required":true,"schema":{"type":"string"},"description":"Event code, with or without the 2026 prefix (casj or 2026casj)."},{"name":"played","in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Match predictions"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request","unauthorized","not_found","rate_limited","upstream_unavailable","service_unavailable","internal_error"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}}}}}}}