Skip to content

Route Preferences

By default Directions returns the fastest route for the mode. The options on this page change that. You can lean away from tolls without banning them, cap the vehicle’s speed, say which way a vehicle faces at the start, pass a pickup point without stopping, or ask what the route you got would cost on foot.

Preferences and penalties change which route is chosen. They never change duration_s. The same goes for the turn costs motor vehicles pay by default for left turns across traffic and the like. That is always the travel time of the route you get back, so a 20-minute toll-free detour reports 20 minutes, not 20 minutes plus whatever penalty made it win. Speed options (top_speed, fixed_speed) are different: they change how fast you drive, so they change duration_s too.

All of these are top-level fields of the POST /route body. /matrix takes the costing options, /isochrone takes the two speed options, and PlazaQL takes all of them as keyword arguments with the same names, values and errors.

Each preference is a number from 0 to 1. Leave it out, or send 0.5, and that kind of road is priced as usual. Below 0.5 its cost goes up, to the factor in the table at 0, so the router uses it only when the alternative is much worse. Above 0.5 its cost goes down, to 0.9x at 1, which tips close calls in its favour.

FieldRoadsAt 0Modes
use_highwaysMotorways and trunk roads, with their links10xauto, truck, motorcycle, motor_scooter
use_tollsWays tagged toll=yes10xauto, truck, motorcycle, motor_scooter
use_ferriesFerry routes (route=ferry), see below10xevery mode
use_living_streetshighway=living_street5xevery mode
use_trackshighway=track15xfoot, bicycle

Ferries are part of the road network. A ferry takes as long as its OSM duration tag says; without one it sails at 20 km/h. Cars, trucks, motorcycles, walkers and cyclists all board unless the ferry’s tags keep them off (motor_vehicle=no on a foot ferry, say). use_ferries: 0 keeps you on land unless the drive round is ten times longer than the crossing. exclude: "ferry" rules ferries out completely.

Factors multiply. A tolled motorway with use_tolls: 0 and use_highways: 0 costs 100x its travel time.

An option a mode doesn’t take is a 422, so use_tolls with mode: "foot" fails instead of being ignored. Cars and trucks never drive tracks, which is why use_tracks is foot and bicycle only. Motorcycles have their own use_trails option instead, see Motorcycle and Scooter Routing. To keep off a road type completely, use exclude instead.

Terminal window
curl -X POST https://plaza.fyi/api/v1/route \
-H "x-api-key: pk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"origin": { "type": "Point", "coordinates": [-74.0060, 40.7128] },
"destination": { "type": "Point", "coordinates": [-73.9855, 40.7580] },
"use_tolls": 0,
"use_highways": 0.3
}'

"shortest": true minimises distance instead of time. Travel time only breaks ties between equally long routes. duration_s is still the time it takes to drive the short route. Turn costs don’t apply. shortest can’t be combined with a preference or a penalty (422), because they would mix metres with seconds. It works for every mode.

FieldRangeModesEffect
top_speed10-252 km/h, whole numbersauto, truck, motorcycle, motor_scooterThe vehicle never goes faster. Roads with a higher speed are driven at this speed. A motor scooter is capped at 45 km/h without it, and top_speed replaces that cap.
fixed_speed1-252 km/h, whole numbersauto, truck, motorcycle, motor_scooterEvery road is driven at exactly this speed. Road class, speed limits, surface, slope and the time of day are ignored; waits at junctions and signals still count. Wins over top_speed.

Both change the route and its duration_s. Use top_speed for a vehicle that is slower than the traffic around it, such as a moped, a tractor or a bus with a governor. Use fixed_speed when you already know your average speed and want Plaza to only find the road.

Penalties are seconds added to the cost of part of the route. Like preferences, they change the choice and not duration_s. Each takes 0 to 3600.

