Realtime/SL

Nothing is synthesised

A stop time event may carry a delay with no absolute time. Deriving one from the other needs the scheduled timetable, which this API does not load. You get what the feed gave.

A GTFS-realtime stop time event has three optional parts: a delay in seconds, an absolute time, and an uncertainty. A feed may send any combination. The obvious convenience — when only one of delay and time is present, compute the other — is one this API deliberately does not offer.

What this looks like in practice

In this capture SL is generous: all 16,502 stop time events that exist carry both a delay and an absolute time, so you will not actually hit the delay-only case here. It remains a real shape in the format, and a client that assumes time is always present will break against a feed that is less forthcoming.

a typical stop time update — both parts present
{
  "stopSequence": 6,
  "stopId": "9022050000778004",
  "scheduleRelationship": "SCHEDULED",
  "arrival": {
    "delay": 9,
    "time": { "epoch": 1777288329, "iso": "2026-04-27T11:12:09.000Z" },
    "uncertainty": 0
  },
  "departure": {
    "delay": 100,
    "time": { "epoch": 1777288420, "iso": "2026-04-27T11:13:40.000Z" },
    "uncertainty": 0
  }
}

Stop arrivals makes the distinction explicit rather than making you infer it. Every call carries hasPredictedTime: false means the feed gave no clock time, so the call sorts last instead of being placed at a computed position it has not earned.

The other shape: nothing at all

A stop can also carry no prediction whatsoever. All 17 of those in this capture are SKIPPED stops — the vehicle is not calling there, so there is nothing to predict, and the arrival and departure keys are absent entirely rather than present and empty.

a SKIPPED stop, from trip 14010000698634451
{
  "stopSequence": 28,
  "stopId": "9022050012081001",
  "scheduleRelationship": "SKIPPED",
  "stop": {
    "stopId": "9022050012081001",
    "name": "Åkermyntan",
    "latitude": 59.379163,
    "longitude": 17.815768,
    "locationType": 0,
    "parentStation": "12081",
    "platformCode": "1"
  }
}

This is the one case where an absent prediction carries real information: paired with scheduleRelationship: "SKIPPED" it means “this service is not stopping here”, which is worth showing a passenger. See get trip update for the full example.

What else is not invented

  • Route and stop names. They come from the static join. A trip with no match in the archive keeps exactly the fields the feed sent — no partial or guessed enrichment. More →
  • Positions. A vehicle with no position is omitted from GeoJSON output and counted in meta.omittedWithoutPosition, rather than being placed at 0,0 or at its last known stop.
  • The route list. List routes reports routes appearing in the feed, not SL's catalogue — 369 here, against 7,880 in the archive.
  • Trip-level delay. Not one trip in this capture reports one, and the API does not aggregate one from the stop time events. The events are right there if you want to do that yourself; the point is that the API will not pass off a derived figure as a reported one.

The one derived field in the API is delayStatus on a trip, and it is derived purely from the trip's own delayearly below −60 seconds, late above +60, on_time between. When delay is absent, delayStatus is absent too, rather than defaulting to on_time.