Delivery optimization
Assign your stops to your vehicles and order each vehicle's stops, so the fleet drives as little as possible while meeting delivery windows, vehicle capacities and working hours. This is the vehicle routing problem with time windows (VRPTW).
How it works
You describe the vehicles (where they start and end, how much they carry, when they work, which skills they have) and the stops (where they are, how long each takes, how many units it gets, when it can be served, which skills it needs). Lanewise finds the routes with the least travel time on the road network and returns, for each vehicle, its ordered stops with arrival and departure times. Stops that no vehicle can serve are returned in unassigned with a reason.
- Times are ISO 8601 datetimes, such as
2026-10-01T08:00:00+02:00; a datetime without an offset is UTC. The response uses the offset of the first datetime in your request, or UTC when you send none. Without any datetime, vehicles leave now. - Durations are in seconds and distances in meters.
- Capacity: when at least one vehicle has a
capacity, each stop'sdemandcounts against it (1 unit by default) and vehicles without a capacity carry any amount. Without capacities,demandis ignored. - End: a vehicle without
endfinishes at its last stop. For a round trip, setendto the same location asstart. - Skills: a stop with skills is only served by a vehicle that has all of them, e.g.
fridgefor chilled goods.
Optimization requires the plan-routes permission. When a stop, start or end is a POI, the key also needs read-pois. Each call counts as one request towards your rate limit.
Limits
- Name
Stops- Description
Between 1 and 50 per request. To plan more, cluster them first and optimize each cluster.
- Name
Vehicles- Description
Between 1 and 3 per request.
- Name
Time windows- Description
Up to 5 per stop; they must not overlap. A stop's
servicetime can be up to 24 hours.
Errors
Besides the usual errors, optimization can answer:
- Name
422- Description
The body is invalid, a POI does not exist in your team, or a location cannot be reached by road with the chosen profile. The
messagesays which.
- Name
502- Description
The routing service failed to answer. Retry in a moment.
- Name
503- Description
Routing is not available right now, or is busy. Retry later.
503 Service Unavailable
{ "message": "Routing is not available yet. Please try again later or contact [email protected]." }
Optimize deliveries
Assign the stops to the vehicles and order them. Requires the plan-routes permission, plus read-pois when you use POIs.
Required attributes
- Name
vehicles- Type
- array
- Description
1 to 3 vehicles.
- Name
vehicles[].start- Type
- object
- Description
Where the vehicle starts:
{ "latitude", "longitude" }or{ "poi_id" }.
- Name
stops- Type
- array
- Description
1 to 50 stops. Each is
{ "latitude", "longitude" }or{ "poi_id" }.
Optional attributes
- Name
profile- Type
- string
- Description
driving-car(default),driving-hgvfor trucks,cycling-regularorfoot-walking.
- Name
vehicles[].id- Type
- string
- Description
Your name for the vehicle, unique, up to 100 characters. Defaults to
vehicle-1,vehicle-2…
- Name
vehicles[].end- Type
- object
- Description
Where the vehicle finishes. Omit it to finish at the last stop.
- Name
vehicles[].capacity- Type
- integer
- Description
Most units the vehicle carries.
- Name
vehicles[].time_window- Type
- array
- Description
Working hours as
[start, end]datetimes.
- Name
vehicles[].skills- Type
- array
- Description
Up to 20 skills, such as
fridgeortail-lift.
- Name
stops[].id- Type
- string
- Description
Your reference for the stop, unique, up to 100 characters. Echoed back.
- Name
stops[].service- Type
- integer
- Description
Seconds spent at the stop. Defaults to 0.
- Name
stops[].demand- Type
- integer
- Description
Units delivered at the stop. Defaults to 1; only counts when a vehicle has a
capacity.
- Name
stops[].time_windows- Type
- array
- Description
When the stop can be served: up to 5 non-overlapping
[start, end]pairs of datetimes. A vehicle arriving early waits.
- Name
stops[].skills- Type
- array
- Description
Skills the vehicle needs, up to 20.
Request
curl https://api.lanewise.app/v1/routes/optimize \
-H "X-API-KEY: {YOUR_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"profile": "driving-car",
"vehicles": [
{
"id": "van-1",
"start": { "latitude": 41.3870, "longitude": 2.1701 },
"end": { "latitude": 41.3870, "longitude": 2.1701 },
"capacity": 10,
"time_window": ["2026-10-01T08:00:00+02:00", "2026-10-01T14:00:00+02:00"]
}
],
"stops": [
{
"id": "order-1",
"latitude": 41.4036,
"longitude": 2.1744,
"service": 300,
"demand": 4,
"time_windows": [["2026-10-01T09:00:00+02:00", "2026-10-01T10:00:00+02:00"]]
},
{ "id": "order-2", "poi_id": "01JGZ8Q3N5T9B2C4D6F8H0K2M4", "service": 120 },
{ "id": "order-3", "latitude": 41.4145, "longitude": 2.1527, "demand": 20 }
]
}'
Response
{
"data": {
"summary": {
"vehicles": 1,
"stops": 3,
"assigned": 2,
"unassigned": 1,
"distance": 5400.5,
"duration": 900,
"service": 420,
"waiting_time": 2400
},
"routes": [
{
"vehicle": "van-1",
"stops": 2,
"distance": 5400.5,
"duration": 900,
"service": 420,
"waiting_time": 2400,
"load": 5,
"start_at": "2026-10-01T08:00:00+02:00",
"end_at": "2026-10-01T09:12:00+02:00",
"geometry": {
"type": "LineString",
"coordinates": [[2.1701, 41.387], [2.1649, 41.3917], [2.1744, 41.4036], [2.1701, 41.387]]
},
"steps": [
{
"type": "start",
"latitude": 41.387,
"longitude": 2.1701,
"arrival": "2026-10-01T08:00:00+02:00",
"departure": "2026-10-01T08:00:00+02:00",
"waiting_time": 0,
"service": 0,
"distance": 0,
"duration": 0,
"load": 5
},
{
"type": "stop",
"index": 1,
"id": "order-2",
"poi_id": "01JGZ8Q3N5T9B2C4D6F8H0K2M4",
"latitude": 41.3917,
"longitude": 2.1649,
"arrival": "2026-10-01T08:03:00+02:00",
"departure": "2026-10-01T08:05:00+02:00",
"waiting_time": 0,
"service": 120,
"distance": 900,
"duration": 180,
"load": 4
},
{
"type": "stop",
"index": 0,
"id": "order-1",
"poi_id": null,
"latitude": 41.4036,
"longitude": 2.1744,
"arrival": "2026-10-01T08:20:00+02:00",
"departure": "2026-10-01T09:05:00+02:00",
"waiting_time": 2400,
"service": 300,
"distance": 2700,
"duration": 480,
"load": 0
},
{
"type": "end",
"latitude": 41.387,
"longitude": 2.1701,
"arrival": "2026-10-01T09:12:00+02:00",
"departure": "2026-10-01T09:12:00+02:00",
"waiting_time": 0,
"service": 0,
"distance": 5400.5,
"duration": 900,
"load": 0
}
]
}
],
"unassigned": [
{
"index": 2,
"id": "order-3",
"poi_id": null,
"latitude": 41.4145,
"longitude": 2.1527,
"reason": "Its demand exceeds the capacity of every vehicle."
}
]
}
}
Response
- Name
summary- Type
- object
- Description
Totals for the whole fleet:
vehicleswith a route,stops,assignedandunassignedstops, and thedistance, drivingduration,serviceandwaiting_timeof all routes.
- Name
routes[].vehicle- Type
- string
- Description
The
idof the vehicle. Vehicles without stops have no route.
- Name
routes[].load- Type
- integer | null
- Description
Units loaded at the start.
nullwhen no vehicle has a capacity.
- Name
routes[].start_at / end_at- Type
- datetime
- Description
When the vehicle leaves its start and reaches its end (or last stop).
- Name
routes[].geometry- Type
- object
- Description
The route as a GeoJSON
LineString, coordinates as[longitude, latitude].
- Name
routes[].steps- Type
- array
- Description
The
start, eachstopin visiting order, and theend. Every step has its position,arrivalanddeparturetimes,waiting_time(early arrivals wait for the time window),service, thedistanceand drivingdurationso far, and theloadon board after it. Stops also have theirindexin your request,idandpoi_id.
- Name
unassigned- Type
- array
- Description
Stops no vehicle can serve, with a
reason: the demand exceeds every capacity, no vehicle has the required skills, the time windows fall outside every vehicle's working hours, or no vehicle can reach it in time with the capacity and hours left.