Skip to content

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
  1. POST creates a job → { "jobId": "…", "status": "pending" }
  2. GET status poll until status: "completed" and downloadReady: true
  3. 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.