LopoRail Public API
Stations, live boards, PPM/performance, and real-time vehicle positions. Versioned, read-only, with API keys and rate limiting.
No matching sections.
Quickstart
Get an API Key
Sign up for a LopoRail account and generate an API key from your API Keys dashboard.
# Your key looks like:
lra_abcdefghijklmnopqrstuvwxyz123456
Make Your First Request
Send your key in the Authorization header:
curl -H "Authorization: Bearer lra_abcdefghijklmnopqrstuvwxyz123456" \
"https://loporail.com/wp-json/loporail/v1/stations?search=kings+cross&limit=5"
const res = await fetch('https://loporail.com/wp-json/loporail/v1/stations?search=kings+cross&limit=5', {
headers: { Authorization: 'Bearer lra_abcdefghijklmnopqrstuvwxyz123456' },
});
const data = await res.json();
console.log(data);
import requests
r = requests.get(
'https://loporail.com/wp-json/loporail/v1/stations',
params={'search': 'kings cross', 'limit': 5},
headers={'Authorization': 'Bearer lra_abcdefghijklmnopqrstuvwxyz123456'},
)
print(r.json())
$res = wp_remote_get( 'https://loporail.com/wp-json/loporail/v1/stations?search=kings+cross&limit=5', array(
'headers' => array( 'Authorization' => 'Bearer lra_abcdefghijklmnopqrstuvwxyz123456' ),
) );
$data = json_decode( wp_remote_retrieve_body( $res ), true );
require 'net/http'
uri = URI('https://loporail.com/wp-json/loporail/v1/stations?search=kings+cross&limit=5')
req = Net::HTTP::Get.new(uri)
req['Authorization'] = 'Bearer lra_abcdefghijklmnopqrstuvwxyz123456'
puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
req, _ := http.NewRequest("GET", "https://loporail.com/wp-json/loporail/v1/stations?search=kings+cross&limit=5", nil)
req.Header.Set("Authorization", "Bearer lra_abcdefghijklmnopqrstuvwxyz123456")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
Explore the Data
Try the endpoints below or use the interactive playground to test live calls.
# Search stations
GET /stations?search=edinburgh
# Get station by CRS
GET /stations/KGX
# Live departures board
GET /boards/KGX?type=departures&limit=10
# Performance metrics (PPM)
GET /ppm/ch
# Vehicle positions
GET /positions/us-mbta
Authentication
All API endpoints (except /info and /openapi) require an API key.
Header Authentication (Recommended)
Authorization: Bearer YOUR_API_KEY
Query Parameter (For Testing)
GET /stations?api_key=YOUR_API_KEY
Rate Limits
API keys have tier-based limits:
| Tier | Daily | Monthly | Burst (per minute) |
|---|---|---|---|
| Free | 1,000 | 10,000 | 60 |
| Developer | 10,000 | 100,000 | 60 |
| Business | 100,000 | 1,000,000 | 60 |
| Enterprise | 1,000,000 | 10,000,000 | 60 |
The Free tier covers /stations, /boards, /info. Live /ppm and /positions require the Developer tier or higher. Free and Developer keys must show a "Powered by LopoRail" link; Business and Enterprise may white-label. See plans
Rate Limit Headers
Every response includes headers to help you manage your quota:
X-RateLimit-Limit-Day— Daily limitX-RateLimit-Remaining-Day— Requests remaining todayX-RateLimit-Limit-Month— Monthly limitX-RateLimit-Remaining-Month— Requests remaining this monthX-RateLimit-Reset-Day— Unix timestamp when daily limit resetsX-RateLimit-Reset-Month— Unix timestamp when monthly limit resets
Exceeding Limits
When you exceed a limit, the API returns 429 Too Many Requests with a Retry-After header indicating seconds until reset.
Endpoints
Stations
GET /stations— Search stationsGET /stations/{crs}— Station by CRSGET /stations/tocs/{toc}— Stations by operator
Live Boards
GET /boards/{crs}— Departures/arrivals
Params: type=departures|arrivals|both, limit, window
Performance (PPM)
GET /ppm/{provider}— PPM metrics
Providers: ch, fr, de, us-mbta, dk, etc.
Vehicle Positions
GET /positions/{provider}— Live positions
Providers: us-mbta, jp, etc.
Geo
GET /geo/nearby— Stations near a pointGET /geo/reverse— Nearest place
Meta
GET /info— API info (public)GET /status— Service status (public)GET /openapi— OpenAPI spec (public)
Live data demo
A real departures board, rendered server-side from the same feed the API serves. No key needed for this sample.
| Time | Destination | Operator | Platform | Status |
|---|---|---|---|---|
| 00:01 | Cambridge | Great Northern | 9 | On time |
| 00:07 | Letchworth Garden City | Great Northern | — | On time |
| 00:31 | Cambridge | Great Northern | — | On time |
| 01:07 | Royston | Great Northern | — | On time |
| 01:10 | Hertford North | Great Northern | — | On time |
| 23:33 | Leeds | LNER | 1 | On time |
Updated 2026-10-05T22:23:58+00:00. Demo shows one station; get a key for full access.
API Reference
Free and Developer keys must display a "Powered by LopoRail" link on your app; Business and Enterprise may white-label.
GET /stations — Search stations by name or CRS code.
| Parameter | Description |
|---|---|
search | Name or CRS to match. |
limit | 1-500 (default 50). |
offset | Pagination offset. |
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/stations"
Example response
{"data":[{"name":"London Kings Cross","crs":"KGX","lat":51.5308,"lon":-0.1238}],"meta":{"count":1,"limit":50,"offset":0}}
GET /stations/{crs} — A single station by CRS code.
| Parameter | Description |
|---|---|
crs | 3-letter CRS code (path). |
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/stations/KGX"
Example response
{"data":{"name":"London Kings Cross","crs":"KGX"}}
GET /boards/{crs} — Live departures/arrivals for a station.
| Parameter | Description |
|---|---|
crs | 3-letter CRS code (path). |
type | departures | arrivals | both. |
limit | Rows (max 100). |
window | Minutes ahead. |
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/boards/KGX"
Example response
{"data":{"station":{"id":"KGX","name":"London Kings Cross"},"services":[{"scheduled_time":"13:06","destination":{"name":"Letchworth Garden City"},"platform":"10","status":{"public_text":"On time"}}]}}
GET /ppm/{provider} — PPM/performance metrics. Developer tier or higher.
| Parameter | Description |
|---|---|
provider | ch, fr, de, dk, us-mbta, ... |
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/ppm/ch"
Example response
{"data":{...},"meta":{"provider":"ch","generated_at":"..."}}
GET /positions/{provider} — Live vehicle positions. Developer tier or higher.
| Parameter | Description |
|---|---|
provider | us-mbta, jp, ... |
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/positions/ch"
Example response
{"data":[{"id":"...","lat":42.3,"lon":-71.1}]}
GET /geo/nearby — Stations within a radius of a point.
| Parameter | Description |
|---|---|
lat | Latitude (-90..90). |
lng | Longitude (-180..180). |
radius | Km (default 25, max 500). |
limit | Max results. |
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/geo/nearby"
Example response
{"data":[{"name":"King's Cross St Pancras","lat":51.530609,"lon":-0.1239491,"distance_km":0.024}]}
GET /geo/reverse — Nearest station/place to a point (max 50 km).
| Parameter | Description |
|---|---|
lat | Latitude. |
lng | Longitude. |
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/geo/reverse"
Example response
{"data":{"name":"King's Cross St Pancras","distance_km":0.024}}
GET /info — API metadata + value ladder (public).
Request
curl -H "Authorization: Bearer $LOPORAIL_API_KEY" "https://loporail.com/wp-json/loporail/v1/info"
Example response
{"name":"LopoRail API","version":"0.4.3","tiers":{...}}
Freight Data
Public, read-only freight endpoints under the loporail-freight/v1 namespace. No API key required.
| Endpoint | Description |
|---|---|
GET /operators | Freight operators |
GET /operators/{slug} | Operator detail |
GET /services | Freight services |
GET /lines | Freight lines |
GET /wagon-types | Wagon types |
GET /terminals | Terminals (optional bbox filter) |
GET /movements | Wagon movements (optional bbox filter) |
GET /locations | Locations (tiploc, search, non_passenger) |
GET /search | Search freight data (q) |
GET /summary | Dataset summary |
curl "https://loporail.com/wp-json/loporail-freight/v1/summary"
curl "https://loporail.com/wp-json/loporail-freight/v1/terminals?bbox=-0.5,51.0,0.5,51.8"
Error Codes
Errors are JSON with a machine-readable code, a message, and — for rate limits — a retry_after value.
| Status | Code | Meaning |
|---|---|---|
401 | missing_api_key | No API key supplied. |
403 | invalid_api_key | Key is invalid or revoked. |
403 | expired_key | Key has expired. |
403 | key_suspended | Key temporarily suspended by abuse protection. |
403 | tier_required | Endpoint needs a higher plan tier (/ppm and /positions require Developer+). |
404 | not_found | No data for the requested resource. |
429 | rate_limit_exceeded | Daily / monthly / account limit reached; see Retry-After. |
429 | burst_limit_exceeded | Too many requests per minute; slow down. |
429 | ip_blocked | Temporary block after repeated failed authentication. |
Status & Coverage
Data coverage
- 128,500 stations
- 3,078 with a CRS code
- Multiple live feeds (UK NRE, CH, DE, FR, DK, US MBTA, JP)
Uptime & Service Level
Our uptime is published, not claimed. These figures are computed from the API audit log and are public — a prospective customer can verify them without an account.
Rolling availability
Rate-limit responses (HTTP 429) are a correct metered outcome and are excluded from the availability calculation. SLA credits are governed by the applicable commercial agreement.
Per-endpoint availability
| Endpoint | Requests | Availability |
|---|
Enterprise entitlements
Built for teams that redistribute rail data to their own users.
| Capability | Developer | Business | Enterprise |
|---|---|---|---|
| Live board & station reference | Yes | Yes | Yes |
| Real-time positions (PPM) | Yes | Yes | Yes |
| Redistribution to your own users | Attribution required | Commercial licence | Commercial licence |
| White-label (hide LopoRail branding) | No | Yes | Yes |
| Published uptime SLO | Best-effort | 99.9% | 99.9%+ |
| Machine-readable SLA | Yes | Yes | Yes |
| Bulk reference dataset exports | No | No | Yes — on request |
Upstream credits required in all cases: “Data source: Rail Delivery Group (Rail Data Marketplace)” and the Open Government Licence v3.0 notice. Full terms are published with the API Terms of Use.
Changelog
v0.5.7 2026-10-05
- Runtime protections: per-account limits, anomaly auto-suspension, IP blocking, key expiry, log retention.
- Public status surface (/status) + graceful degradation.
- Geo endpoints: /geo/nearby and /geo/reverse.
- Freight dataset documented; DK (SIRI-ET) exposed via /ppm/dk.
v0.3.0 2026-09-30
- Paid API tiers + Stripe checkout; value ladder (Free excludes /ppm and /positions).
- API-key transactional emails + full i18n.
v0.2.0 2026-09-25
- Public REST surface: /stations, /boards, /ppm, /positions, /info, /openapi.
SDKs & Tools
Versioned starter clients (SDK 0.5.7) — download and go. Machine-readable manifest: sdk manifest.
- JavaScript: Starter ES-module client. Download .js
- Python: Starter client using requests. Download .py
- PHP: Starter client for WordPress. Download .php
- Postman: Ready-made collection (set the loporail_key variable). Download collection
- OpenAPI 3.1: Generate clients for any language. View the OpenAPI spec
Terms of Use & Attribution
- Read-only: the LopoRail public API is read-only.
- Data sources: station data © National Rail / Network Rail and respective operators; performance data © Office of Rail and Road / respective operators.
- Rate limits: respect the documented limits; do not attempt to circumvent them.
- Caching: cache responses and poll no more often than necessary.