Truck isochrones, billed one call per contour
POST /isochrone returns reachability contours: the area a vehicle can get to
from an origin inside a time or distance budget, as GeoJSON you can drop
straight onto a map. Send "costing": "truck" and the same height, width,
length and weight limits that shape a truck route shape the contour, so a
4-metre-high box van's 30-minute area stops at the low bridge instead of running
through it. The limits are enforced inside the costing model rather than
filtered off the result afterwards, so the polygon is an area the vehicle can
actually reach. Billing is one call per contour, capped at 10 contours per
request: 0.05p per contour on standard costing, 1p per contour when the body
carries truck costing.
What does POST /isochrone actually return?
A GeoJSON FeatureCollection with one feature per contour, returned from the
routing engine. Each feature's properties carry the contour value in the
unit you asked for and a metric of "time" or "distance".
Here is a real request. Where can a 4-metre-high, 40-tonne truck get to from a London depot in 10 and 20 minutes:
export BASE=https://api.mapmap.ai
export API_KEY=snk_...
curl -fsS -X POST "$BASE/isochrone" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"locations": [{ "lat": 51.5074, "lon": -0.1278 }],
"costing": "truck",
"contours": [{ "time": 10 }, { "time": 20 }],
"polygons": true,
"costing_options": { "truck": { "height": 4.0, "weight": 40.0 } }
}'
And the response shape, truncated to one feature:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "contour": 10.0, "metric": "time" },
"geometry": {
"type": "Polygon",
"coordinates": [[[-0.14, 51.50], [-0.11, 51.50], [-0.11, 51.52], [-0.14, 51.50]]]
}
}
]
}
GeoJSON geometry is [lon, lat]. That is the GeoJSON standard, not our
choice. Note that request locations are {lat, lon} objects with named keys,
so there is no coordinate-order ambiguity on the way in.
The parameters worth knowing:
| Field | Required | What it does |
|---|---|---|
locations | yes | Origins the reachability is computed from. |
costing | yes | auto, truck, bus, motor_scooter, motorcycle, bicycle or pedestrian. |
contours | yes | Each is a time in minutes or a distance in kilometres. A contour carrying neither is a 400. |
polygons | no | Return polygons instead of the default linestrings. |
denoise | no | 0 to 1. Higher drops smaller contour islands. |
generalize | no | Geometry simplification tolerance in metres. |
show_locations | no | Include the input locations as GeoJSON points. |
costing_options | no | Passed to the engine verbatim, keyed by costing name. |
What do fleets actually use reachability polygons for?
Depot siting, service-area definition and driver-hours planning. None of the three is drawing a pretty blob on a map.
- Depot siting. You have a candidate site and a list of customers. The question is not "how far is it" but "how many of these can a 40-tonne artic reach inside a 45-minute window". Run the contour from each candidate, count the customers inside, compare. Straight-line radius answers a different question and answers it wrongly, because the truck drives roads, not circles.
- Service-area definition. "Can we serve this postcode in under 45 minutes" is a contract question, and a sales team asking it 200 times a day wants a polygon they can test a point against rather than 200 route calls. One isochrone, cached, does the work of a lot of routing.
- Driver-hours planning. A driver has a legal driving window left. The contour is the honest answer to what is still reachable inside it, computed against the roads the vehicle can legally use.
For the many-to-many version of the depot question, where you want the actual
travel time from each of 12 depots to each of 40 stops rather than an area, use
POST /matrix instead. We wrote that up in
the matrix API and how it bills by element. The rule of
thumb: an isochrone answers "what is in range", a matrix answers "how far is
each of these, exactly".
Why is a truck isochrone a different shape from a car isochrone?
Because costing_options reaches the engine verbatim, so the truck cost model
that gates a route gates the contour too. A low bridge, a weight-limited lane
and a width restriction are edges the search cannot traverse under truck
costing, and an area computed from a search that cannot traverse them is
therefore an area the truck can actually reach.
{
"truck": {
"height": 4.0, "width": 2.55, "length": 16.5,
"weight": 40.0, "axle_load": 9.0, "axle_count": 5,
"hazmat": true, "adr_tunnel_code": "C"
}
}
Dimensions in metres, weights in tonnes. adr_tunnel_code is honoured by our
ADR-extended engine, so a hazmat contour can be tightened by the tunnels the
load may not enter. A stock engine ignores unknown keys, which is why the
request stays portable.
Be careful with what adr_tunnel_code in costing_options.truck does and does
not promise. The isochrone is shaped by ADR costing where the engine supports
it, but the isochrone is not a compliance product and we would rather say so
here than let you find out later.
POST /adr/check is the authoritative tunnel-entry decision, and it is the one
to log. Do not build a dangerous-goods compliance step on top of a polygon.
What does an isochrone cost?
One call per contour, and the class is decided from the body.
| Request | Units | Class | Cost |
|---|---|---|---|
1 contour, "costing": "auto" | 1 | Standard | 0.05p |
3 contours, "costing": "auto" | 3 | Standard | 0.15p |
3 contours, "costing": "truck" | 3 | Premium | 3p |
10 contours, "costing": "truck" | 10 | Premium | 10p |
| 11 contours | rejected | n/a | 400, nothing billed |
Standard is 0.05p per call up to 1M calls a month, 0.03p to 10M, 0.02p beyond. Premium is 1p, 0.7p and 0.4p across the same bands. Bands are marginal, so each price applies only to the calls inside it.
The free tier is denominated in standard calls: 50,000 a month after email verification, and a premium call draws 20 of them. A 3-contour truck isochrone therefore draws 60 included calls, which is worth knowing before you put one behind a page load. Prices reconcile with /pricing.json, which wins if this page ever drifts.
What this does not do
Five real limits, in writing.
- A top-level
adrobject is ignored here, and still bills premium. Theadrextension is honoured onPOST /route,POST /route/alongandPOST /optimiseonly. The isochrone request has no such field, so it is dropped silently by the handler, while the billing classifier sees it and charges premium. You pay 1p a contour for something that had no effect. Put truck and ADR settings incosting_options.truckinstead, which is the path that actually reaches the engine. - 10 contours is a hard cap. An 11th contour is a
400and the request is not billed. Split it. - GraphHopper deployments are more limited. On a self-hosted deployment
running
SN_ROUTING_ENGINE=graphhopper, isochrones are single-origin, cannot mix time and distance contours in one call, and match your contours exactly only when they are evenly spaced. Valhalla, the default, has none of these limits. - The MCP isochrone tool is narrower than the endpoint.
reachable_areaon our MCP server computes time contours from a single origin, in minutes only, with nocosting_options. For distance contours, multiple origins or truck dimensions, an agent has to call the HTTP endpoint directly. - A contour is a model, not a promise. It is computed from the road network and the costing model, and it does not know that your yard gate is locked at 18:00 or that the customer needs a 20-minute unload. Reachability is not serviceability.
Try it
The full parameter table, the error envelope and the sibling endpoints are in
the analysis APIs documentation, which is authoritative on
request and response shapes. A key comes from one card-free call to
POST /v1/keys and works immediately, and the
playground runs the request in a browser with no install.
Routing derives from OpenStreetMap, so credit "© OpenStreetMap contributors" when you render or republish it.
