Live Haramain railway data, over GraphQL.
A single, typed GraphQL endpoint for the Saudi Arabia Railways Haramain High-Speed line (HHR): timetables, topology, real-time train movements, and the live signalling mesh.
The API is a Backend-for-Frontend over SAR's MONR monitoring system. It authenticates upstream, holds the session, fans the live feed out to many subscribers, and re-exposes everything as clean, English-typed GraphQL with real-time subscriptions over graphql-ws.
Everything here is interactive — run queries in the playground and start live subscriptions right from these docs.
At a glance
| Capability | Type |
|---|---|
| Discover lines & sections | Query |
| Timetables & circulation detail | Query |
| Live train positions | Subscription |
| Live signalling mesh | Subscription |
| Drive control-room viewers | Mutation |
Connecting
Everything is served from one origin. Queries and mutations are HTTP POST; subscriptions use a WebSocket on the same path with the graphql-transport-ws subprotocol.
| Channel | Endpoint |
|---|---|
| GraphQL · HTTP | — |
| Subscriptions · WebSocket | — |
Two ways to try it
1. The Playground (recommended). Built into these docs. Click Run in playground on any operation and it loads a ready-to-run example with real values filled in — just press Run. The easiest way to explore.
2. Apollo Sandbox (advanced). The full GraphQL IDE — schema explorer, autocomplete, variables & headers panels. Open the HTTP endpoint in a browser.
Quickstart
Fetch the available lines and their sections — no auth or SDK required.
Authentication
The gateway authenticates to the upstream MONR system with a service account on your behalf — you never handle those credentials. Consumer-facing auth (API keys / JWT) is applied at the edge in production deployments; in that case your provider issues you a key and tells you how to pass it.
Caching & freshness
Slow timetable/topology reads are cached server-side (transparent to you). Identical queries seconds apart may return identical data — that's expected. Live subscriptions are never cached.
| Operation | Cache TTL |
|---|---|
| lineSections / sectionPoints | ~10 min |
| sectionTrains | ~20 s |
| trainDetail | ~15 s |
| searchTrains | ~10 s |
Queries
Read timetables and topology. All queries are HTTP POST to the GraphQL endpoint.
Subscriptions
Real-time data over graphql-ws. Click Stream on any subscription — frames appear in the docked console at the bottom, which stays open as you read.
Mutations
Side-effecting actions that drive operator viewers. Restricted in production.
Types
The object types returned by the operations above.
Errors
Errors use the standard GraphQL envelope:
{ "errors": [ { "message": "…", "path": ["…"] } ], "data": null }A field returning null with no error usually means "no data" (e.g. an empty time window). If the upstream MONR server is temporarily unreachable, the API automatically serves recent sample data instead of erroring — the badge in the header shows sample data in that state, and flips back to live data when the upstream returns.
Data dictionary
Units & time. Train km/pk are kilometres; distanceMeters is metres. Times (departureTime, time, serviceDate…) are epoch milliseconds. circulationDate is dd/MM/yyyy; actualTime/diff are H:mm:ss; delay is seconds (grid) or a short string like 0m (live).
| Field | Values |
|---|---|
| Train.direction | ODD (Makkah→Madinah) · EVEN (reverse) |
| Train.status | Running · Finished · Next to Run … |
| SinopticElement.type | CV (track circuit) · SV/SE (signal) · DSV (switch) · BAU (block) |
| ElementPart.state | colour/state code: I (inactive) · V · R · Az · B · A |