Job pooling
Optimizing pick-up and delivery routes often involves pooling jobs (completing jobs concurrently). For instance, if 2 jobs have similar pick-up locations, and the delivery locations are in the same or similar direction, it can save time to complete the pick-ups close together and then make the deliveries one after the other, rather completing the stops in one job first and then completing the stops in the other job.
Jobs are pooled by default, however, not all jobs can be pooled. A patient being transported between medical facilities for a procedure should not be delayed by other jobs or take a less direct route.
Prerequisites
Learn how to configure an API Key to run the examples in this guide:
-
Log in to Timefold Platform: app.timefold.ai
-
From the Dashboard, click your tenant, and from the drop-down menu select Tenant Settings, then choose API Keys.
-
Create a new API key or use an existing one. Ensure the list of models for the API key contains the Pick-up and Delivery Routing model.
In the examples, replace <API_KEY> with the API Key you just copied.
1. Prohibit job pooling
Jobs can prohibit job pooling by setting prohibitJobPooling to true:
{
"jobs": [
{
"id": "Job A",
"prohibitJobPooling": true,
"stops": [
{
"id": "A1",
"location": [33.78592, -84.46136],
"duration": "PT20M"
},
{
"id": "A2",
"location": [33.72757, -83.96354],
"duration": "PT20M",
"stopDependencies": [
{
"id": "jobA_dep1",
"precedingStop": "A1"
}
]
}
]
}
]
}
prohibitJobPooling is optional and if omitted the default behavior false will be used.
The Prohibit job pooling hard constraint penalizes solutions that pool jobs that prohibit pooling.
Jobs will be left unassigned if assigning them means breaking this constraint.
In the following example, there are 2 jobs: Job A and Job B. Job A prohibits job pooling. Job B does not prohibit job pooling.
Because Job A prohibits job pooling, jobs A and B are not pooled.
-
Input
-
Output
Try this example in Timefold Platform by saving this JSON into a file called sample.json and make the following API call:
|
curl -X POST -H "Content-type: application/json" -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans [email protected]
{
"config": {
"run": {
"name": "Job pooling example"
}
},
"modelInput": {
"drivers": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startLocation": [33.68786, -84.18487],
"endLocation": [33.68786, -84.18487],
"minStartTime": "2027-02-01T09:00:00Z",
"maxEndTime": "2027-02-01T17:00:00Z"
}
]
}
],
"jobs": [
{
"id": "Job A",
"prohibitJobPooling": true,
"stops": [
{
"id": "A1",
"location": [33.78592, -84.46136],
"duration": "PT20M"
},
{
"id": "A2",
"location": [33.72757, -83.96354],
"duration": "PT20M",
"stopDependencies": [
{
"id": "jobA_dep1",
"precedingStop": "A1"
}
]
}
]
},
{
"id": "Job B",
"stops": [
{
"id": "B1",
"location": [34.11110, -84.43002],
"duration": "PT20M"
},
{
"id": "B2",
"location": [33.48594, -84.26560],
"duration": "PT20M",
"stopDependencies": [
{
"id": "jobB_dep1",
"precedingStop": "B1"
}
]
}
]
}
]
}
}
To request the solution, locate the ID from the response to the post operation and append it to the following API call:
|
curl -X GET -H 'X-API-KEY: <API_KEY>' https://app.timefold.ai/api/models/pickup-delivery-routing/v1/route-plans/<ID>
{
"metadata": {
"id": "ID",
"parentId": null,
"originId": "ID",
"name": "Job pooling example",
"submitDateTime": "2025-09-30T06:12:54.265182705Z",
"startDateTime": "2025-09-30T06:13:26.33030959Z",
"activeDateTime": "2025-09-30T06:13:26.43084283Z",
"completeDateTime": "2025-09-30T06:14:57.962548606Z",
"shutdownDateTime": "2025-09-30T06:14:58.511460375Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-16318soft",
"tags": [
"system.type:from-request",
"system.profile:default"
],
"validationResult": {
"summary": "OK"
}
},
"modelOutput": {
"drivers": [
{
"id": "Carl",
"shifts": [
{
"id": "Carl-2027-02-01",
"startTime": "2027-02-01T09:00:00Z",
"itinerary": [
{
"id": "B1",
"arrivalTime": "2027-02-01T10:02:52Z",
"startServiceTime": "2027-02-01T10:02:52Z",
"departureTime": "2027-02-01T10:22:52Z",
"effectiveServiceDuration": "PT20M",
"kind": "STOP",
"travelTimeFromPreviousStandstill": "PT1H2M52S",
"travelDistanceMetersFromPreviousStandstill": 69415
},
{
"id": "B2",
"arrivalTime": "2027-02-01T11:43:32Z",
"startServiceTime": "2027-02-01T11:43:32Z",
"departureTime": "2027-02-01T12:03:32Z",
"effectiveServiceDuration": "PT20M",
"kind": "STOP",
"travelTimeFromPreviousStandstill": "PT1H20M40S",
"travelDistanceMetersFromPreviousStandstill": 97259
},
{
"id": "A1",
"arrivalTime": "2027-02-01T12:49:24Z",
"startServiceTime": "2027-02-01T12:49:24Z",
"departureTime": "2027-02-01T13:09:24Z",
"effectiveServiceDuration": "PT20M",
"kind": "STOP",
"travelTimeFromPreviousStandstill": "PT45M52S",
"travelDistanceMetersFromPreviousStandstill": 49494
},
{
"id": "A2",
"arrivalTime": "2027-02-01T14:03:08Z",
"startServiceTime": "2027-02-01T14:03:08Z",
"departureTime": "2027-02-01T14:23:08Z",
"effectiveServiceDuration": "PT20M",
"kind": "STOP",
"travelTimeFromPreviousStandstill": "PT53M44S",
"travelDistanceMetersFromPreviousStandstill": 62124
}
],
"metrics": {
"totalTravelTime": "PT4H31M58S",
"travelTimeFromStartLocationToFirstStop": "PT1H2M52S",
"travelTimeBetweenStops": "PT3H16S",
"travelTimeFromLastStopToEndLocation": "PT28M50S",
"totalTravelDistanceMeters": 309811,
"travelDistanceFromStartLocationToFirstStopMeters": 69415,
"travelDistanceBetweenStopsMeters": 208877,
"travelDistanceFromLastStopToEndLocationMeters": 31519,
"endLocationArrivalTime": "2027-02-01T14:51:58Z",
"overtime": "PT0S"
}
}
]
}
]
},
"inputMetrics": {
"stops": 4,
"drivers": 1,
"driverShifts": 1
},
"kpis": {
"totalTravelTime": "PT4H31M58S",
"travelTimeFromStartLocationToFirstStop": "PT1H2M52S",
"travelTimeBetweenStops": "PT3H16S",
"travelTimeFromLastStopToEndLocation": "PT28M50S",
"totalTravelDistanceMeters": 309811,
"travelDistanceFromStartLocationToFirstStopMeters": 69415,
"travelDistanceBetweenStopsMeters": 208877,
"travelDistanceFromLastStopToEndLocationMeters": 31519,
"totalUnassignedStops": 0,
"totalAssignedStops": 4,
"totalActivatedDrivers": 1,
"totalOvertime": "PT0S"
},
"run": {
"id": "b309d2ba-ffc5-4d32-b909-693b3f2d700b",
"parentId": null,
"originId": "b309d2ba-ffc5-4d32-b909-693b3f2d700b",
"name": "Job pooling example",
"submitDateTime": "2025-09-30T06:12:54.265182705Z",
"startDateTime": "2025-09-30T06:13:26.33030959Z",
"activeDateTime": "2025-09-30T06:13:26.43084283Z",
"completeDateTime": "2025-09-30T06:14:57.962548606Z",
"shutdownDateTime": "2025-09-30T06:14:58.511460375Z",
"solverStatus": "SOLVING_COMPLETED",
"score": "0hard/0medium/-16318soft",
"tags": [
"system.type:from-request",
"system.profile:default"
],
"validationResult": {
"summary": "OK"
}
}
}
Next
-
See the full API spec or try the online API.