1. Create an account
Verify your email and open the account dashboard.
Sign upDeveloper documentation
Search national stops and stations, resolve source identifiers, and explore operators, routes and ordered calling patterns across bus, rail, tram and metro datasets.
Verify your email and open the account dashboard.
Sign upName the key and copy it when shown. Plaintext is displayed once.
Include the key in the X-API-Key header.
Resolve a station, fetch its next departures, then discover nearby bus stops and published routes. These are three authenticated requests; there is no automatic polling.
/v1/resolve?crs=PWYKeep the returned id; do not invent a UKPT identifier.
/v1/map/locations/{id}/departures?limit=5URL-encode the id as a single path segment. This is the existing map-board API, not a new versioned departures contract.
/v2/rail/stations/PWY/nearby-bus-servicesRequest bounded stops and routes. Published routes are not guaranteed timed connections.
Set UKPT_API_KEY privately. Avoid shell tracing or shared logs.
curl --fail --max-time 15 -H "X-API-Key: $UKPT_API_KEY" "https://api.ukpt.botric.co.uk/v1/resolve?crs=PWY"import json, os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None # Never forward the key to another host.
request = Request(
"https://api.ukpt.botric.co.uk/v1/resolve?crs=PWY",
headers={"X-API-Key": os.environ["UKPT_API_KEY"]},
)
with build_opener(NoRedirect()).open(request, timeout=15) as response:
station = json.load(response)
print(station["id"], station["name"])The standard library raises HTTPError for non-success responses. Apply the error guidance below before adding retries.
Download and inspect the source, then run it on your server with Node.js 20 or later. Set your own key privately in the environment; never embed it in browser JavaScript.
Download rail-to-bus.mjs# UKPT_API_KEY must already be set in your environment.
node rail-to-bus.mjs PWYOutputs station, departures and buses objects. Requests time out after 15 seconds, reject redirects and stop on errors without printing your key. No packages or local server are required.
generated_at is response creation time. source_observed_at is the latest contributing live update where available; null means freshness is unknown, not current.aimed_departure is scheduled; expected_departure is an available prediction. Check live, cancelled and calendar_status. A GPS position alone is not an arrival prediction.A minimal empty board can look like the example below. This is illustrative, not a live observation; consult OpenAPI for the full schema.
{
"location_id": "ukpt:location:rail:EXAMPLE",
"location_name": "Example station",
"mode": "rail",
"departures": [],
"message": "No current departures are available from the imported live/timetable data.",
"generated_at": "2026-10-01T12:00:00Z",
"source_observed_at": null
}Each rail-to-bus workflow resolves a station, reads a bounded board and finds onward bus routes. Keep keys in your server environment and respect quota headers.
For trams, search /v1/locations/search?q=your-stop, check mode and locality, then pass the returned ID to departures.mjs. /v1/passenger/stops/{id}/routes lists routes; /v1/passenger/coverage describes imported inventory. Tram schedules may be available without live estimates. The /partners screen view is a bounded local departures-display example.
Choose a public bus, train, tram or metro stop. This limited example uses the website proxy. Run your integration on your server with your own key.
Check service_date (Europe/London), source_status, source_observed_at and each row’s status. generated_at is the response time. Empty, scheduled, stale and unsupported results have different meanings.
Read the HTTP status before parsing a success schema. Error bodies normally contain detail (a string or validation list); do not treat them as an empty board.
| Status | What to do |
|---|---|
| 400 / 422 | Correct missing identifiers or invalid parameters; do not retry unchanged input. |
| 401 | Check the X-API-Key header and whether your key is revoked or expired. |
| 404 | The identifier is not available; nearby-station lookup also requires coordinates. |
| 409 | Resolve returned ambiguous or conflicting identifiers. Ask for a more specific identifier. |
| 429 | Stop requests. Minute throttles include Retry-After seconds. Monthly exhaustion includes Retry-After seconds and X-MonthlyLimit-Reset (Unix seconds). |
| 503 | The service or quota checks are unavailable. Keep old information labelled stale/unavailable; use bounded backoff rather than rapid retries. |
Free accounts allow 10,000 requests per UTC calendar month and 60 per minute. Read X-RateLimit-Limit/Remaining/Reset and X-MonthlyLimit-Limit/Remaining/Reset when supplied. Free account allowances are shared across keys, including rotated keys. Internal keys without an account retain their own allowances. Authenticated requests can consume allowance before endpoint validation, including errors and cached responses.
For a small prototype, refresh a visible departure board no faster than every 60 seconds and reuse station IDs and nearby-route results. Continuous one-minute polling uses about 43,200 requests in a 30-day month, beyond the free allowance. Stop when hidden and budget across all endpoints. This recommendation is not a freshness guarantee.
Use the interactive OpenAPI reference for current parameter ranges and schemas. v1 and v2 coexist; a larger version number does not replace every v1 endpoint. No new deprecation guarantee is introduced by this guide.
Schedules use Europe/London time and describe configured imports, not successful observations or guaranteed freshness. BODS processes one operator code per hour, not the whole catalogue each hour.
| Dataset | Gathered from | Configured refresh | Coverage |
|---|---|---|---|
| Bus stops and localities | NaPTAN and NPTG, Department for Transport | Daily overnight | Names, coordinates, ATCO/NaPTAN codes and locality hierarchy |
| Rail-replacement stops | DfT NaPTAN Rail Replacement Locations | Daily at 00:45 | Official replacement-stop locations, ATCO codes and station links |
| Bus services, routes and calling patterns | Bus Open Data Service (BODS), TransXChange | One NOC every hour; full catalogue continuously cycles | English registered bus timetables, route numbers, LineRef and ServiceCode |
| Bus operators and NOC codes | BODS operator catalogue | Refreshed during each hourly NOC pass | Operator identities, NOC codes and dataset import state |
| Live bus vehicle positions | Bus Open Data Service (BODS), SIRI-VM | Polled every 15 seconds; positions expire after 10 minutes | Vehicle references, lines, journeys, GPS coordinates, bearings and destinations where supplied |
| Rail stations and identifiers | National Rail Knowledgebase and Network Rail CORPUS | Knowledgebase daily at 06:00; CORPUS weekly Sunday | Stations, accessibility, CRS, TIPLOC, STANOX, NLC and UIC identifiers |
| Planned trains and replacement buses | Network Rail Schedule/CIF | Daily at 05:50; rolling horizon up to about 12 weeks | Planned calling patterns, UIDs, TOCs and category BR replacement buses |
| Near-term rail timetable | National Rail Darwin Timetable Files | Checked daily at 05:40 | Dated operational timetables and planned station calls |
| Live trains and replacement services | National Rail Darwin realtime Kafka | Continuous, normally within seconds | RIDs, estimates, actual times, platforms and cancellations |
| London transport | Transport for London Unified API | Daily at 05:00 | London Underground, bus, tram and other supported TfL lines |
| Regional tram and metro | Traveline National Dataset (TNDS) | Weekly Sunday at 05:30 | Manchester Metrolink, Sheffield Supertram and other available urban networks |
curl -H "X-API-Key: ukpt_your_key" \
"https://api.ukpt.botric.co.uk/v1/locations/search?q=Manchester%20Piccadilly"| Method | Path | Purpose |
|---|---|---|
| GET | /v1/bus/vehicles/{registration}/mot | Known bus registrations: recorded DVSA MOT/annual tests, cached 24 hours; ready/stale/not_found/unavailable status and checked_at. Not a current roadworthiness guarantee. |
| GET | /v1/locations | List and filter transport locations |
| GET | /v1/locations/search?q=Piccadilly | Search locations by name |
| GET | /v1/locations/near?lat=&lon=&radius= | Find nearby locations |
| GET | /v1/operators?mode=tram | List operators by mode |
| GET | /v1/operators/{id}/routes | List routes for one operator |
| GET | /v1/routes?mode=metro | List routes by mode |
| GET | /v1/routes/{id}/stops | Get a route’s ordered stops |
| GET | /v1/resolve?crs=&naptan=&atco=&tiploc= | Resolve source identifiers |
| GET | /v1/map/locations/{id}/departures?limit=5 | Read the existing location departure board (limit 1–30) |
| GET | /v1/rail/operators/{toc}/summary | Compact operator counts, cached 60 seconds, with latest rail receipt time |
| GET | /v1/rail/operator-directory | Lightweight operator names and service status; no national timetable or realtime count scan |
| GET | /v1/rail/operators/{toc}/track-network | Configured rail operators: OSM route-member GeoJSON, monthly checks, source dates and ODbL attribution; not train positions |
| GET | /v1/rail/operators/{toc}/station-network | Timetable-derived station snapshot, cached 30 days, checked_at and truncation flag; no track geometry |
| GET | /v2/locations/RailReplacement | List tagged rail-replacement stops |
| GET | /v2/rail/stations/{crs}/nearby-bus-services?distance=500 | Find bus operators and routes near a rail station |
| GET | /v1/rail/railreplacement?date=YYYY-MM-DD&toc=XX | List live and 12-week planned replacements; TOC is optional |
| GET | /v1/rail/railreplacement/{rid} | Get a replacement journey and linked ATCO stops |
| GET | /v1/rail/railreplacement/{uid}/{date} | Get a future or live replacement by UID and date |
| GET | /v1/rail/train/{rid} | Get an ordered rail journey by RID |
| GET | /v1/rail/disruption-snapshot | Bounded active/unexpired rail notices, operator indicators and successful feed import freshness |
| GET | /v1/tfl/rail | List TfL rail, Tube, DLR and tram lines |
| GET | /v1/tfl/bus | List TfL bus lines and matched operating-company tags |
| GET | /v1/tfl/stations | List TfL stations |
| GET | /v1/tfl/bus-stops | List TfL bus stops |
| GET | /v1/tfl/operators | List operators matched to TfL lines |
| GET | /v1/tfl/search?q=FirstLondon | Search TfL lines, stops, stations and operators |
| GET | /v1/tfl/bus/{route_number} | Get a TfL bus route with operators, patterns and identified stops |
| GET | /v1/tfl/rail/{line_name} | Get all calling patterns and stops for a TfL rail line |
| GET | /v1/tfl/rail/status | Get current TfL rail network status |
Use a station CRS code to find nearby bus stops and published routes. Request a compact response for passenger interfaces:
curl -H "X-API-Key: ukpt_your_key" "https://api.ukpt.botric.co.uk/v2/rail/stations/PWY/nearby-bus-services?distance=500&stop_limit=6&service_limit=30&include_calling_patterns=false"Distances are straight-line metres, not walking routes or guaranteed transfers. Optional service_date=YYYY-MM-DD selects published service validity periods (default: today in the UK); it does not confirm weekday, holiday or live operation. Stops and services each accept limits from 1 to 500. Calling patterns are included by default.
Responses include stop coordinates, service_date, generated_at, stops_truncated and services_truncated. Each parameter combination is cached for up to one hour. The generated timestamp describes the list, not an upstream observation or departure update.
Issue, rotate and revoke keys from the account dashboard. Keep keys server-side and never publish them in URLs, screenshots or source control. See the limits and error guidance above before adding polling.
NaPTAN/NPTG supplies national locations, BODS supplies English bus timetables, TfL supplies London modes, TNDS supplies selected tram and metro timetables, and DfT supplies rail-replacement locations. Network Rail and National Rail feeds supply rail identifiers, schedules, live running updates, incidents, accessibility and operator status.