FieldAdded forModes
maneuver_penaltyEach junction delay the route reaches: intersections, and traffic signals, stop signs or crossings where two roads meet. It counts junctions passed, not turns: going straight on through a crossing pays it too. A road without junctions pays nothing, however Plaza splits it. Higher values favour routes through fewer junctions and stops. Roads Plaza hasn’t modelled junctions for yet carry no penalty.auto, truck, motorcycle, motor_scooter
service_penaltyEach service road (highway=service) the route turns into from another kind of road: parking aisles, alleys, driveways. A service road crossed by other streets still pays once.auto, truck, motorcycle, motor_scooter
gate_costEach gate passed (barrier=gate, lift_gate, swing_gate, sliding_gate, hampshire_gate), unless the gate is tagged access=yes, permissive, designated or public.every mode
toll_booth_costEach toll booth (barrier=toll_booth) passed. The 15 s a booth stop takes is already in duration_s; this only makes booths less attractive.auto, truck, motorcycle, motor_scooter
country_crossing_costEach border crossed: a barrier=border_control node, or a road leaving one country for another.every mode
{
"origin": { "type": "Point", "coordinates": [2.3522, 48.8566] },
"destination": { "type": "Point", "coordinates": [2.2945, 48.8584] },
"maneuver_penalty": 20,
"service_penalty": 60
}

Some of this happens without any option. A bollard, block or chain across a road closes it to cars, trucks, motorcycles and scooters but not to walkers and cyclists. A stile, kissing gate, turnstile, log or debris lets only walkers through, and a wall, fence, rope or jersey barrier lets nobody through. A locked gate, or one tagged access=private or access=no, is closed to everyone its own tags don’t let through: foot=yes on a private gate still lets walkers past. A barrier where roads meet stops traffic passing it, not reaching it: with a bollard where a footway crosses a street, you can drive up to the bollard from either end of the street. A trip that starts at the bollard can leave it either way, on routes, matrices and isochrones alike, but it still can’t drive through. Roads under construction (highway=construction) are closed to every mode. A road tagged access=no is closed too, unless a more specific tag such as foot=yes or bicycle=yes opens it for that mode.

Closures that only hold at some times follow depart_at. Say a mountain pass is tagged access:conditional = no @ (Nov-Apr). It is closed to a route leaving in January and open to one leaving in July:

{
"origin": { "type": "Point", "coordinates": [8.5683, 46.5592] },
"destination": { "type": "Point", "coordinates": [8.4167, 46.5594] },
"mode": "auto",
"depart_at": "2026-07-15T09:00:00+02:00"
}

Without depart_at the road keeps its plain value, which for that pass is open. maxspeed:conditional works the same way, and trucks also read conditional weight, height, width, length and axle-load limits.

Other gates stay open, but drivers pay 330 s of routing cost for each gate whose access isn’t tagged, so a route only goes through one when the way round is more than five and a half minutes slower. Walkers and cyclists don’t pay it. gate_cost adds to it, for every mode. As with every penalty, duration_s stays the real travel time.

Drivers (cars, trucks, motorcycles and scooters) also pay 300 s of routing cost to board a ferry from a road, and 15 s to turn onto a track from another kind of road. Valhalla adds the gate’s 30 s and the ferry’s 300 s to its arrival time. Plaza doesn’t: they only decide which route wins, and duration_s is the time on the road and the ferry’s sailing time. Matrices, isochrones and the other computations that price travel time alone leave all of them out.

location_types has one entry per point: origin, each waypoint, destination.

TypeMeaning
breakA stop. The route is split into legs here and gets a waypoint step. This is the default, and the origin and destination must be break.
throughPass this point without stopping. No new leg, no arrival step, and no turning back here: for the rest of the way to the next point, the route can’t use the road segment it came in on at all.
viaLike through, but turning back is allowed. Use it to pull a route past a point that sits at a dead end.
Terminal window
curl -X POST https://plaza.fyi/api/v1/route \
-H "x-api-key: pk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"origin": { "type": "Point", "coordinates": [-74.0060, 40.7128] },
"destination": { "type": "Point", "coordinates": [-73.9855, 40.7580] },
"waypoints": [{ "type": "Point", "coordinates": [-73.9866, 40.7306] }],
"location_types": ["break", "through", "break"],
"steps": true
}'

With steps, this route has one leg and no waypoint step. waypoint_names still has one entry per point; names of through and via points are not used.

These pick which way the vehicle travels at a point. Like location_types they have one entry per point, with null for points that don’t need one.

FieldValuesMeaning
headings0-360Direction of travel, degrees clockwise from north.
heading_tolerances0-180, default 60How far the road may differ from the heading. Needs a heading at the same point.
preferred_sidessame, opposite, eithersame keeps the point on the vehicle’s driving side (the right, or the left in left-hand-traffic countries, Hong Kong and Macau included), so a passenger gets out at the curb. opposite puts it across the road. either is the default.
approachescurb, unrestrictedMapbox’s name for the same thing. curb is preferred_sides: same; unrestricted is the default. Sending curb and opposite for the same point is a 422.

