A typed Dart client for the Statbotics API v3: EPA statistics, events, event team lists, and match schedules for FRC. Pure Dart, so it works in Flutter apps, CLIs, and servers alike.
import 'package:statbotics_client/statbotics_client.dart';
final client = StatboticsClient();
try {
final events = await client.getEvents(2026);
final teams = await client.getEventTeams('2026txhou');
} finally {
client.close();
}No API key needed. Transient server errors are retried with a pluggable sleep (inject a no-op in tests); non-transient failures throw StatboticsApiException with the status code.
StatboticsClient targets https://api.statbotics.io/v3. It needs no auth key.
Single-object endpoints return null on 404; list endpoints return an empty
list. Anything else outside 2xx throws StatboticsApiException. Remember to
call close() when you are done so the underlying HTTP client is released.
| Method | Endpoint | Returns |
|---|---|---|
getEvent(eventKey) |
GET /event/{eventKey} |
StatboticsEvent? |
getEvents(year) |
GET /events?year={year} |
List<StatboticsEvent> |
getEventTeams(eventKey) |
GET /team_events?event={eventKey} |
List<StatboticsTeamEvent> |
getTeamEvents(team, {year}) |
GET /team_events?team={team}[&year={year}] |
List<StatboticsTeamEvent> |
getTeamYears(team) |
GET /team_years?team={team} |
List<StatboticsTeamYear> |
getEventTeamsBasic(eventKey) |
GET /teams?event={eventKey} |
List<StatboticsTeamBasic> |
getEventMatches(eventKey) |
GET /matches?event={eventKey} |
List<StatboticsMatch> |
getEventTeamssorts results by rank ascending.getTeamEventssorts results newest season first, then by event key, so a team's most recent results lead. Passyearto narrow to one season.getTeamYearssorts results newest season first, one row per season the team competed in. Compare seasons onepa.unitlessorepa.norm, notepa.totalPoints: point values belong to a season's game, so a 2002 total of 16.6 and a 2025 total of 92.77 say nothing about which robot was better.getEventssorts results by week then name.getEventMatchessorts results by comp level (qm,ef,qf,sf,f) then match number.getEventTeamsBasicreturns basic team info (number + nickname) and returns an empty list if the endpoint is unavailable.
The API is public and read-only, so models only decode the fields each endpoint
exposes (EPA and the per-phase breakdown on StatboticsTeamEvent and
StatboticsTeamYear, alliance teams on StatboticsMatch, dates and location
on StatboticsEvent). Each model also round-trips through toJson for
caching.
StatboticsEpa.totalPoints is the point estimate the API reports at
epa.total_points, not an average over matches. The per-phase measures
(autoPoints, teleopPoints, endgamePoints) come from epa.breakdown and
are null for seasons predating that game's scoring split, so treat every EPA
field as nullable.
Model tests assert against captured live response bodies in test/fixtures/,
not only hand-written maps, so a change to the API's shape fails a test rather
than reaching a consumer. Refresh a fixture by re-running the curl in the
comment above _fixture in the test file.
The Statbotics API returns occasional 500s under load. StatboticsClient
retries transient responses (HTTP 429 and any 5xx) up to maxAttempts times
(default 3) with exponential backoff (300 * attempt^2 ms) before surfacing
the error. 404 and other 4xx are returned or thrown immediately, with no retry.
final client = StatboticsClient(
maxAttempts: 5,
sleep: (duration) async {
// use your own sleeper (e.g. a fake clock in tests)
},
);AGPL-3.0