Statistics and exports¶
To analyse the service over long periods, you do not walk the per-item endpoints: the API exposes bulk outputs, in two shapes.
Two shapes, two uses¶
| Shape | Path | What it returns | For what |
|---|---|---|---|
| Aggregate | /stats/<domain> |
Values already computed over the period | Dashboard, tracked indicator |
| Export | /export/<domain> |
The detail, row by row | Data warehouse, reprocessing |
The choice follows the need: an aggregate saves you from recomputing what the API already knows how to do, an export gives the raw material for analyses the API does not provide.
The eight exports¶
These eight endpoints make up the datahub category, intended to feed a data warehouse.
| Export | Expected parameters |
|---|---|
export/trips |
date (required) |
export/trip-km |
start_date, end_date (required) |
export/passenger-counts |
start_date, end_date (required) |
export/punctuality |
start_date (required), end_date, and optional filters (event: arrival or departure, stop_id, gtfs_id, driver_id, route_id) |
export/trip-tracking |
start_date, end_date (required) |
export/drivers |
archived (optional) |
export/vehicles |
statuses (optional) |
export/alerts |
none |
Three points to read carefully before writing a client:
- The exports do not share the same signature. Some bound a period, one takes a single date, three take none. There is no generic call to write once for all eight.
- Write dates in the compact
YYYYMMDDform. It is the only one all five dated endpoints accept.punctualityandtrip-trackingalso tolerateYYYY-MM-DD, buttrips,trip-kmandpassenger-countsreject it with a validation error. - The perimeter follows the key's rights. A key restricted to certain teams exports fewer rows than expected, with nothing in the response to say so.
All eight endpoints return CSV. The spec nevertheless declares application/json for export/trips: that is a declaration defect, the content served is indeed CSV.
The aggregates¶
The same domains exist in computed form, under /stats/. They bound the period with start_date and end_date in the YYYYMMDD format, except stats/punctuality, which takes from and to as Unix timestamps in seconds. A YYYYMMDD date passed to from or to is rejected there with a 422 error, as is any value before January 1, 2015.
| Endpoint | Purpose |
|---|---|
GET /api/v4/groups/<group_id>/stats/punctuality |
Punctuality aggregated over the period |
GET /api/v4/groups/<group_id>/stats/trip-km |
Kilometres covered |
GET /api/v4/groups/<group_id>/stats/passenger-counts |
Ridership |
GET /api/v4/groups/<group_id>/stats/vehicles-used |
Vehicles committed |
On-demand route details¶
GET /api/v4/groups/<group_id>/export/on-demand-routes/details returns a semicolon-delimited CSV with one row per passenger count, sorted by date then ascending passed_at, plus the rows described below for service sessions and kilometres. start_date and end_date are required in YYYYMMDD format and filter inclusive service dates. route_id and teams are optional; the reader's team restrictions apply as they do to statistics.
The columns, in order, are date, passed_at, route_id, route_short_name, stop_id, stop_name, vehicle, boarding, alighting, tranche_horaire, route_long_name, stop_lat, stop_lon, service_id, service_start, service_end, service_km. date is the service date in YYYY-MM-DD format; passed_at is an ISO 8601 timestamp with the group's timezone offset. Multiple counts at the same stop or instant remain separate rows. tranche_horaire is the local hour of passed_at, lower bound included (08:00-09:00). stop_lat, stop_lon and route_long_name are those of the route as configured today, and the coordinates are empty for a removed stop.
A service session without any count gets a row of its own (empty stop and time, 0 boarding and alighting). service_id, service_start and service_end (local time, empty while the service is open) identify the session of each row. service_km holds the kilometres of the session for a local day, on the first row of that session for that day and empty on the others, so that its sum is never counted twice; a session crossing midnight carries its kilometres on each of the two days, and a day of kilometres with no count gets its own row. Kilometres recorded before sessions were identified come as rows with an empty service_id, one per day, route and device. A session started before the period is listed only through the counts and kilometres it has inside it. An empty period produces only the header.
The vehicle belongs to the service of the same device and route containing the passage: inclusive start, exclusive end; a service without an end remains open, even if it started several days before the exported period. When services overlap, the most recently started one wins. vehicle is empty without a matching service or known vehicle; otherwise it shows the licence plate and fleet number, keeping only the filled parts. A stop removed from its route retains its count with an empty stop_name.
The sums of service_km, boarding and alighting by date and route match export/on-demand-routes with the same filters. Both exports require the same statistics read roles and the group's ON_DEMAND and DEVICE_EVENT_SOURCING features. The download button in Op ships separately.
Asynchronous exports¶
For long periods, an export can be prepared in the background instead of being streamed in the response: POST /api/v4/groups/<group_id>/exports creates the job and answers 202 with a job_id; GET /api/v4/groups/<group_id>/exports/<job_id> returns its state (pending, running, succeeded or failed) and, once succeeded, a signed download URL, renewed on each read. A job is only visible to the person who requested it and disappears after thirty days, together with its file.
The request body carries export_type and params. The file produced is the same CSV as the matching synchronous export, with the same access rules: a role the synchronous endpoint refuses makes the job fail, with no file.
export_type |
params |
Matching synchronous export |
|---|---|---|
trip_km |
start_date, end_date (required), files, trips_over_days |
export/trip-km |
passenger_counts |
start_date, end_date (required) |
export/passenger-counts |
punctuality |
start_date (required), end_date, event (arrival or departure), stop_id (list), gtfs_id, driver_id, route_id |
export/punctuality |
An invalid parameter is rejected with a 422 error, without creating a job. The synchronous endpoints remain available and unchanged.
Querying the past¶
An export covers a closed period. To reconstruct an elapsed day in detail — events reported, passings observed, GTFS then in force — query the history instead.
The reminder from the GTFS reference page fully applies here: data from six months ago is interpreted against the GTFS in force at that date, not against today's.
Good practice¶
- Split long periods. An export over a whole year produces a large response and a long request; chaining monthly slices is safer and resumes after a failure.
- Do not set a short timeout. These responses are streamed, and a wide export takes time to start.
- Replaying an export has no side effect. These endpoints are read-only: when in doubt about a slice, asking for it again costs only the transfer.
The matching code examples, in the language selected in the header, are on the Collection examples page.