Skip to main content

Maximum Trips Per Vehicle

In some logistics scenarios, it's necessary to limit the number of trips a vehicle can make from a depot. For example, a driver might have a maximum number of routes they can perform in a shift, or a vehicle might need to return to the depot for reloading or maintenance after a certain number of tours.

This constraint is particularly relevant for operations using a Pickup and Delivery (PDP) model, where a vehicle can perform multiple trips, returning to a depot between them.

tip

The model supports setting end of trip for either pick ups or drop offs further enhancing flexibility for defining what the trip is.

How It Works

To enforce a maximum number of trips, two key parameters must be configured in a Stateless API request:

  1. number_of_trips: This parameter is set on the vehicle object and defines the maximum number of trips that the vehicle is allowed to perform.
  2. end_of_trip: true: This flag must be set on the node that represents the pick up or drop off. It signals to the optimizer that visiting this node completes a trip, allowing the vehicle to start a new one if its limit has not been reached.

When does a trip end?

Understanding exactly when a trip ends is important for configuring constraints correctly, especially at depot locations where both drop-offs (returns) and pickups (new jobs) might occur.

  • Delivery ends a trip: If a vehicle performs a delivery at a depot (and there are no further actions for that specific run), this delivery marks the end of the trip.
  • Pickup starts a trip: If a vehicle performs a pickup at a depot, this action typically marks the start of a new trip. Any of hte consequent dropoff can be considered the end of trip. If a vehicle returns for pickup, it is considered as a new trip.
  • Simultaneous Delivery and Pickup: If a vehicle arrives at a depot to both drop off goods (from the previous run) and pick up new goods (for the next run):
    • The Drop-off constitutes the Last Stop of the current trip (Trip i).
    • The Pickup constitutes the First Stop of the next trip (Trip i+1).

When a vehicle completes a delivery and returns to a node marked as end_of_trip: true, the optimizer counts this as one completed trip. It will only assign a new trip to the vehicle if the total number of completed trips is less than the number_of_trips limit.

Impact on Backhauls vs Deliveries

It is important to visualize the impact of end_of_trip: true on different logistics models, especially when number_of_trips is limited (e.g., set to 1).

  • Backhaul (Return to Depot/DC): In a backhaul scenario, the vehicle typically performs a pickup (e.g., from a supplier) and returns to the Distribution Center (DC) to drop it off. Here, the DC drop-off is marked as end_of_trip: true. The vehicle completes one full cycle (Depot -> Supplier -> DC), which counts as 1 completed trip. This is the standard usage.
  • Deliveries (Drop-offs): It is also acceptable to mark all customer drop-off nodes as end_of_trip: true. This configuration prohibits consecutive visits (separate trips) to these locations. With number_of_trips: 1, the vehicle can still perform its delivery run (visiting multiple drop-offs in sequence if optimal) but will be prevented from starting a second separate trip consisting of these locations. Note that any interruption in the sequence of the nodes with end_of_trip set to true would be considered as another trip. In addition, this configuration prevents backhauls: since the final delivery marks the end of the trip, any subsequent pickup (e.g., for a return load) would be treated as starting a new (second) trip.
tip

Related constraint is maximum number of pickups and\or dropoff locations for a vehicle.

End of Trip in Integration API through simulation settings

When using the Stateful API (e.g., via order upload), you can specify which booking locations should be considered as the end of a trip by adding the following parameters to the data object of the simulation:

  • is_dropoff_end_of_trip: Set to true if the drop-off for this booking can be considered as the end of a trip (limiting the number of trips vehicle constraint).
  • is_pickup_end_of_trip: Set to true if the pickup for this booking can be considered as the end of a trip (limiting the number of trips vehicle constraint).
tip

Alternatively, the flag can be set on the selected nodes using update node API if only certain nodes have to be marked as end of trip if not all nodes have to be marked in the simulation on bookings upload.

Example

