Realtime/SL

Forecast from your own inputs

A forecast for a trip this snapshot does not carry — one being planned, say.

GET /api/punctuality/predict

Two modes, chosen by whether you supply currentDelaySeconds:

Anchored. With a current delay and a horizonStops, you get that delay shifted by the empirical drift distribution over that many stops. method reads anchored.

Prior. Without a current delay you get the marginal distribution for the route or mode — the network's punctuality for that service, with no knowledge of the specific trip. method reads prior, and horizonStops is not accepted, because a prior has no horizon to speak of.

Passing a routeId the static archive does not know is not an error: the forecast quietly backs off to mode or global, and the response carries a Warning header saying so. Read it, or you will take a global answer for a route-specific one.

Parameters

Query

ParameterTypeDefaultDescription
routeIdstring

Forecast from this route's own history. Also settles the mode when the archive knows the route. Unknown ids back off and set a Warning header.

modeenum

Coarse vehicle mode, derived from the GTFS route_type. Selects the slice to forecast from when no route is given, or when the route has too few samples.

trammetrorailbusferrycablefuniculartaxiother

Only four modes carry trips in this capture: bus (791), metro (70), tram (53) and ferry (19). The other five match nothing and fall back to the global slice.

currentDelaySecondsnumber

The trip's delay right now, in seconds; negative is early. Supplying it switches the method from prior to anchored and makes horizonStops required.

horizonStopsinteger1

How many stops ahead to forecast, 1 or more. Required with currentDelaySeconds; rejected without it.

delayBasisenumdeparture

Whether to read each call's delay from its departure or its arrival event. In this capture the two are usually close but not identical — a vehicle can arrive on time and leave late.

departurearrival

toleranceSecondsnumber60

The half-width of the on-time band, 0–3600. A delay within ±this counts as on_time; outside it is early or late. Widen it to match your own definition of punctual.

thresholdSecondsstring

Comma-separated delays, at most 10, to report an exceedance probability for. 300,600 answers “how likely is this more than 5 minutes late, and more than 10?” Adds an exceedance array to the forecast.

Response

data.query echoes what the model actually used — check it, since a routeId that failed to resolve will be missing its route. data.forecast is the answer, and model carries provenance.

GET /api/punctuality/predict?routeId=9011001087500000&currentDelaySeconds=180&horizonStops=5&thresholdSeconds=300,600
curl "$API_BASE/api/punctuality/predict?routeId=9011001087500000&currentDelaySeconds=180&horizonStops=5&thresholdSeconds=300,600"
200 · application/json
{
  "data": {
    "query": {
      "routeId": "9011001087500000",
      "route": {
        "routeId": "9011001087500000",
        "agencyId": "505000000000000001",
        "shortName": "875",
        "type": 700
      },
      "mode": "bus",
      "currentDelaySeconds": 180,
      "horizonStops": 5,
      "delayBasis": "departure",
      "toleranceSeconds": 60
    },
    "forecast": {
      "horizonStops": 5,
      "horizonBand": "4-6",
      "method": "anchored",
      "delayBasis": "departure",
      "currentDelaySeconds": 180,
      "basis": "mode",
      "basisKey": "bus",
      "sampleSize": 2227,
      "sufficientSamples": true,
      "toleranceSeconds": 60,
      "probability": {
        "early": 0.015,
        "onTime": 0.067,
        "late": 0.917
      },
      "delaySeconds": {
        "p10": 74,
        "p25": 137,
        "p50": 181,
        "p75": 229,
        "p90": 274,
        "mean": 180
      },
      "exceedance": [
        {
          "thresholdSeconds": 300,
          "probability": 0.057
        },
        {
          "thresholdSeconds": 600,
          "probability": 0.003
        }
      ],
      "expected": "late"
    }
  },
  "model": {
    "feedTimestamp": "2026-04-27T11:22:38.000Z",
    "builtAt": "2026-08-07T12:36:57.044Z",
    "delayBasis": "departure",
    "tripObservations": 933,
    "driftObservations": 15451,
    "minSamples": 30
  }
}
Captured from a local instance — this endpoint is not on the public API.

Prior mode

No currentDelaySeconds, so no anchor and no horizon — this is the metro's punctuality in general. Note method: "prior" and how much wider the quantiles are than the anchored case above.

GET /api/punctuality/predict?mode=metro
curl "$API_BASE/api/punctuality/predict?mode=metro"
200 · application/json
{
  "data": {
    "query": {
      "mode": "metro",
      "delayBasis": "departure",
      "toleranceSeconds": 60
    },
    "forecast": {
      "horizonStops": 1,
      "horizonBand": "1",
      "method": "prior",
      "delayBasis": "departure",
      "basis": "mode",
      "basisKey": "metro",
      "sampleSize": 70,
      "sufficientSamples": true,
      "toleranceSeconds": 60,
      "probability": {
        "early": 0,
        "onTime": 0.943,
        "late": 0.057
      },
      "delaySeconds": {
        "p10": -10,
        "p25": 0,
        "p50": 4,
        "p75": 22,
        "p90": 47,
        "mean": 14
      },
      "expected": "on_time"
    }
  },
  "model": {
    "feedTimestamp": "2026-04-27T11:22:38.000Z",
    "builtAt": "2026-08-07T12:36:57.044Z",
    "delayBasis": "departure",
    "tripObservations": 933,
    "driftObservations": 15451,
    "minSamples": 30
  }
}
Captured from a local instance — this endpoint is not on the public API.

Errors

400
{ "error": "request_failed", … }

`horizonStops` given without `currentDelaySeconds`, `horizonStops` below 1 or fractional, `toleranceSeconds` outside 0–3600, or an unknown `mode`/`delayBasis`.