BetaLike the app, the API is in beta. There is no availability guarantee, and fields and limits may still change.
The public API.
The same data the apps and this website use. No key required, CORS is open.
Ground rules
https://api.oeffigo.appAccess-Control-Allow-Origin: *.429 with Retry-After: 30.no-store. Successes vary with how fast the data moves: max-age=10 as the rule, 15 for /v1/vehicles, 60 in the browser and 300 at the edge for /v1/history. POST /v1/predictions/batch is no-store — its answer depends on the body you sent and belongs in no shared cache. The Cache-Control header on each response is authoritative./v2 is versioned separately and names its version on every response in the X-OeffiGo-Contract header./v2 is the app contract, not an open API/v2 is the versioned app contract. Its machine-readable schema is at /v2/schema and the current route list at /v2/meta; both are open to read. You still cannot call the data routes from outside: they reject browser requests and require an installation token only the apps get. These routes are meant for the apps only./v1The /v1 endpoints documented here are the open surface: plain GETs, CORS allowed, usable without credentials. They stay. The apps themselves speak /v2. Extra fields can appear at any time; ignore fields you do not know.{ "error": "code" } with a matching HTTP status.Endpoints
Lightweight liveness check.
Returns 200 while the worker has a recent successful cycle, otherwise 503. Meant for monitoring, not for fetching data.
Request
curl -s https://api.oeffigo.app/v1/healthResponse
{
"ok": true,
"version": "5dc1a29",
"predictionsLive": 1312,
"confidentPredictions": 1190
}Operational state of every component.
Returns the overall state, a component list and a half-hourly record of successful and failed requests per data path. The standalone status page combines this report with external checks. Successful data requests are not externally measured availability.
Request
curl -s https://api.oeffigo.app/v1/statusResponse
{
"generatedAt": "2026-07-26T13:25:06.754Z",
"overall": "operational",
"components": [
{ "id": "api", "name": "…", "status": "operational" }
],
"measurement": { "mode": "official_realtime" }
}Reliability analysis computed from official actual times.
Totals, a daily trend, lines and a weekday×hour pattern. Individual stop events are counted, not whole journeys. A missing data point is never treated as a cancellation — cancellations come only from explicit events.
| Parameter | Type | Meaning |
|---|---|---|
from | ISO date | Start of the period. |
to | ISO date | End of the period. At most a 366-day span. |
line | string | Restrict to a single line. |
source | string | Restrict to one data source: wiener_linien, trias, stmk_gtfs_rt. |
Request
curl -s "https://api.oeffigo.app/v1/history?from=2026-07-15&to=2026-07-26&line=U1"Response
{
"period": { "from": "2026-07-15", "to": "2026-07-26" },
"totals": {
"onTimeShare": 0.9556,
"avgDelayMin": 0.57,
"late10Share": 0.007
},
"officialRealtime": {
"daily": [ { "day": "2026-07-26", "events": 18109, "on_time_pct": 97.4 } ],
"lines": [ { "line": "U1", "events": 39970, "on_time_pct": 96 } ],
"heatmap": [ { "dow": 1, "hour": 8, "avg_delay_min": 0.6 } ]
}
}- Cacheable at the edge for five minutes; supports ETag and 304.
- `dow` is ISO: 1 = Monday … 7 = Sunday.
Our own delay estimates derived from official realtime.
High and medium confidence entries only. Every entry expires at most eight minutes after its last real observation; older values are never served. <strong>A filter is required</strong>: without one you get <code>400 missing_query</code>.
| Parameter | Type | Meaning |
|---|---|---|
lat + lon | number | A radius search, optionally with radius. One of the four filters. |
line | string | Line label, e.g. U1. One of the four filters. |
jid | string | One journey reference. One of the four filters. |
jids | string[] | Up to 40 journey references, comma separated. One of the four filters. |
radius | int | Only together with lat + lon. |
limit | int | At most 200. Does not substitute for a filter. |
Request
curl -s "https://api.oeffigo.app/v1/predictions?line=U1&limit=50" -H 'If-None-Match: "abc123"'Response
{
"generatedAt": "2026-07-26T13:25:06.754Z",
"count": 2,
"predictions": [
{ "jid": "og1.…", "delayMin": 2, "confidence": "high" }
]
}- Responses carry a weak ETag. With If-None-Match you get a body-less 304 while the snapshot is unchanged — the battery-friendly way to poll.
Look up many journey references at once.
Journey references are long and do not belong in a URL. For more than a handful, this is the right call.
Request
curl -s -X POST https://api.oeffigo.app/v1/predictions/batch \
-H 'content-type: application/json' \
-d '{"jids":["og1.…","og1.…"]}'Response
{
"generatedAt": "2026-07-26T13:25:06.754Z",
"count": 2,
"predictions": [ … ]
}A bounded map viewport of vehicles.
Requires either a bounding box or a list of journey references — there is no unfiltered response. Entries are at most eight minutes old. The `source` field tells you what an empty result means.
| Parameter | Type | Meaning |
|---|---|---|
minLat, minLon, maxLat, maxLon | number | Visible map viewport — all four or none. |
jids | string[] | Comma-separated journey references. The server accepts up to 500; in practice URL length caps it around 40. Does not trigger a fetch. |
limit | number | 1–1,000, default 500. |
Request
curl -s "https://api.oeffigo.app/v1/vehicles?minLat=48.18&minLon=16.30&maxLat=48.24&maxLon=16.42"Response
{
"generatedAt": "2026-08-03T19:25:06.754Z",
"count": 242,
"source": "ondemand_live",
"measurementMode": "official_realtime",
"vehicles": [ { "jid": "og1.…", "lat": 48.2, "lon": 16.37 } ]
}- `ondemand_live` with an empty list means "no vehicles right now", not "service off". `ondemand_capped` means "live vehicles temporarily unavailable".
Vienna metro realtime, cached.
Only stations from a fixed list; the endpoint does not pass arbitrary requests on to the operator monitor. Data source: City of Vienna, CC BY 4.0.
| Parameter | Type | Meaning |
|---|---|---|
diva | int | Station identifier from the list. |
Request
curl -s "https://api.oeffigo.app/v1/wiener-linien/metro?diva=60200657"Response
{
"generatedAt": "2026-08-03T19:16:51.814Z",
"status": "ok",
"departures": [
{ "line": "1", "towards": "Stefan-Fadinger-Platz",
"plannedAt": "2026-08-03T19:23:30.000Z",
"realAt": "2026-08-03T19:26:50.000Z" }
]
}- The delay is not a field — it is the difference between
realAtandplannedAt. Both are ISO timestamps, not milliseconds. - Can answer
503 source_standbywhen the Wiener Linien source is switched off. Try again later.
Server-driven messages for the apps.
Lets us publish notices without shipping an app update. Closed and expired messages are not included.
Request
curl -s https://api.oeffigo.app/v1/announcementsResponse
{
"generatedAt": "2026-07-26T13:25:06.754Z",
"announcements": [ { "id": "…", "title": "…", "body": "…" } ]
}What the API does not offer.
The typed routes the apps use to fetch timetable and journey data are bound to the apps and are not part of this documentation. Please do not build anything that depends on them.
Journey references (og1.…) are opaque and provider-bound. Do not unpack, rewrite or mix them across networks — pass them back unchanged.
The API is open so people can build with it. Stay inside the limits, cache responses, and if you reuse Vienna realtime data, credit the source: City of Vienna — data.wien.gv.at, CC BY 4.0.
ÖffiGo