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 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 and addresses across Great Britain.
Combines NaPTAN stop data with geocoding. 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. |
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. Nationwide, 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
Scheduled departures for a station.
| 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 |
{
"crs_code": "LDS",
"station": { "crs_code": "LDS", "name": "Leeds",
"latitude": 53.7956409037, "longitude": -1.5480300934 },
"departures": [
{ "line": "NT", "line_name": "Northern", "operator": "Northern",
"destination": "Bradford Forster Square", "mode": "rail",
"aimed_departure_time": "2026-08-05T15:42:00+00:00",
"status": "on_time", "source": "gtfs_scheduled" }
]
}
Rail is scheduled-timetable data only — there is no live rail feed
integrated yet, so source is always gtfs_scheduled
and there is no live parameter. Unlike bus, which does have live
matching.
Journey planning
GET /v1/journey.json
Plan a door-to-door journey between two coordinates, with fare estimates and live vehicle positions. Covers South and West Yorkshire only, unlike the endpoints above — 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": "South and West Yorkshire",
"bbox": [53.2989, -2.1755, 53.9649, -0.8529] },
"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 engineering works are not covered.
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.