# KickoffAPI — Dart SDK

One file, `dart:io` only, no pub dependencies. Dart 2.12+ / Flutter.

```dart
import 'package:kickoffapi/kickoffapi.dart';

final api = KickoffApi('YOUR_API_KEY');   // free key: https://kickoffapi.com/portal/

final live = await api.live_scores();              // { data: [...], meta: {...} }
final epl  = await api.standings({'league': 'lg_8K3mZ0pQ', 'season': 2025});
final fx   = await api.fixture('fx_99Xy');

api.close();   // releases the pooled sockets
```

Talks to **v2** by default (`https://api.kickoffapi.com/api/v2`), so every method
returns the unified `{ data, meta }` envelope with v2's native ids (`lg_…`,
`tm_…`, `fx_…`). Pass `baseUrl:` to the constructor for a different API version.
See the [v2 reference](https://docs.kickoffapi.com/api-v2-reference.html) or the
[v1 reference](https://docs.kickoffapi.com/api-v1-reference.html) (legacy,
sunsets 01 Jan 2027).

## Methods

One per v2 endpoint, named exactly as the Python client names it
(`live_scores()`, `head_to_head()`, `xg_batch()`, …) so the same call reads the
same in every official SDK — which is why these are snake_case rather than Dart's
usual lowerCamelCase. `get()`/`post()` are there for anything without a named
method yet.

```dart
await api.countries({'limit': 50});
await api.leagues({'country': 'England'});
await api.fixtures({'league': 'lg_8K3mZ0pQ', 'season': 2025, 'limit': 20});
await api.fixture_events('fx_99Xy', {'refresh': true});
await api.top_scorers({'league': 'lg_8K3mZ0pQ', 'season': 2025});
await api.odds({'fixture': 'fx_99Xy'});
await api.xg({'x': 110, 'y': 38, 'bodyPart': 'Right Foot', 'situation': 'Open Play'});
await api.xg_batch([
  {'x': 110, 'y': 38, 'bodyPart': 'Right Foot', 'situation': 'Open Play'},
  {'x': 118, 'y': 41, 'bodyPart': 'Head', 'situation': 'Penalty'},
]);
await api.resolve({'entity': 'team', 'legacyId': 33});
await api.account_status();
```

## Errors

Every non-2xx response throws `KickoffApiException`, which carries the status and
the message the API sent back. A 429 also carries the `Retry-After` wait:

```dart
try {
  final fx = await api.fixture('fx_does_not_exist');
} on KickoffApiException catch (e) {
  print(e.status);   // 404
  print(e.message);  // fixture not found
}

try {
  await api.countries({'limit': 5000});
} on KickoffApiException catch (e) {
  if (e.status == 429 && e.retryAfter != null) {
    await Future.delayed(Duration(seconds: e.retryAfter!));
  }
}
```

Spec: `/openapi-v2.yaml`.
