Calculate custom costs per job

A new optimization algorithm is now the default for HERE Tour Planning API /async requests. Learn about the new algorithm →

The costPerJob feature enables charging a cost per specific job instead having to rely only on time, distance, or fixed costs. This capability lets you cover a wide variety of business models, such as:

  • Parcel/weight-based variable pricing. For example, when delivering heavier or multi-box orders incurs a higher handling fee.
  • Delivery Service Partner (DSP) rate cards. For example, outsourced contractors might charge different flat rates per stop.
  • Driver-customer affinity (soft preference for familiar drivers). For example, when customers prefer seeing the same driver regularly, but other drivers can serve them if routes become too inefficient.
📘

Note

This is an ALPHA feature. To enable it, add costPerJob to experimentalFeatures in the problem configuration.

For more information, see Explore experimental features.

Define per-job cost rate and cost factor

You set up job-specific costs in the following problem components:

  • costs.perJob.rate on a vehicle type. This is the base rate that represents the "fixed" cost that the optimization algorithm applies to all jobs that vehicles of this type complete. The following example shows how to configure the perJob.rate parameter in a vehicle type:

    {
      "costs": {
        "time": 0.005,
        "perJob": {
          "rate": 5
        }
      }
    }
  • plan.jobs[].costFactor. Represents a multiplier that corresponds to the "size" or "complexity" of a job from your business perspective. For example, you might set a higher cost factor if a job requires delivering heavy goods, involves handling multiple packages, and so on. If not specified, the default value is 1. The following example shows how to specify costFactor for an individual job:

    {
      "id": "Delivery_1",
      "costFactor": 4,
      "tasks": {
        "deliveries": [
          {
            "places": [
              {
                "duration": 300,
                "location": {
                  "lat": 52.5299,
                  "lng": 13.401
                }
              }
            ],
            "demand": [
              4
            ]
          }
        ]
      }
    }

Calculate the per-job cost

The API calculates each job's cost by multiplying the job's costFactor by the vehicle type's costs.perJob.rate.

📘

Per-job cost

costPerJob = costFactor × rate

For example, a job with costFactor: 4 and rate: 5 has a per-job cost of 4 × 5 = 20.

The solution returns the sum of all per-job costs as the perJob property within the statistic.costs object at the general solution level and for each individual tour. The following example shows the perJob property in the solution's statistic.costs object:

{
  "statistic": {
    "costs": {
      "distance": 5.49585,
      "perJob": 45,
      "time": 14.095
    }
  }
}

If you specify costFactor without configuring the perJob.rate parameter in a problem, the API triggers the costFactorWithoutPerJob notice, as shown in the following example:

"notices": [
  {
    "code": "costFactorWithoutPerJob",
    "title": "Jobs have costFactor set but no vehicle defines perJob cost: Delivery_1, Delivery_3",
    "action": "Add perJob to vehicle costs or remove costFactor from jobs"
  }
]

For more information, see API Reference.

Override the cost for specific jobs

The API also provides the costs.perJob.overrides[] parameter for more flexibility. By using this parameter you can ignore costFactor and perJob.rate settings for specific jobs when your business model requires a specific cost for certain known jobs regardless of their complexity or other factors.

For example, a rush delivery job might require priority handling with fixed $20 pricing, bypassing the normal per-job rate calculations.

The following example shows how to override the cost for specific job IDs:

{
  "costs": {
    "time": 0.005,
    "perJob": {
      "rate": 5,
      "overrides": [
        {
          "jobId": "RushDelivery",
          "cost": 20
        }
      ]
    }
  }
}

In this example, RushDelivery is assigned a flat cost of 20. This override bypasses both the vehicle's normal perJob.rate and any costFactor on the job.

Example: Assigning jobs to vehicles when capacity is limited

This sample scenario shows how per-job costs influence the following tour aspects:

  • Which vehicle type serves which job when the vehicle types have different per-job rates.
  • Which jobs the algorithm leaves unassigned when the fleet capacity is not enough to serve all of them.

