Truck Routing
A car route will happily send a 4.1 m semi-trailer under a 3.8 m bridge. Set mode to truck and describe the vehicle, and Plaza routes around every road whose posted limit the truck exceeds.
Truck mode works on Directions, Isochrones and the Distance Matrix. All three are premium endpoints, so each request counts as 4x. Truck mode and its options also work on Route Optimization, the Meeting Point, Map Matching and Routing Debug.
What truck mode does
Section titled “What truck mode does”- Skips restricted roads. An edge is left out when its OSM
maxheight,maxwidth,maxlength,maxweightormaxaxleloadis below the truck’s value. Roads with no limit tagged stay in. - Avoids hazmat restrictions when
truck.hazmatistrue: roads taggedhazmat=noornoon any hazmat class (hazmat:water,hazmat:explosive,hazmat:B, …; Plaza doesn’t ask for the load’s class, so every class counts), and tunnels in ADR category B to E (hazmat:adr_tunnel_cat). A tunnel without those tags is not avoided. - Keeps to roads trucks may use. Motorways down to living streets, no footways, paths or tracks.
hgv=nocloses a road.hgv=yesopens one thataccess=nowould close. Without anhgvtag,motor_vehicle, thenvehicle, thenaccessdecide. - Follows one-way streets and turn restrictions, including truck-only ones (
restriction:hgv).except=hgvlets trucks make the turn. - Times the trip at truck speeds, capped by the road’s
maxspeed(or the country’s default limit when the road has none), slowed by rough surfaces and uphill grades, plus the same junction and traffic-signal delays as cars. Withdepart_at, the same time-of-day profile as car routes scales them. See how Plaza prices a road.
| Road class | Truck speed (km/h) |
|---|---|
| motorway | 100 |
| trunk | 85 |
| primary | 70 |
| secondary | 60 |
| tertiary | 50 |
| unclassified | 40 |
| residential | 30 |
| service | 20 |
| living_street | 10 |
Link roads are slower: 60 km/h for motorway_link down to 35 km/h for tertiary_link.
OSM writes limits in many forms. Plaza reads 4.2, 4,2, 4.2 m, 420 cm, 13'6", 13 ft, 7.5 t, 7.5 to, 7500 kg, 7,500 kg, 16000 lbs and US short tons (20 st), and ignores values like none or default that carry no limit. A weight without a unit is in tonnes, OSM’s default, so 7,500 is 7.5 t. ton and tons are read as metric tonnes. When a road has both maxheight and maxheight:physical, the lower one applies. The same goes for maxwidth:physical, maxweightrating and maxweight:hgv. Conditional limits such as maxweight:conditional (a limit that only applies at certain times) are ignored.
Where Plaza has elevation for a road, climbs slow trucks more than cars: 80% of the speed on a 2-4% grade, 60% at 4-6%, 45% at 6-8% and 30% above that. Downhill stretches run at the normal speed. Grades are averaged over each OSM way, so a road over a hill with both ends at the same height counts as flat.
The vehicle
Section titled “The vehicle”Send the vehicle in a truck object. Anything you leave out takes the default, which matches a standard US semi-trailer.
| Field | Type | Default | Range |
|---|---|---|---|
truck.height | number | 4.11 | Metres, above 0, at most 10 |
truck.width | number | 2.6 | Metres, above 0, at most 10 |
truck.length | number | 21.64 | Metres, above 0, at most 60 |
truck.weight | number | 21.77 | Gross weight in tonnes, above 0, at most 200 |
truck.axle_load | number | none | Heaviest axle in tonnes, above 0, at most 50 |
truck.axle_count | integer | none | 2 to 20 |
truck.hazmat | boolean | false | Carrying hazardous materials |
Axle-load limits are checked against axle_load. Without it, Plaza checks weight / axle_count, and with neither it doesn’t check axle loads at all.
A value out of range, an unknown field, or a truck object without mode: "truck" is a 422 validation_error.
A route
Section titled “A route”A 4 m truck carrying 30 t of fuel:
curl -X POST https://plaza.fyi/api/v1/route \ -H "x-api-key: $PLAZA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "origin": { "type": "Point", "coordinates": [-87.6298, 41.8781] }, "destination": { "type": "Point", "coordinates": [-87.9073, 41.9742] }, "mode": "truck", "truck": { "height": 4.0, "weight": 30, "hazmat": true } }'The response is the usual route Feature, with three extra properties:
{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [[-87.6298, 41.8781], "..."] }, "properties": { "distance_m": 28417.3, "duration_s": 1342.8, "mode": "truck", "truck": { "height": 4.0, "width": 2.6, "length": 21.64, "weight": 30.0, "hazmat": true }, "warnings": [ { "type": "restriction_near_route", "restriction": "maxheight", "message": "3.8 m height restriction on a nearby road", "location": [-87.6512, 41.8899], "osm_way_id": 123456789 } ] }}warnings lists up to 10 restrictions the truck exceeds on roads within about 30 m of the route: the side street with the low bridge, the parallel road with the weight limit. The route doesn’t use them, but a driver who takes a wrong turn will meet them. The list is empty when there’s nothing nearby. Routes with alternatives carry mode and truck but no warnings.
The same route in PlazaQL. The vehicle fields become keyword arguments:
route( origin: point(41.8781, -87.6298), destination: point(41.9742, -87.9073), mode: "truck", height: 4.0, weight: 30, hazmat: true);When no route fits
Section titled “When no route fits”If the road network connects the two points but every path crosses a restriction the truck exceeds, you get a 404 with code no_route. details.restrictions lists what blocked the path the truck would otherwise have taken, up to 20 entries in route order:
{ "error": { "code": "no_route", "message": "No route fits the vehicle: every path crosses a height, width, length, weight or hazmat restriction it exceeds.", "details": { "reason": "vehicle_dimensions_exceeded", "restrictions": [ { "type": "maxheight", "limit": 3.5, "vehicle": 4.11, "osm_way_id": 123456789, "location": [-87.6512, 41.8899] } ] } }}A hazmat entry has a limit of null. When the points aren’t connected at all, you get the ordinary not_found instead. The explanation is only given for routes without waypoints.
Limits that change with the time
Section titled “Limits that change with the time”Some limits only hold at certain times, or only for heavier vehicles. Send depart_at and Plaza applies the conditional ones in force at that local time: hgv:conditional, maxweight:conditional, maxheight:conditional, maxwidth:conditional, maxlength:conditional, maxaxleload:conditional, plus the access and maxspeed:conditional tags every mode reads. A town centre tagged hgv:conditional = no @ (Mo-Sa 07:00-19:00) is closed to this truck at 08:30 on a Tuesday and open to it at 21:00:
curl -X POST https://plaza.fyi/api/v1/route \ -H "x-api-key: $PLAZA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "origin": { "type": "Point", "coordinates": [-87.6298, 41.8781] }, "destination": { "type": "Point", "coordinates": [-87.9073, 41.9742] }, "mode": "truck", "truck": { "weight": 30 }, "depart_at": "2026-03-24T08:30:00-05:00" }'Vehicle conditions are checked against your truck object, so maxweight:conditional = 7.5 @ (weight>3.5) binds a 30 t truck and not a 3 t van. Without depart_at, time conditions never hold and each road keeps its plain value. The directions guide lists the conditions Plaza reads.
Isochrones and matrices
Section titled “Isochrones and matrices”mode and truck work the same way on POST /api/v1/isochrone and POST /api/v1/matrix. A truck isochrone shows where the truck can get to in the time budget without breaking a limit:
curl -X POST https://plaza.fyi/api/v1/isochrone \ -H "x-api-key: $PLAZA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "geometry": { "type": "Point", "coordinates": [-87.6298, 41.8781] }, "time": 1800, "mode": "truck", "truck": { "height": 4.3, "weight": 40 } }'isochrone(center: point(41.8781, -87.6298), time: 1800, mode: "truck", height: 4.3, weight: 40);
matrix( origins: [point(41.8781, -87.6298)], destinations: [point(41.9742, -87.9073), point(41.7868, -87.7522)], mode: "truck", axle_count: 5);Neither takes depart_at for trucks, so conditional limits don’t apply there. Each road keeps its plain value.
In PlazaQL the vehicle keywords need mode: "truck" and only route(), isochrone(), matrix(), optimize(), centroid(), expansion(), map_match() and ev_route() accept them. Anything else fails the query with a 400 query_error.
Electric trucks
Section titled “Electric trucks”Add ev to a truck route and Plaza plans the charging stops for an electric truck. The route keeps every truck rule above. Consumption scales with truck.weight: about 300 Wh/km, plus 22.5 Wh/km per tonne, plus 30 Wh/km for auxiliaries. That works out to about 0.8 kWh/km at the default 21.77 t and 1.2 kWh/km for a 40 t tractor-trailer. Climbs cost the energy it takes to lift the truck’s mass, and some of it comes back downhill.
Unless you set connector_types, the truck only stops at CCS (type2_combo, type1_combo) and MCS (socket:mcs) chargers. Plaza doesn’t know whether a truck fits a charging site, so check the stops before you rely on them.
curl -X POST https://plaza.fyi/api/v1/route \ -H "x-api-key: $PLAZA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "origin": { "type": "Point", "coordinates": [4.4777, 51.9244] }, "destination": { "type": "Point", "coordinates": [6.9603, 50.9375] }, "mode": "truck", "truck": { "weight": 40 }, "ev": { "battery_capacity_wh": 540000 } }'ev_route(origin: point(51.9244, 4.4777), destination: point(50.9375, 6.9603), battery: 540000, mode: "truck", weight: 40);The EV routing guide covers the charge profile and the other ev fields.
Limits of the data
Section titled “Limits of the data”Truck mode is only as good as the OSM tags. A bridge nobody has tagged with maxheight doesn’t stop the router, and a missing tag means no limit, not an unknown one. In well-mapped countries most tunnels and low bridges are tagged. In others, check the route before you send the truck.