Skip to content

Collection examples

The eight exports of the datahub spec feed a data warehouse from a group's operations. This page gives a runnable example for each, in the language selected in the header — currently Python.

Every example authenticates with an API key, sent in the Authorization header. The Authentication page describes how to obtain one. They read their inputs from the environment:

Variable Content
GROUP_ID The identifier of the group to export
PYSAE_API_KEY The API key carrying the export rights

Before you start: the signatures differ

The eight exports are not called the same way. Three families coexist, and no generic call covers all eight.

Export Parameters
trips date (required)
trip-km, passenger-counts start_date, end_date (required)
punctuality, trip-tracking start_date (required), end_date
drivers, vehicles, alerts no required parameter

Write dates in the compact YYYYMMDD form — for example 20260131. It is the only format the five dated endpoints all accept. punctuality and trip-tracking also tolerate YYYY-MM-DD, but the other three reject it: sticking to the compact form saves you from remembering which is which.

The guide's Statistics and exports page details what each one returns.

Operated trips

GET /api/v4/groups/{group_id}/export/trips covers a single day, given by date. To cover a period, chain one call per day.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/trips",
    headers={"Authorization": f"Api-Key {api_key}"},
    params={"date": "20260131"},
    timeout=None,
)
response.raise_for_status()

with open("trips.csv", "wb") as file:
    file.write(response.content)

Kilometres covered

GET /api/v4/groups/{group_id}/export/trip-km bounds a period with start_date and end_date, both required.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/trip-km",
    headers={"Authorization": f"Api-Key {api_key}"},
    params={"start_date": "20260101", "end_date": "20260131"},
    timeout=None,
)
response.raise_for_status()

with open("trip-km.csv", "wb") as file:
    file.write(response.content)

Passenger counts

GET /api/v4/groups/{group_id}/export/passenger-counts follows the same signature as kilometres.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/passenger-counts",
    headers={"Authorization": f"Api-Key {api_key}"},
    params={"start_date": "20260101", "end_date": "20260131"},
    timeout=None,
)
response.raise_for_status()

with open("passenger-counts.csv", "wb") as file:
    file.write(response.content)

Punctuality

GET /api/v4/groups/{group_id}/export/punctuality only requires start_date. The endpoint also accepts optional filters — event, stop_id, gtfs_id, driver_id, route_id — to narrow the export.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/punctuality",
    headers={"Authorization": f"Api-Key {api_key}"},
    params={"start_date": "20260101", "end_date": "20260131"},
    timeout=None,
)
response.raise_for_status()

with open("punctuality.csv", "wb") as file:
    file.write(response.content)

Trip tracking

GET /api/v4/groups/{group_id}/export/trip-tracking requires start_date and end_date.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/trip-tracking",
    headers={"Authorization": f"Api-Key {api_key}"},
    params={"start_date": "20260101", "end_date": "20260131"},
    timeout=None,
)
response.raise_for_status()

with open("trip-tracking.csv", "wb") as file:
    file.write(response.content)

Drivers

GET /api/v4/groups/{group_id}/export/drivers exports the driver reference, with no notion of period. The optional archived parameter includes archived drivers, absent by default.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/drivers",
    headers={"Authorization": f"Api-Key {api_key}"},
    timeout=None,
)
response.raise_for_status()

with open("drivers.csv", "wb") as file:
    file.write(response.content)

Vehicles

GET /api/v4/groups/{group_id}/export/vehicles exports the fleet. The optional statuses parameter narrows it to vehicles in a given state.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/vehicles",
    headers={"Authorization": f"Api-Key {api_key}"},
    timeout=None,
)
response.raise_for_status()

with open("vehicles.csv", "wb") as file:
    file.write(response.content)

Alerts

GET /api/v4/groups/{group_id}/export/alerts exports the group's passenger information messages. No parameters.

import os

import httpx

group_id = os.environ["GROUP_ID"]
api_key = os.environ["PYSAE_API_KEY"]

response = httpx.get(
    f"https://api.pysae.com/api/v4/groups/{group_id}/export/alerts",
    headers={"Authorization": f"Api-Key {api_key}"},
    timeout=None,
)
response.raise_for_status()

with open("alerts.csv", "wb") as file:
    file.write(response.content)

In production

  • Split long periods. A monthly slice resumes after a failure; a yearly export starts over.
  • Do not set a short timeout. Responses are streamed and a wide export takes time to start: a client enforcing a tight deadline cuts the connection before the first row.
  • Replaying an export has no side effect. These endpoints are read-only.
  • Check the key's perimeter. A key restricted to certain teams exports fewer rows, with nothing in the response to say so.