With a heading or a side, the point snaps to the nearest road within 500 m that can be travelled that way, and the route goes past the point in that direction. It may not use that road the other way round, so a car facing east at the start drives east first, even if its destination is behind it. The exception is a dead end: facing into a cul-de-sac, the route turns round at its end rather than failing. When the origin and destination are on the same road, facing the same way with the destination ahead, the route is just that road. If no road nearby fits, or the one that fits is ruled out by exclude, avoid_datasets or a truck limit, the point snaps as if neither option were set. So does a point that lies exactly on the road with only preferred_sides set, since it has no side.

{
"origin": { "type": "Point", "coordinates": [-73.9925, 40.7505] },
"destination": { "type": "Point", "coordinates": [-73.9855, 40.7580] },
"headings": [90, null],
"preferred_sides": [null, "same"]
}

Routes start and end at junctions. When the origin is in the first half of its road segment, the route includes that segment; in the second half, it starts at the next junction. The destination works the other way round. A waypoint’s segment is always included. Alternatives start and end at the junctions without the segment.

recostings prices the route you got under up to five other costing models, without routing again. Each one has a name, an optional mode (auto, foot, bicycle, motorcycle or motor_scooter, defaulting to the route’s own mode; truck routes must name one) and any of the options above. A bicycle, walking or motorcycle recosting uses the request’s options for that mode when the route has them, else the defaults.

Terminal window
curl -X POST https://plaza.fyi/api/v1/route \
-H "x-api-key: pk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"origin": { "type": "Point", "coordinates": [-74.0060, 40.7128] },
"destination": { "type": "Point", "coordinates": [-73.9855, 40.7580] },
"recostings": [
{ "name": "slow_van", "top_speed": 80 },
{ "name": "no_tolls", "use_tolls": 0 },
{ "name": "walk", "mode": "foot" }
]
}'

The route’s properties then include:

"recostings": {
"slow_van": { "duration_s": 1320.4, "cost": 1320.4 },
"no_tolls": { "duration_s": 1104.9, "cost": 4870.2 },
"walk": { "duration_s": null, "cost": null }
}
  • duration_s is the travel time of the same path under that model: the same road, driven at that mode’s speeds, with the request’s depart_at.
  • cost is what the router would have scored the path: seconds with preferences and penalties added, or metres with shortest. Like duration_s, it leaves out waiting at the junction where the route ends. Comparing cost across recostings shows how much a preference dislikes the route you took.
  • Both are null when that mode can’t use the path, for example walking on a motorway or cycling the wrong way down a one-way street.

Each entry can also be a string, "name: key=value, ...", which is the form PlazaQL uses: "no_tolls: use_tolls=0" or "walk: mode=foot". Waypoint routes sum the legs; alternatives each get their own.

/matrix takes every costing option. Pairs follow the route the options and turn costs pick, and duration_s is its travel time.

/isochrone takes top_speed and fixed_speed. An isochrone’s budget is travel time, so preferences and penalties have nothing to change there and are a 422.

Everything here is checked before routing. A bad value is a 422 validation_error naming the field:

  • a number out of range: use_tolls must be a number from 0 to 1
  • an option the mode doesn’t take: use_tolls applies to mode auto, truck, motorcycle or motor_scooter, not foot (the same for toll_booth_cost)
  • shortest with a preference or penalty: shortest cannot be combined with use_tolls
  • a per-point list of the wrong length: headings must have one entry per point (origin, waypoints, destination): expected 3, got 2
  • a recosting without a name, with a repeated name or an unknown option
  • location_types, headings, heading_tolerances, preferred_sides, approaches or recostings with ev
  • any of these options with mode: "transit"

route() takes all of these as keyword arguments, matrix() and ev_route() take the costing options, and isochrone() takes the speeds. PlazaQL has no null, so per-point options use the ;-separated string form when some points have no value, and recostings are strings:

route(
origin: point(40.7128, -74.0060),
destination: point(40.7580, -73.9855),
waypoints: [point(40.7306, -73.9866)],
use_tolls: 0,
top_speed: 100,
location_types: ["break", "through", "break"],
headings: "90;;",
preferred_sides: ";;same",
recostings: ["walk: mode=foot"]
);

REST accepts the string forms too, so "headings": "90;;" and "headings": [90, null, null] mean the same thing.