Documentation menu
Buses: live positions, stops, lines and timetables
MapMap carries England's local buses as display data: where the buses are now, where the stops are, which lines call at them, and what the timetable says leaves next. Four pieces, one rule.
| You want | Use | Data |
|---|---|---|
| Buses moving on a map | GET /v1/transit/vehicles or the SDK's TransitVehiclesLayer | DfT Bus Open Data Service (BODS) live positions |
| Stops and lines drawn on a map | The SDK's TransitStopsLayer | NaPTAN stops, BODS timetable shapes |
| The stops near a point | GET /v1/transit/stops or the MCP tool nearby_stops | NaPTAN, with lines from BODS timetables |
| What leaves a stop next | GET /v1/transit/departures or the MCP tool next_departures | BODS timetables, paired with live positions |
The rule: display, never prediction. Positions are drawn where the feed reports them. Departure times are the timetable's. A departure can show a bus seen on its line heading towards the stop, which is an observation of where that bus is, not a forecast of when it arrives. MapMap does not compute journey times, plan trips on public transport, or predict arrivals or delays, and nothing on this page should be presented as doing so.
Coverage. England's local bus and coach services: BODS is England's national bus data service. Stops just across the border are included where an England service calls at them. Scotland and Wales' own networks, rail, tram, metro and ferry are not included.
Live bus positions
GET /v1/transit/vehicles?bbox=west,south,east,north answers GeoJSON for a
box of at most 4 square degrees, refreshed from the national feed every
minute, with the feed's freshness on every answer (feed.status ok,
stale past 180 seconds, or unknown; a stale feed still returns its
buses, marked paused). Full parameters and fields:
API reference.
import { MapMapMap, TransitVehiclesLayer } from "@mapmap/maps";
const map = new MapMapMap({ container: "map", apiKey: "snk_..." });
const buses = new TransitVehiclesLayer(map, {
onFeedStatus: ({ label }) => (pill.textContent = label), // "Live, 42 s ago"
});
buses.start();
The layer polls only the area on screen, every 30 seconds by default, and draws one poll behind so buses glide between fixes.
Stops and lines on a map
Two map tile archives, rebuilt every night and kept apart from the base
map: transit-stops (layer stops, zoom 10 to 14) and transit-lines
(layer lines, zoom 6 to 14). With an API key on the map, the layer reads
them through the keyed tile API
(https://api.mapmap.ai/tiles/transit-stops/tiles.json, metered like the
base map tiles), which works from your own site. Self-hosting, or reading
the archives directly, their current addresses are in the tiles index,
https://tiles.mapmap.ai/index.json, under transit.
import { MapMapMap, TransitStopsLayer } from "@mapmap/maps";
const map = new MapMapMap({ container: "map", apiKey: "snk_..." });
const transit = new TransitStopsLayer(map, {
onStopClick: async (stop) => {
// stop.id is the NaPTAN ATCO code; stop.lines the lines calling there
const res = await fetch(
`https://api.mapmap.ai/v1/transit/departures?stop=${stop.id}`,
{ headers: { Authorization: "Bearer snk_..." } },
);
showBoard(await res.json());
},
});
await transit.ready; // false when no transit archives are published
- Lines show from zoom 6, in the route's own colour where the timetable publishes one. A route the timetable gives no shape for is drawn dashed, stop to stop, so it never reads as the road the bus takes.
- Stops show from zoom 11 with the MapMap transit glyph.
- Both layers survive
map.setStyle(...)and sit in themiddleslot (above roads, below labels) unless you passslot. - Options:
stops,lines(both defaulttrue),slot,onStopClick,lineColour,stopColour,source("auto","api"or"tiles-host"), andstopsUrl/linesUrlfor a copy you host yourself.
What @mapmap/maps exports for this, besides the layer itself:
| Export | What it is |
|---|---|
TransitStopsLayerOptions | The options above |
TransitStopFeature | What onStopClick hands you: id (ATCO code), name, indicator, locality, bearing, lines |
transitStopFromProperties(props) | Turns a stop feature's tile properties into a TransitStopFeature (parses lines) |
transitTileJsonUrl(baseUrl, id, apiKey?) | The keyed TileJSON URL for transit-stops or transit-lines on a gateway |
transitUrlsFromIndex(index, indexUrl) | The pmtiles:// URLs a tiles index announces under transit |
transitArchiveUrl(url, indexUrl) | One archive path resolved to a pmtiles:// URL |
TRANSIT_TILES_INDEX_URL | https://tiles.mapmap.ai/index.json |
NAPTAN_SHORT_CREDIT | "Bus stops: NaPTAN, DfT, OGL v3.0", the stops credit the layer adds |
TRANSIT_LINE_FALLBACK_COLOUR | The line colour where the timetable publishes none |
Stop properties in the tiles: id (ATCO code), name, indicator,
locality, bearing (NaPTAN compass point), lines (a JSON array as a
string). Line properties: line, operator, noc (National Operator
Code), colour (when published), direction (outbound, inbound or
absent), route, and shape (gtfs, or stops for a stop-to-stop chain).
Stops near a point
curl -s "https://api.mapmap.ai/v1/transit/stops?lat=51.5080&lon=-0.1281&radius_m=300" \
-H "Authorization: Bearer $KEY"
radius_m 1 to 2,000 (default 500), limit 1 to 50 (default 10). Each
stop carries its ATCO code, name, indicator, locality, position, distance
and bearing from your point, and lines. Every answer carries coverage
and attribution.
Departures at a stop
curl -s "https://api.mapmap.ai/v1/transit/departures?stop=490013767T&limit=6" \
-H "Authorization: Bearer $KEY"
{
"stop": { "id": "490013767T", "name": "Trafalgar Square", "indicator": "Stop T", "lines": ["12", "88", "139", "159", "453"] },
"basis": "timetable",
"label": "Timetable times from Bus Open Data timetables. A live entry shows a vehicle on this line observed upstream of the stop. MapMap does not predict arrival times.",
"departures": [
{ "line": "88", "destination": "Parliament Hill Fields", "operator": "GAHL",
"scheduled_at": "2026-10-12T18:19:00+01:00", "trip": "VJ...",
"live": { "vehicle": "YX13BKG", "distance_m": 62, "age_s": 154 } }
],
"timetable_date": "2026-10-12",
"coverage": "England bus stops (NaPTAN) with services from BODS timetables. Rail, tram and ferry are not included.",
"attribution": "Contains public sector information licensed under the Open Government Licence v3.0. Source: NaPTAN, Department for Transport. ..."
}
at(RFC 3339, default now),window_minutes(1 to 180, default 60),limit(1 to 30, default 8). The index holds three service days from today; anatoutside them is refused with the window named.- Show the
labelwith the departures. It says what the times are and what a live entry is. liveappears when a bus on the same line, seen in the last five minutes, is within 3 km of the stop and its reported heading points towards it. Per line, the nearest such bus goes with the earliest departure. It is a heuristic over live positions, not a link between that bus and that timetabled journey.
For agents
The MCP tools nearby_stops and next_departures return the same answers,
with label and coverage on every one. Pass them on: an agent must not
present a timetable time or a live entry as an arrival time. See
MCP.
Keys and metering
/v1/transit/vehicles, /v1/transit/stops and /v1/transit/departures
are one Standard unit per request each, and a browser (maps) key may call
them. The stops and lines tiles meter like the base map tiles when read
through the keyed tile API. A live map polling every 30 seconds makes 2,880 calls a day
per open page, so poll the visible area only and prefer a longer interval
where the use allows.
Licence and credits
All of this is public sector information licensed under the Open Government Licence v3.0:
- Stops: "Contains public sector information licensed under the Open Government Licence v3.0. Source: NaPTAN, Department for Transport."
- Live positions and timetables: "Contains public sector information from the Bus Open Data Service, licensed under the Open Government Licence v3.0. Source: Department for Transport (data.bus-data.dft.gov.uk)."
Every API answer carries its attribution; the SDK layers add the short
credits ("Bus stops: NaPTAN, DfT, OGL v3.0" and "Bus data: DfT Bus Open
Data Service, OGL v3.0") to the map's one credit line. The transit data is
served from its own files and paths and is never merged into the
OpenStreetMap-derived base map.