Data API reference¶
All paths are relative to {API_V1_PREFIX} (default /api). Implementation: src/api/features/data_api/router.py.
Pagination¶
List endpoints return:
{
"data": [ … ],
"nextCursor": "<opaque>" | null
}
Pass cursor from nextCursor on the next request until nextCursor is null.
Track cursors encode time|trackUuid. Metric cursors encode time|elementId|classGroupId.
Asset catalog¶
GET /data/assets?limit=100&cursor=<locationId>
Returns zone-scoped locations with nested sources and scans. globalSources (sources without a location) appear on the first page only.
| Param | Default | Max |
|---|---|---|
limit |
100 | 500 |
Metadata¶
GET /data/class-groups
GET /data/class-vocabulary
GET /data/metrics/definitions
- class-groups — configured group labels and member mappings (404 if not configured).
- class-vocabulary — mod registry class names.
- metrics/definitions — catalog of stored metric names and series kinds.
Tracks¶
List¶
GET /data/scans/{scanId}/tracks
| Param | Description |
|---|---|
from, to |
Time range (defaults to source file duration) |
limit |
Page size (default 1000, max 5000) |
cursor |
Pagination cursor |
since, since_id |
Incremental sync watermark |
class_group_id |
Repeatable filter |
line_first, line_last |
Counting-line filters |
include_plate |
true to include license plate fields |
Detail¶
GET /data/scans/{scanId}/tracks/{trackUuid}
Full payload: events, trajectory points, attributes. imageUrl and plateImageUrl are path-relative under {API_V1_PREFIX}/media/{scanId}/{trackId} (media GET does not require auth).
Streaming export¶
GET /data/scans/{scanId}/tracks/export?format=csv|ndjson
Same filters as the list endpoint. Response is streamed CSV (UTF-8 BOM) or NDJSON.
CSV columns (without plate): ID, Track ID, Time, Class Group ID, Class, Lines, Speed, Make, Model, Image URL. With include_plate=true, adds License Plate and Plate Image URL.
Metrics¶
GET /data/scans/{scanId}/metrics?metric=<name>
| Metric | Description |
|---|---|
density.avg |
Average density |
density.max |
Peak density |
speed.avg |
Average speed |
speed.count |
Speed sample count |
blind.avg |
Blind-zone average |
| Param | Description |
|---|---|
series_kind |
total or class |
from, to |
Time range |
element_id |
Repeatable geometry element filter |
class_group_id |
Repeatable class group filter |
limit |
Default 1000, max 5000 |
cursor |
Pagination cursor |
Async exports (large scans)¶
For very large datasets (1M+ rows), use background jobs:
POST /data/scans/{scanId}/exports/tracks?format=csv|ndjson
GET /data/exports/{jobId}
GET /data/exports/{jobId}/download
- POST creates a job →
{ "jobId": "…", "status": "pending" } - GET status poll until
status: "completed"anddownloadReady: true - GET download returns the file
Job statuses: pending, running, completed, failed.
Error responses¶
Structured errors:
{ "code": "INVALID_ID", "detail": "Invalid scan_id" }
Query validation failures may return plain { "detail": "…" } with HTTP 400.