Overview
Products
Reference
API reference
Every endpoint TransCAPI exposes, with its parameters and response shape. For a runnable, always-current version of this generated from the server itself, see the OpenAPI document and the Swagger UI.
Conventions
| Base URL | https://api.transcapi.com |
|---|---|
| Auth | X-Api-Key header, or api_key query parameter. See authentication. |
| Format | JSON. All endpoints are GET. |
| Timestamps | ISO 8601 with offset, e.g. 2026-08-05T16:41:00+01:00. |
| Coordinates | WGS84 decimal degrees. |
| Rate limits | Per key, per day and per minute, by tier. See rate limits. |
List endpoints share a pagination envelope: page,
limit, results_count and a member array
of results. Pass page and limit to page through.
Every response carries a request_time.
Errors use a consistent envelope and an HTTP status to match:
{ "error": { "code": "missing_api_key", "message": "Missing API key. ..." } }
See error codes for the full list.
Places
GET /v1/places.json
Search stops, settlements, postcodes, streets and landmarks across Great Britain. Settlements
come from NPTG, the national register of cities, towns, villages and suburbs,
and postcodes from Ordnance Survey's Code-Point Open. A full postcode matches
exactly, and a partial one (S1, SW1A) lists the
postcodes it starts. Places sharing a name are ranked by size, so
Newport returns the city in South Wales first. Settlements and
postcodes come before stops, unless you supply lat/lon,
in which case everything is sorted by distance. Streets and landmarks
(type street and landmark: schools,
universities, hospitals, parks, hills, lakes, beaches) come from Ordnance
Survey's Open Names, Great Britain only. Name the town to pick one of many
streets with the same name: Broad Street, Sheffield or
broad street sheffield. Otherwise the nearest comes first if you
supply lat/lon, or the one in the largest town.
Apostrophes, full stops and a leading "The" don't matter, and Welsh and
Gaelic names match too. House numbers are not searchable. Supply a query, a lat/lon,
or a bounding box.
| Parameter | Default | Description |
|---|---|---|
query | — | Free-text search term. |
type | all | Repeatable. One of bus_stop, train_station, tram_stop, settlement, postcode, street, landmark, airport. Airport results carry an iata code for /v1/airports/{iata}.json. |
lat, lon | — | Bias and sort results by distance from this point. |
min_lat, min_lon, max_lat, max_lon | — | Bounding box. All four required together. |
limit | 20 | 1–100. |
page | 1 |
{
"request_time": "2026-08-05T15:40:05Z",
"query": "Sheffield",
"page": 1, "limit": 1, "results_count": 1,
"member": [
{
"type": "bus_stop",
"name": "Airport Access Road/Hurst Lane",
"description": "Doncaster Sheffield Airport",
"latitude": 53.484269965,
"longitude": -1.026033473,
"atcocode": "370046721"
}
]
}
distance (metres) is included on each result when
lat/lon is supplied, and omitted otherwise.
Bus
GET /v1/bus/stops.json
Find bus, tram and rail stops near a point or within a bounding box. Across Great Britain, backed by NaPTAN.
| Parameter | Default | Description |
|---|---|---|
lat, lon | — | Centre point for a radius search. |
radius | 400 | Metres from lat/lon. |
min_lat, min_lon, max_lat, max_lon | — | Bounding box instead of a radius. |
type | all | Repeatable stop type filter. |
limit | 20 | |
page | 1 |
{
"member": [
{
"type": "bus_stop",
"atcocode": "370027633",
"naptan_code": "37027633",
"name": "Leopold Street/Orchard Lane",
"short_name": "Leopold Street",
"indicator": "27633",
"street": "Leopold Street",
"locality": "Sheffield Centre",
"town": "Sheffield",
"latitude": 53.381315404,
"longitude": -1.470926487,
"distance": 59.8
}
]
}
GET /v1/bus/stop_timetables/{atcocode}.json
Departures for a stop, combining the scheduled timetable with live BODS SIRI-VM vehicle positions.
| Parameter | Default | Description |
|---|---|---|
atcocode | required | In the path. NaPTAN ATCO code, e.g. 370023169. |
datetime | now | ISO 8601 anchor for the departure window. |
from_offset | 0 | Window start, minutes relative to datetime. |
to_offset | 60 | Window end, minutes. |
live | false | Match live vehicles onto departures. |
limit | 20 |
{
"atcocode": "370023169",
"live_requested": true,
"live_error": null,
"departures": [
{
"line": "X1",
"line_name": "X1",
"operator": "First South Yorkshire",
"destination": "Maltby",
"direction": 0,
"mode": "bus",
"aimed_departure_time": "2026-08-05T15:41:00+00:00",
"expected_departure_time": "2026-08-05T15:41:00+00:00",
"best_departure_estimate": "2026-08-05T15:41:00+00:00",
"status": "on_time",
"source": "siri_vm",
"dated_vehicle_journey_ref": "VJfde1c8a6f4a5b81dfb398429a6ef7793",
"live_vehicle": {
"vehicle_ref": "FSYO-35126",
"operator_ref": "FSYO",
"latitude": 53.376426,
"longitude": -1.471027,
"bearing": "28",
"destination_name": "Quilter_Road",
"recorded_at_time": "2026-08-05T15:39:49+00:00"
}
}
]
}
Use best_departure_estimate when you want a single time to
display: it is the live estimate where one exists and the timetabled time
otherwise. source tells you which you got —
siri_vm for live, gtfs_scheduled for timetable.
live_vehicle is null on departures with no vehicle
matched, and live_error is non-null if the live feed itself
failed, so a feed outage is distinguishable from a quiet stop.
Rail
GET /v1/rail/stations.json
Find rail stations near a point or within a bounding box. Covers all of Great Britain (~2,600 stations). Stations are keyed by 3-letter CRS code.
| Parameter | Default | Description |
|---|---|---|
lat, lon | — | Centre point. |
radius | 5000 | Metres. |
min_lat, min_lon, max_lat, max_lon | — | Bounding box instead of a radius. |
limit | 20 | |
page | 1 |
{
"member": [
{ "crs_code": "SHF", "name": "Sheffield",
"latitude": 53.3782362148, "longitude": -1.4621101656, "distance": 558.9 }
]
}
GET /v1/rail/station_timetables/{crs_code}.json
Departures and arrivals for a station, scheduled from the national timetable and merged with live running from National Rail's Darwin.
| Parameter | Default | Description |
|---|---|---|
crs_code | required | In the path, e.g. LDS. |
datetime | now | ISO 8601 anchor for the window. |
from_offset | 0 | Minutes. |
to_offset | 60 | Minutes. |
limit | 20 | |
type | departures | departures, arrivals or both. |
live | true | Merge live running from Darwin. |
{
"crs_code": "LDS",
"station": { "crs_code": "LDS", "name": "Leeds",
"latitude": 53.7954, "longitude": -1.5476 },
"live_requested": true,
"live": true,
"live_error": null,
"messages": [
"Trains running in the Exeter St Davids area may be delayed, cancelled or revised. ..."
],
"departures": [
{ "mode": "rail", "line": "TP", "line_name": "TransPennine Express",
"operator": "TransPennine Express", "trip_id": "T19113",
"destination": "Newcastle",
"aimed_departure_time": "2026-09-28T16:52:00+00:00",
"expected_departure_time": "2026-09-28T17:00:00+00:00",
"best_departure_estimate": "2026-09-28T17:00:00+00:00",
"status": "late", "platform": "15",
"delay_reason": "This service has been delayed by trespassers on the railway earlier today",
"cancel_reason": null, "source": "darwin" }
]
}
Live running comes from National Rail's Darwin, the railway's own real-time system, and is on by default (live=false turns it off). Each scheduled train is matched to Darwin's board for the station and gains an expected time, a platform, and a status: on_time, late, early, delayed (late, with no estimate yet), or cancelled, with delay_reason or cancel_reason where Darwin gives one. source is darwin for a matched train and gtfs_scheduled for one Darwin did not list, which is a few percent at a busy station. messages carries National Rail's station notices as plain text.
Darwin covers two hours either side of now. A board asked for further out is scheduled only, and live is false. live_error says why if Darwin could not be reached, and the board still comes back, scheduled.
Journey planning
GET /v1/journey.json
Plan a door-to-door journey between two coordinates, with fare estimates and live vehicle positions. Covers Great Britain. See the journey planning docs for the full detail on fares, live data and coverage.
| Parameter | Default | Description |
|---|---|---|
from_lat, from_lon | required | Origin. |
to_lat, to_lon | required | Destination. |
datetime | now | Departure, or arrival when arrive_by=true. |
arrive_by | false | |
transit_modes | tram,rail | Optional sub-modes. Bus and coach always searched. |
modes | walk,bicycle | Non-transit alternatives. |
include_fares | true | |
include_live | true | |
include_intermediate_stops | false | |
max_walk_distance | — | Metres. Caps the longest single walking leg. |
max_total_walk_distance | — | Metres. Caps total walking across the journey. |
include_steps | false | Turn-by-turn directions on walking legs. |
limit | 5 | 1–20. |
{
"coverage": { "area": "Great Britain",
"bbox": [49.8, -8.7, 61.0, 2.0] },
"next_available": false,
"itineraries": [
{
"start_time": "2026-08-05T16:36:36+01:00",
"end_time": "2026-08-05T17:09:14+01:00",
"duration_s": 1958.0,
"fare": { "amount_gbp": 2.80, "complete": true },
"legs": [ /* see journey planning docs */ ]
}
],
"alternatives": { "walk": { "itineraries": [] },
"bicycle": { "itineraries": [] } }
}
An origin or destination outside the covered area returns
422 outside_coverage rather than an empty list, so "we don't plan
here yet" is distinguishable from "no journeys found".
Disruptions
GET /v1/disruptions.json
Planned works, incidents and line status, merging the national BODS SIRI-SX situation feed with TfL's line status. See the disruptions docs for the response shape and the caveats on matching a situation to a service.
| Parameter | Default | Description |
|---|---|---|
mode | all | bus, tram, rail, tube, dlr, overground, elizabeth-line. |
severity | all | severe, minor, information, good, unknown. |
atcocode | — | Only situations naming this stop. |
operator, line | — | Substring match. |
lat, lon, radius | 2000 m | Max 50000. Both lat and lon, or neither. |
min_lat, min_lon, max_lat, max_lon | — | Bounding box instead of a radius. |
path | — | Encoded polyline (precision 5). Only situations naming a stop near it, matched on their complete stop list. |
path_radius | 300 m | How far off the path a stop may be. Maximum 5000. |
planned | both | true for works only, false for incidents only. |
active_only | true | Exclude situations outside their validity period. |
include_good_service | false | Include healthy TfL lines. |
limit, page | 50 | Maximum 100 per page. |
Ordered most disruptive first, then most recently raised. Only situations naming a stop with coordinates can match a geographic query. National Rail incidents are placed by the stations named in their affected routes.
Service
GET /health
Unauthenticated liveness check. Returns {"status": "ok"}.
GET /openapi.json
The OpenAPI 3.0 document for this API, generated from the running server. Unauthenticated.