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 Node.js.
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 { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/trips`,
)
url.searchParams.set("date", "20260131")
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("trips.csv", Buffer.from(await response.arrayBuffer()))
Kilometres covered¶
GET /api/v4/groups/{group_id}/export/trip-km bounds a period with start_date and end_date, both required.
import { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/trip-km`,
)
url.searchParams.set("start_date", "20260101")
url.searchParams.set("end_date", "20260131")
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("trip-km.csv", Buffer.from(await response.arrayBuffer()))
Passenger counts¶
GET /api/v4/groups/{group_id}/export/passenger-counts follows the same signature as kilometres.
import { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/passenger-counts`,
)
url.searchParams.set("start_date", "20260101")
url.searchParams.set("end_date", "20260131")
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("passenger-counts.csv", Buffer.from(await response.arrayBuffer()))
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 { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/punctuality`,
)
url.searchParams.set("start_date", "20260101")
url.searchParams.set("end_date", "20260131")
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("punctuality.csv", Buffer.from(await response.arrayBuffer()))
Trip tracking¶
GET /api/v4/groups/{group_id}/export/trip-tracking requires start_date and end_date.
import { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/trip-tracking`,
)
url.searchParams.set("start_date", "20260101")
url.searchParams.set("end_date", "20260131")
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("trip-tracking.csv", Buffer.from(await response.arrayBuffer()))
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 { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/drivers`,
)
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("drivers.csv", Buffer.from(await response.arrayBuffer()))
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 { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/vehicles`,
)
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("vehicles.csv", Buffer.from(await response.arrayBuffer()))
Alerts¶
GET /api/v4/groups/{group_id}/export/alerts exports the group's passenger information messages. No parameters.
import { writeFile } from "node:fs/promises"
const groupId = process.env.GROUP_ID
const apiKey = process.env.PYSAE_API_KEY
const url = new URL(
`https://api.pysae.com/api/v4/groups/${groupId}/export/alerts`,
)
const response = await fetch(url, {
headers: { Authorization: `Api-Key ${apiKey}` },
})
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`)
}
await writeFile("alerts.csv", Buffer.from(await response.arrayBuffer()))
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.
Internal note
Internal endpoints serve the same data for back-office use. They are not intended for external integrations.