Level crossing API documentation
The reference: every endpoint, what each machine-readable field means and how to read it, the limits, and the terms. New here? The overview says what the API is for and shows it running.
Base URL and versioning
All endpoints live under https://mindthegates.uk/api/v1/.
Every response carries "api": 1. Fields are added without
notice; nothing is removed or renamed within a version. A breaking
change would ship as /api/v2/ with v1 kept running.
A machine-readable description is at
/api/v1/openapi.json
(OpenAPI 3.1).
Endpoints
| Endpoint | What it returns | Cache |
|---|---|---|
GET /api/v1/crossings |
The catalogue: every listed crossing with its slug,
name, region, type and, where
located, lat/lon. About 220 KB. |
1 hour |
GET /api/v1/search?q= |
Ranked name search over the catalogue; the same ordering the site and the apps use. Empty query, empty result. | 5 min |
GET /api/v1/crossing/{slug} |
One crossing: live state, forecast timeline, next trains, direction labels and the safety wording. The slug is the one in the catalogue or in the page URL. | 20 s |
GET /api/v1/near?lat=&lon=&limit= |
The listed crossings nearest a point, nearest first, with the straight-line distance in kilometres and miles. Up to 25. | 1 min |
GET /api/v1/busiest?limit= |
Today's ranking by predicted minutes closed to road traffic between 06:00 and 22:00, up to 100 rows. 503 until the day's ranking has been built. | 5 min |
Example
curl -s -H 'User-Agent: my-app/1.0' https://mindthegates.uk/api/v1/crossing/thatcham
{
"api": 1,
"slug": "thatcham",
"name": "Thatcham",
"type": "CCTV-monitored manual barrier",
"region": "Wales & Western",
"source": "nr",
"generated_at": "2026-10-07T08:12:04+01:00",
"status": {
"state": "open",
"confirmed": false,
"confirmed_source": null,
"until": "2026-10-07T08:19:30+01:00",
"marginal": false,
"held_note": null
},
"timeline": [
{"state": "open", "start": "2026-10-07T08:12:04+01:00", "end": "2026-10-07T08:19:30+01:00",
"conditional": false, "parked": false, "marginal": false},
{"state": "closed", "start": "2026-10-07T08:19:30+01:00", "end": "2026-10-07T08:23:10+01:00",
"conditional": false, "parked": false, "marginal": false}
],
"direction_labels": {"up": "towards Reading", "down": "towards Newbury"},
"trains": [
{"at": "2026-10-07T08:20:30+01:00", "headcode": "1K42", "direction": "up",
"origin": "Bedwyn", "destination": "London Paddington",
"cancelled": false, "live": true, "delay_sec": 60}
],
"meta": {"disclaimer": "...", "known_coverage_gap": null}
}
Abridged: real responses carry a few more fields, all documented in the OpenAPI description.
Reading the status
status.stateisopen,closedorunknown. Unknown means no forecast could be made right now, not that the crossing is open.status.confirmedis true only when the railway's own signalling data proves the barriers are down. Thenconfirmed_sourcesays how:signallingormonitor. Everything else is a forecast.status.untiland every timelineendare medians of a spread. Show them with a~, as the site does. A closure can start earlier or end later.marginalon an open state means the next closure may already be starting.conditionalon a closure means it depends on a train that may not run.parkedmeans a train is standing on the approach and the barriers may drop whenever it is signalled on.sourceisnrfor Great Britain, where live feeds correct and confirm the forecast, andnirfor Northern Ireland, where the forecast comes from the published timetable and live departure boards and is never confirmed.meta.known_coverage_gap, when present, names a known limit at that crossing, such as trams that share it and are not fully covered. Absent means no gap of that kind is on record, which is a weaker claim than completeness.- Times are ISO 8601 with the crossing's own UTC offset. Show them as received: the crossing is in the UK whoever is reading.
Rate limits and caching
Limits count requests that reach the origin per client address;
responses served from cache are free. The per-crossing endpoint allows
120 origin renders per 60 seconds. Over
that you get 429 with Retry-After, never a 5xx.
Every per-crossing response carries RateLimit-Limit,
RateLimit-Remaining and RateLimit-Reset, so a
client that reads them never meets the 429.
Honour Cache-Control. A client polling one crossing every
20 seconds, or many crossings once each, is well within the limits. If
you need many crossings often, poll /api/v1/busiest or the
catalogue and fetch detail on demand.
If your use needs more than these limits, or it is commercial, get in touch first: the limits exist to keep the site up for everyone, and there is room for a use case we know about.
Send a User-Agent that names your project. Browsers may
call the API from any origin: every /api/ response carries
Access-Control-Allow-Origin: *.
Credit and terms
The API is free to use. Please credit Mind the Gates and link to the
crossing's page (https://mindthegates.uk/crossing/{slug})
wherever you show its data. The underlying information comes from
Network Rail's open data feeds for Great Britain and from Translink for
Northern Ireland, under their respective open data terms, and the
forecasts are ours. There is no uptime promise or SLA: this is a one-person
project, though the site has run since May 2026 with very little
downtime, and the apps and widgets depend on this same API. Commercial use, or anything above the limits, needs a
conversation first: get in touch. If you build
something, say hello too.
For agents
An MCP server at /mcp exposes the same facts as tools,
and every crossing page has a markdown twin at
/crossing/{slug}.md. Discovery is at
/.well-known/api-catalog, the MCP server card at
/.well-known/mcp/server-card.json, and the house rules at
/auth.md.