The example problem consists of the following key components:

  • Fleet:
    • vehicle type id: vehicle_standard
      • vehicles available: 1
      • capacity: 4
      • perJob.rate: 5
    • vehicle type id: vehicle_premium
      • vehicles available: 1
      • capacity: 4
      • perJob.rate: 15
    • Both vehicle types start and end their shifts at the same depot and have identical time and distance costs. They differ only in the perJob.rate value.
  • Jobs: 10 deliveries, each with a demand of 1 and its own costFactor, which reflects the complexity of the job.

The following table shows the cost of each job for each vehicle type, calculated as costFactor × rate:

Job IDcostFactorvehicle_standard (rate: 5)vehicle_premium (rate: 15)
Job_CF1_A1515
Job_CF1_B1515
Job_CF2_A21030
Job_CF2_B21030
Job_CF5_A52575
Job_CF5_B52575
Job_CF6_A63090
Job_CF6_B63090
Job_CF10_A1050150
Job_CF10_B1050150
📘

Note

Consider the following aspects of this tour setup:

  • vehicle_standard is cheaper for every job, but it can serve only four of them. The more complex a job is, the more you save by assigning it to vehicle_standard.
  • Job_CF10_A and Job_CF10_B are the closest jobs to the depot (within 1 km), but they have the highest costFactor.
  • The total demand is 10, but the two vehicles can carry only 8 units together (2 × 4). The algorithm cannot serve all jobs and needs to decide which two to leave out.

The following figure visualizes the problem:

The following section shows the full problem configuration:

Click to expand/collapse the sample JSON
{
    "configuration": {
        "experimentalFeatures": [
            "costPerJob"
        ]
    },
    "plan": {
        "jobs": [
            {
                "id": "Job_CF6_A",
                "costFactor": 6,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.56,
                                        "lng": 13.33
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF1_A",
                "costFactor": 1,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.575,
                                        "lng": 13.36
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF2_A",
                "costFactor": 2,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.49,
                                        "lng": 13.35
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF5_A",
                "costFactor": 5,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.48,
                                        "lng": 13.4
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF1_B",
                "costFactor": 1,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.565,
                                        "lng": 13.4
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF6_B",
                "costFactor": 6,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.58,
                                        "lng": 13.39
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF2_B",
                "costFactor": 2,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.47,
                                        "lng": 13.37
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF5_B",
                "costFactor": 5,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.495,
                                        "lng": 13.43
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF10_A",
                "costFactor": 10,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.533,
                                        "lng": 13.38
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            },
            {
                "id": "Job_CF10_B",
                "costFactor": 10,
                "tasks": {
                    "deliveries": [
                        {
                            "places": [
                                {
                                    "duration": 300,
                                    "location": {
                                        "lat": 52.531,
                                        "lng": 13.37
                                    }
                                }
                            ],
                            "demand": [
                                1
                            ]
                        }
                    ]
                }
            }
        ]
    },
    "fleet": {
        "types": [
            {
                "id": "vehicle_standard",
                "profile": "vehicle",
                "amount": 1,
                "capacity": [
                    4
                ],
                "costs": {
                    "fixed": 0,
                    "distance": 0.00045,
                    "time": 0.0041666667,
                    "perJob": {
                        "rate": 5
                    }
                },
                "shifts": [
                    {
                        "start": {
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": "2026-07-30T08:00:00+02:00"
                        },
                        "end": {
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": "2026-07-30T18:00:00+02:00"
                        }
                    }
                ]
            },
            {
                "id": "vehicle_premium",
                "profile": "vehicle",
                "amount": 1,
                "capacity": [
                    4
                ],
                "costs": {
                    "fixed": 0,
                    "distance": 0.00045,
                    "time": 0.0041666667,
                    "perJob": {
                        "rate": 15
                    }
                },
                "shifts": [
                    {
                        "start": {
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": "2026-07-30T08:00:00+02:00"
                        },
                        "end": {
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": "2026-07-30T18:00:00+02:00"
                        }
                    }
                ]
            }
        ],
        "profiles": [
            {
                "name": "vehicle",
                "type": "car"
            }
        ]
    }
}