Here is a JSON snippet for a node_scheduler request that demonstrates this constraint.

  • The vehicle is limited to one trip. Its starting point is explicitly set to the depot using partial_route.
  • There are three bookings (A, B, and C). Bookings A and B are close to the depot and can be served in a single trip. Booking C is far away and has a later time window, making it impossible to serve in the same trip as A and B.
  • The depot node is marked with end_of_trip: true.

Because the vehicle is limited to one trip, the optimizer will serve bookings A and B and reject booking C.

{
"current_time": "2025-11-21T08:00:00Z",
"engine_settings": {
"calculation_parameters": {
"calculations_mode": "sync",
"scheduling_mode": "prebook"
},
"model_parameters": {
"optimize_quantity": "total_time",
"booking_penalty": 100000
},
"routing_engine": {
"routing_engine_name": "osrme",
"url": "http://mapbox-osrm-proxy",
"road_network": "van"
}
},
"nodes": [
{
"uid": "depot",
"node_type": "depot",
"lat": 1.3521,
"lon": 103.8198,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T18:00:00Z",
"close_time_ts_dynamic": "2025-11-21T18:00:00Z",
"service_time": 0,
"demand": {},
"end_of_trip": true
},
{
"uid": "pickup_A",
"booking_uid": "booking_A",
"node_type": "pickup",
"lat": 1.306,
"lon": 103.832,
"open_time_ts": "2025-11-21T09:00:00Z",
"close_time_ts": "2025-11-21T10:00:00Z",
"close_time_ts_dynamic": "2025-11-21T10:00:00Z",
"service_time": 300,
"demand": { "units": 10 }
},
{
"uid": "dropoff_A",
"booking_uid": "booking_A",
"node_type": "dropoff",
"lat": 1.283,
"lon": 103.858,
"open_time_ts": "2025-11-21T10:00:00Z",
"close_time_ts": "2025-11-21T11:00:00Z",
"close_time_ts_dynamic": "2025-11-21T11:00:00Z",
"service_time": 300,
"demand": { "units": -10 }
},
{
"uid": "pickup_B",
"booking_uid": "booking_B",
"node_type": "pickup",
"lat": 1.32,
"lon": 103.9,
"open_time_ts": "2025-11-21T09:00:00Z",
"close_time_ts": "2025-11-21T10:00:00Z",
"close_time_ts_dynamic": "2025-11-21T10:00:00Z",
"service_time": 300,
"demand": { "units": 10 }
},
{
"uid": "dropoff_B",
"booking_uid": "booking_B",
"node_type": "dropoff",
"lat": 1.34,
"lon": 103.92,
"open_time_ts": "2025-11-21T10:00:00Z",
"close_time_ts": "2025-11-21T11:00:00Z",
"close_time_ts_dynamic": "2025-11-21T11:00:00Z",
"service_time": 300,
"demand": { "units": -10 }
},
{
"uid": "pickup_C",
"booking_uid": "booking_C",
"node_type": "pickup",
"lat": 1.449,
"lon": 103.801,
"open_time_ts": "2025-11-21T14:00:00Z",
"close_time_ts": "2025-11-21T15:00:00Z",
"close_time_ts_dynamic": "2025-11-21T15:00:00Z",
"service_time": 300,
"demand": { "units": 5 }
},
{
"uid": "dropoff_C",
"booking_uid": "booking_C",
"node_type": "dropoff",
"lat": 1.43,
"lon": 103.79,
"open_time_ts": "2025-11-21T15:00:00Z",
"close_time_ts": "2025-11-21T16:00:00Z",
"close_time_ts_dynamic": "2025-11-21T16:00:00Z",
"service_time": 300,
"demand": { "units": -5 }
}
],
"vehicles": [
{
"agent_id": "vehicle_1",
"start_time": "2025-11-21T08:00:00Z",
"end_time": "2025-11-21T18:00:00Z",
"capacity": { "units": 50 },
"number_of_trips": 1,
"partial_route": ["depot"]
}
]
}

Playground

You can experiment with this constraint in the playground below.

  • Run the simulation with number_of_trips: 1 and observe that Booking C is rejected.
  • Change the number_of_trips to 2 and run it again. The optimizer will now be able to perform a second trip to service Booking C.
Loading...