QuavernQuavsit

Section 06 of 12

Streams

Two endpoints push updates over server-sent events (SSE) instead of being polled: departures at a stop and vehicles on a line. Streams need a plan with the streams entitlement (every paid plan, including Pay as you go); on Free they answer 402 plan_required.

Endpoints#

RouteParametersInterval
GET /stops/{stop_id}/departures/streaminterval, horizon, limit, line, direction15–120 s, default 20
GET /lines/{line_id}/vehicles/streaminterval10–120 s, default 10

The response is text/event-stream with Cache-Control: no-cache. Ask for it with a client that keeps the connection open:

sh
curl -N "https://api.quavern.net/v1/stops/tbm:stop:3824/departures/stream?interval=20&limit=5" \
  -H "Authorization: Bearer qv_p_…"

Events#

Each event is event: <name> followed by one data: line of JSON, then a blank line.

EventWhenPayload
metafirst, once{"plan", "interval", "max_duration"}
departuresevery interval seconds{"data": [Departure…], "meta": {…}}, identical to the REST response
vehiclesevery interval seconds{"data": [Vehicle…], "meta": {…}}
erroronce, then the stream closes{"code", "reason", "message"}

A comment line : ping is sent every 15 seconds to keep intermediaries from closing an idle connection; SSE clients ignore it. The server closes the stream after max_duration (600 s); reconnect to continue.

text
event: meta
data: {"plan":"pro","interval":20,"max_duration":600}

event: departures
data: {"data":[{"stop_id":"tbm:stop:3824","line_code":"B","headsign":"Berges de la Garonne","expected_at":"2026-09-07T14:13:20+02:00","realtime":true,"status":"delayed"}],"meta":{"generated_at":"2026-09-07T12:11:03Z","units":1,"freshness":"realtime"}}

: ping

Units#

Every departures or vehicles event costs 1 unit, charged as it is sent; meta, pings and error cost nothing. A stream at the default departures interval therefore costs 3 units per minute. When the monthly budget runs out mid-stream the server sends error with reason quota_exhausted and closes.

Concurrency#

Each key may hold a bounded number of streams at once, set by the plan: 2 on Pay as you go and Starter, 3 on Pro, 8 on Max, 16 on Enterprise. Opening one more answers 429 stream_limit_reached. The service also has a global ceiling; when it is reached the same error is returned even below your own limit, so treat it as a retry-later condition (back off 5 to 30 seconds).

Client notes#

The browser EventSource API cannot set an Authorization header, and keys must not reach browsers anyway: consume streams from a server process and relay to your clients if needed. With fetch, read response.body as a stream and split on blank lines. Reconnect on close with a short, jittered delay, and prefer polling the REST endpoint if you need fewer than one update every 20 seconds.