Solution interpretation

The solution contains two tours with four jobs each, and two unassigned jobs. The following figure visualizes the tours for each vehicle:

Sample tour including the costPerJob feature

The following table shows how the optimization algorithm assigned the jobs:

TourJobscostFactor valuesPer-job cost
vehicle_standard_1Job_CF6_A, Job_CF6_B, Job_CF5_A, Job_CF5_B6, 6, 5, 55 × 22 = 110
vehicle_premium_1Job_CF1_A, Job_CF1_B, Job_CF2_A, Job_CF2_B1, 1, 2, 215 × 6 = 90

The algorithm put the four jobs with the highest costFactor into the cheaper vehicle (vehicle_standard_1), because this is where the savings are the largest, while the four jobs with the lowest costFactor stay with vehicle_premium_1.

The most expensive Job_CF10_A and Job_CF10_B are unassigned with the CAPACITY_CONSTRAINT reason code, as the following example shows:

"unassigned": [
  {
    "jobId": "Job_CF10_A",
    "reasons": [
      {
        "code": "CAPACITY_CONSTRAINT",
        "description": "cannot be assigned due to capacity of vehicle"
      }
    ]
  }
]

Serving Job_CF10_A or Job_CF10_B would be easy from the distance point of view, but each of them costs at least 50, while every assigned job costs at most 30 on the vehicle that serves it. For example, replacing Job_CF5_A (25) with Job_CF10_A (50) in the vehicle_standard_1 tour would raise the per-job cost by 25. This shows that when capacity is limited, the costFactor values can outweigh the distance savings of serving nearby jobs.

The following section provides the full solution JSON:

Click to expand/collapse the sample JSON
{
    "statistic": {
        "cost": 278.185750373,
        "distance": 70135,
        "duration": 11190,
        "times": {
            "break": 0,
            "driving": 8790,
            "serving": 2400,
            "stopping": 0,
            "waiting": 0
        },
        "costs": {
            "distance": 31.56075,
            "perJob": 200,
            "time": 46.625000373000006
        },
        "intraStopDistance": 0
    },
    "tours": [
        {
            "shiftIndex": 0,
            "statistic": {
                "cost": 149.4116168525,
                "distance": 35961,
                "duration": 5575,
                "times": {
                    "break": 0,
                    "driving": 4375,
                    "serving": 1200,
                    "stopping": 0,
                    "waiting": 0
                },
                "costs": {
                    "distance": 16.18245,
                    "perJob": 110,
                    "time": 23.2291668525
                },
                "intraStopDistance": 0
            },
            "stops": [
                {
                    "activities": [
                        {
                            "jobId": "departure",
                            "type": "departure",
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": {
                                "arrival": "2026-07-30T06:00:00Z",
                                "start": "2026-07-30T06:00:00Z",
                                "end": "2026-07-30T06:00:00Z"
                            }
                        }
                    ],
                    "load": [
                        4
                    ],
                    "time": {
                        "arrival": "2026-07-30T06:00:00Z",
                        "departure": "2026-07-30T06:00:00Z"
                    },
                    "distance": 0,
                    "location": {
                        "lat": 52.5286,
                        "lng": 13.3764
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF6_A",
                            "type": "delivery",
                            "location": {
                                "lat": 52.56,
                                "lng": 13.33
                            },
                            "time": {
                                "arrival": "2026-07-30T06:11:32Z",
                                "start": "2026-07-30T06:11:32Z",
                                "end": "2026-07-30T06:16:32Z"
                            }
                        }
                    ],
                    "load": [
                        3
                    ],
                    "time": {
                        "arrival": "2026-07-30T06:11:32Z",
                        "departure": "2026-07-30T06:16:32Z"
                    },
                    "distance": 5568,
                    "location": {
                        "lat": 52.56,
                        "lng": 13.33
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF6_B",
                            "type": "delivery",
                            "location": {
                                "lat": 52.58,
                                "lng": 13.39
                            },
                            "time": {
                                "arrival": "2026-07-30T06:30:37Z",
                                "start": "2026-07-30T06:30:37Z",
                                "end": "2026-07-30T06:35:37Z"
                            }
                        }
                    ],
                    "load": [
                        2
                    ],
                    "time": {
                        "arrival": "2026-07-30T06:30:37Z",
                        "departure": "2026-07-30T06:35:37Z"
                    },
                    "distance": 12313,
                    "location": {
                        "lat": 52.58,
                        "lng": 13.39
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF5_B",
                            "type": "delivery",
                            "location": {
                                "lat": 52.495,
                                "lng": 13.43
                            },
                            "time": {
                                "arrival": "2026-07-30T07:02:46Z",
                                "start": "2026-07-30T07:02:46Z",
                                "end": "2026-07-30T07:07:46Z"
                            }
                        }
                    ],
                    "load": [
                        1
                    ],
                    "time": {
                        "arrival": "2026-07-30T07:02:46Z",
                        "departure": "2026-07-30T07:07:46Z"
                    },
                    "distance": 25050,
                    "location": {
                        "lat": 52.495,
                        "lng": 13.43
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF5_A",
                            "type": "delivery",
                            "location": {
                                "lat": 52.48,
                                "lng": 13.4
                            },
                            "time": {
                                "arrival": "2026-07-30T07:15:11Z",
                                "start": "2026-07-30T07:15:11Z",
                                "end": "2026-07-30T07:20:11Z"
                            }
                        }
                    ],
                    "load": [
                        0
                    ],
                    "time": {
                        "arrival": "2026-07-30T07:15:11Z",
                        "departure": "2026-07-30T07:20:11Z"
                    },
                    "distance": 28697,
                    "location": {
                        "lat": 52.48,
                        "lng": 13.4
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "arrival",
                            "type": "arrival",
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": {
                                "arrival": "2026-07-30T07:32:55Z",
                                "start": "2026-07-30T07:32:55Z",
                                "end": "2026-07-30T07:32:55Z"
                            }
                        }
                    ],
                    "load": [
                        0
                    ],
                    "time": {
                        "arrival": "2026-07-30T07:32:55Z",
                        "departure": "2026-07-30T07:32:55Z"
                    },
                    "distance": 35961,
                    "location": {
                        "lat": 52.5286,
                        "lng": 13.3764
                    }
                }
            ],
            "typeId": "vehicle_standard",
            "vehicleId": "vehicle_standard_1"
        },
        {
            "shiftIndex": 0,
            "statistic": {
                "cost": 128.77413352050002,
                "distance": 34174,
                "duration": 5615,
                "times": {
                    "break": 0,
                    "driving": 4415,
                    "serving": 1200,
                    "stopping": 0,
                    "waiting": 0
                },
                "costs": {
                    "distance": 15.3783,
                    "perJob": 90,
                    "time": 23.395833520500002
                },
                "intraStopDistance": 0
            },
            "stops": [
                {
                    "activities": [
                        {
                            "jobId": "departure",
                            "type": "departure",
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": {
                                "arrival": "2026-07-30T06:00:00Z",
                                "start": "2026-07-30T06:00:00Z",
                                "end": "2026-07-30T06:00:00Z"
                            }
                        }
                    ],
                    "load": [
                        4
                    ],
                    "time": {
                        "arrival": "2026-07-30T06:00:00Z",
                        "departure": "2026-07-30T06:00:00Z"
                    },
                    "distance": 0,
                    "location": {
                        "lat": 52.5286,
                        "lng": 13.3764
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF1_A",
                            "type": "delivery",
                            "location": {
                                "lat": 52.575,
                                "lng": 13.36
                            },
                            "time": {
                                "arrival": "2026-07-30T06:12:18Z",
                                "start": "2026-07-30T06:12:18Z",
                                "end": "2026-07-30T06:17:18Z"
                            }
                        }
                    ],
                    "load": [
                        3
                    ],
                    "time": {
                        "arrival": "2026-07-30T06:12:18Z",
                        "departure": "2026-07-30T06:17:18Z"
                    },
                    "distance": 5942,
                    "location": {
                        "lat": 52.575,
                        "lng": 13.36
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF1_B",
                            "type": "delivery",
                            "location": {
                                "lat": 52.565,
                                "lng": 13.4
                            },
                            "time": {
                                "arrival": "2026-07-30T06:27:24Z",
                                "start": "2026-07-30T06:27:24Z",
                                "end": "2026-07-30T06:32:24Z"
                            }
                        }
                    ],
                    "load": [
                        2
                    ],
                    "time": {
                        "arrival": "2026-07-30T06:27:24Z",
                        "departure": "2026-07-30T06:32:24Z"
                    },
                    "distance": 10139,
                    "location": {
                        "lat": 52.565,
                        "lng": 13.4
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF2_A",
                            "type": "delivery",
                            "location": {
                                "lat": 52.49,
                                "lng": 13.35
                            },
                            "time": {
                                "arrival": "2026-07-30T06:56:30Z",
                                "start": "2026-07-30T06:56:30Z",
                                "end": "2026-07-30T07:01:30Z"
                            }
                        }
                    ],
                    "load": [
                        1
                    ],
                    "time": {
                        "arrival": "2026-07-30T06:56:30Z",
                        "departure": "2026-07-30T07:01:30Z"
                    },
                    "distance": 21319,
                    "location": {
                        "lat": 52.49,
                        "lng": 13.35
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "Job_CF2_B",
                            "type": "delivery",
                            "location": {
                                "lat": 52.47,
                                "lng": 13.37
                            },
                            "time": {
                                "arrival": "2026-07-30T07:08:31Z",
                                "start": "2026-07-30T07:08:31Z",
                                "end": "2026-07-30T07:13:31Z"
                            }
                        }
                    ],
                    "load": [
                        0
                    ],
                    "time": {
                        "arrival": "2026-07-30T07:08:31Z",
                        "departure": "2026-07-30T07:13:31Z"
                    },
                    "distance": 24678,
                    "location": {
                        "lat": 52.47,
                        "lng": 13.37
                    }
                },
                {
                    "activities": [
                        {
                            "jobId": "arrival",
                            "type": "arrival",
                            "location": {
                                "lat": 52.5286,
                                "lng": 13.3764
                            },
                            "time": {
                                "arrival": "2026-07-30T07:33:35Z",
                                "start": "2026-07-30T07:33:35Z",
                                "end": "2026-07-30T07:33:35Z"
                            }
                        }
                    ],
                    "load": [
                        0
                    ],
                    "time": {
                        "arrival": "2026-07-30T07:33:35Z",
                        "departure": "2026-07-30T07:33:35Z"
                    },
                    "distance": 34174,
                    "location": {
                        "lat": 52.5286,
                        "lng": 13.3764
                    }
                }
            ],
            "typeId": "vehicle_premium",
            "vehicleId": "vehicle_premium_1"
        }
    ],
    "unassigned": [
        {
            "jobId": "Job_CF10_A",
            "reasons": [
                {
                    "code": "CAPACITY_CONSTRAINT",
                    "description": "cannot be assigned due to capacity of vehicle"
                }
            ]
        },
        {
            "jobId": "Job_CF10_B",
            "reasons": [
                {
                    "code": "CAPACITY_CONSTRAINT",
                    "description": "cannot be assigned due to capacity of vehicle"
                }
            ]
        }
    ]
}

Next steps


Did this page help you?