Twelve raw feeds.
One station identity.
One call.
Headcode reconciles, normalises and enriches the open UK rail feeds — Darwin, Network Rail Open Data, NaPTAN, ORR, the Rail Data Marketplace and the rest — so you integrate once. Start with whatever station code you have, get back all of them, plus live running data a single feed never carries.
The problem
The integration tax nobody quotes you for
The raw data is free. That is the trap. CRS for the fares system, TIPLOC for the timetable, STANOX for movements, plus NLC, ATCO and UIC — every feed names the same station differently, and a big terminus like London Bridge answers to a whole cluster of TIPLOCs across its platforms. Darwin arrives as a push port; reference data from the Rail Data Marketplace, Network Rail and NaPTAN turns up on its own cadence in its own shape. Before you render a single departure board you have written a reconciliation layer, a code-mapping table and a normaliser — and you now own all of it. Headcode is that layer, run as a service, with British English schemas and metric units throughout.
Without Headcode
With Headcode
GET /v1/stations/KGX
One station object, every known identifier resolved, live and planned data already joined. The reconciliation layer becomes a field you read.
- No code-mapping table to maintain
- No feed cadences to babysit
- No bespoke normaliser to debug at 07:00
One request
Every identifier, plus the live running data
Ask King's Cross for the next departures to York and Edinburgh in a single call. Send the CRS code you already have; Headcode resolves the station across every code system and answers grouped by destination, with scheduled and live-estimated times blended from the timetable and Darwin. No second call to a different feed to find out the train is two minutes down.
curl https://api.headcode.dev/v1/stations/KGX/next?destinations=YRK,EDB \ -H "Authorization: Bearer hc_…"
{
"destinations": {
"YRK": [
{
"rid": "202605141A45",
"uid": "C12345",
"toc_code": "GR",
"scheduled_public": "10:03",
"estimated": "10:05"
}
],
"EDB": [ … ]
}
}GET /v1/stations/{id}/next — requires the boards:read scope, on every plan including Free.
rid+uidLook the full service up via
/v1/services/{service_id}— calling points, formation, live status.toc_codeResolves to the operating company through
/v1/operatorsreference data.estimatedvsscheduled_publicEstimated is live from Darwin; the scheduled public time stays put. Both arrive in one response.
Identifier resolution
Bring any code. Get every code back.
Identifiers are forgiving by design. Pass a CRS, TIPLOC, STANOX, NLC, ATCO, UIC or a Headcode slug — any of them resolves the same station, and the response hands back every known identifier for it, so the mapping table you used to maintain becomes a field you read. Operational points that are not stations — junctions, sidings and depots — are reachable too, via Locations.
Send any one of these:
GET /v1/stations/KGX the same station, every time.
Single-station lookup: GET /v1/stations/{id}. Fuzzy text and lat/lng radius search on GET /v1/stations. Identifiers shown are King's Cross's real reference codes.
What you can build on day one
Live in the API today, documented with OpenAPI
A raw feed answers one narrow question; a Headcode response answers the question you actually had — already joined, already normalised. Built for passenger-info, journey, logistics, proptech and analytics teams. Every product ships a downloadable spec; access is enforced per scope on your key.
Live station boards
Departures, arrivals, a combined board, and next departures to several destinations at once.
Service detail
Calling points, formations and live status — looked up by UID and scheduled start date.
Platform train order
The order trains will berth on a platform — the detail that makes a board match the rail.
Disruptions
Darwin station messages and train alerts, unified into one disruption shape.
Operators & reason codes
Train operating company reference, and the delay and cancellation reason-code lookup.
Locations
Non-station operational points — junctions, sidings and depots — by TIPLOC.
Operations
Feed health you can monitor: heartbeat status and active alarms across the ingest.
Board filtering
Filter departures and arrivals by operator, platform, destination or calling point.
Live and historic
The same shape, today or last spring
Today's 10:03 to York and the same departure from a Tuesday last year come back in the same shape. From Pro, a 13-month lookback serves the board, departures and arrivals for any past day through dedicated history endpoints — same fields, same identifiers — so a backfill and a live view share code. Two analytics products sit alongside as add-ons.
Historical data
Board, departures and arrivals for a past day, on dedicated history endpoints — not a date param bolted onto the live board.
GET /v1/stations/{id}/history/board · /departures · /arrivals scope: history:read · Pro and upAnalytics add-ons
Performance reads aggregate punctuality from pre-computed daily rollups. Footfall is station passenger-flow data. Each grants a scope without changing your request limits.
Performance +£49/mo · Footfall +£69/mo scopes: performance:read · footfall:read · Pro and upPlans
Start free, and stay legible
Free is a real working tier for evaluation and small projects. Starter covers most production apps on live data and adds Darwin operations. Pro adds the 13-month history and production volume, and is where Performance and Footfall become available. Limits are per account — every key shares the same daily and burst budget.
- ●100 requests/day
- ●2 req/s burst
- ●2 API keys
- ●Boards, services, train order, disruptions, reference
- ●10,000 requests/day
- ●10 req/s burst
- ●5 API keys
- ●Everything in Free, plus Darwin operations
- ●100,000 requests/day
- ●50 req/s burst
- ●25 API keys
- ●Adds 13-month history; Performance + Footfall available
- ●Reserved throughput above 50 req/s
- ●Contractual SLA
- ●Custom keys & volume
- ●Bespoke data arrangements
What each plan unlocks
| Capability | Scope | Free | Starter | Pro | Ent. |
|---|---|---|---|---|---|
| Live station boards | boards:read | ✓ | ✓ | ✓ | ✓ |
| Reference data (stations, operators, reason codes, locations) | reference:read | ✓ | ✓ | ✓ | ✓ |
| Service lookup | services:read | ✓ | ✓ | ✓ | ✓ |
| Train order | train-order:read | ✓ | ✓ | ✓ | ✓ |
| Disruptions | disruptions:read | ✓ | ✓ | ✓ | ✓ |
| Darwin operations (heartbeat, alarms) | operations:read | — | ✓ | ✓ | ✓ |
| Historical data (13-month lookback) | history:read | — | — | ✓ | ✓ |
| Performance analytics | performance:read | — | — | Add-on | ✓ |
| Footfall | footfall:read | — | — | Add-on | ✓ |
Self-serve purchasing isn't live yet — to add a pack or enable analytics while billing is being built, email [email protected] and access can be adjusted on your key. Add-on prices are indicative and may change before launch.
OpenAPI-first
Your tooling already knows the shape
Every product ships a versioned, downloadable specification you can point a client generator, mock server or contract tests at — the schema is the contract, in British English. Eight products are live today; the guides explain the concepts behind them, this page is the short version.
Request access
Tell us what you're building
Headcode is in active development and there is no self-serve signup yet, so access is by request — which also means you can ask for exactly the scopes and headroom your build needs. A Free key is enough to integrate against. Email us with your use-case and the station codes you're starting from, and we'll send a token.