Two trucks (truck-1, truck-2) sit at the same depot. Four jobs around Amsterdam/Rotterdam need visiting. No GPS, no matrix computation — just a plain distance_matrix in meters. The solver splits the jobs between the two trucks to minimize total distance.
Basic Routing
This is the simplest non-trivial FG-API request: a small explicit distance matrix, two vehicles, and a handful of jobs with no time windows, no skills, no capacities. It is a good starting point for understanding the request/response shape before adding constraints.
The distance matrix
Five locations: index 0 is the shared depot, indices 1-4 are job stops. Values are meters.
| 0 (depot) | 1 (job-4821) | 2 (job-4822) | 3 (job-4823) | 4 (job-4824) | |
|---|---|---|---|---|---|
| 0 | 0 | 5200 | 8700 | 6100 | 9400 |
| 1 | 5200 | 0 | 4300 | 7800 | 10200 |
| 2 | 8700 | 4300 | 0 | 5500 | 6900 |
| 3 | 6100 | 7800 | 5500 | 0 | 3100 |
| 4 | 9400 | 10200 | 6900 | 3100 | 0 |
Locations 1-2 (job-4821, job-4822) and 3-4 (job-4823, job-4824) form two natural clusters: the 1-2 pair is 4300m apart, the 3-4 pair is 3100m apart, while crossing between the clusters costs 5500-10200m. With two trucks available, the cheapest plan is one truck per cluster instead of one truck zig-zagging across both.
Request
{
"metadata": { "problem_id": "basic-routing-example" },
"travel": {
"distance_matrix": [
[0, 5200, 8700, 6100, 9400],
[5200, 0, 4300, 7800, 10200],
[8700, 4300, 0, 5500, 6900],
[6100, 7800, 5500, 0, 3100],
[9400, 10200, 6900, 3100, 0]
]
},
"resources": [
{ "id": "truck-1", "depot_index": 0 },
{ "id": "truck-2", "depot_index": 0 }
],
"jobs": [
{ "id": "job-4821", "location_index": 1, "service_time": 300 },
{ "id": "job-4822", "location_index": 2, "service_time": 300 },
{ "id": "job-4823", "location_index": 3, "service_time": 240 },
{ "id": "job-4824", "location_index": 4, "service_time": 240 }
],
"options": { "time_limit_seconds": 5 }
}
Response
{
"status": "optimal",
"problem_id": "basic-routing-example",
"solve_time_ms": 38,
"routes": [
{
"resource_id": "truck-1",
"day": null,
"activities": [
{ "type": "start", "location_index": 0, "departure_time": 0 },
{ "type": "service", "job_id": "job-4821", "location_index": 1, "arrival_time": 5200, "departure_time": 5500, "service_time": 300, "distance_from_prev": 5200, "time_from_prev": 5200 },
{ "type": "service", "job_id": "job-4822", "location_index": 2, "arrival_time": 9800, "departure_time": 10100, "service_time": 300, "distance_from_prev": 4300, "time_from_prev": 4300 },
{ "type": "end", "location_index": 0, "arrival_time": 18800, "distance_from_prev": 8700, "time_from_prev": 8700 }
],
"summary": {
"total_distance": 18200,
"total_travel_time": 18200,
"total_service_time": 600,
"total_waiting_time": 0,
"total_time": 18800,
"num_stops": 2,
"num_jobs": 2,
"cost": 18200.0
},
"violations": []
},
{
"resource_id": "truck-2",
"day": null,
"activities": [
{ "type": "start", "location_index": 0, "departure_time": 0 },
{ "type": "service", "job_id": "job-4823", "location_index": 3, "arrival_time": 6100, "departure_time": 6340, "service_time": 240, "distance_from_prev": 6100, "time_from_prev": 6100 },
{ "type": "service", "job_id": "job-4824", "location_index": 4, "arrival_time": 9440, "departure_time": 9680, "service_time": 240, "distance_from_prev": 3100, "time_from_prev": 3100 },
{ "type": "end", "location_index": 0, "arrival_time": 19080, "distance_from_prev": 9400, "time_from_prev": 9400 }
],
"summary": {
"total_distance": 18600,
"total_travel_time": 18600,
"total_service_time": 480,
"total_waiting_time": 0,
"total_time": 19080,
"num_stops": 2,
"num_jobs": 2,
"cost": 18600.0
},
"violations": []
}
],
"unserved": [],
"score": {
"total": 36800.0,
"travel_distance": 36800.0,
"travel_time": 36800.0,
"fixed_vehicle_cost": 0.0,
"penalty_dropped": 0.0,
"fairness_cost": 0.0,
"num_vehicles_used": 2
},
"warnings": []
}
What happened
The solver assigned truck-1 to the job-4821/job-4822 cluster and truck-2 to the job-4823/job-4824 cluster:
- truck-1 drives depot → job-4821 (5200m) → job-4822 (4300m) → depot (8700m), total 18200m.
- truck-2 drives depot → job-4823 (6100m) → job-4824 (3100m) → depot (9400m), total 18600m.
Combined distance is 36800m. Compare that to sending a single truck through all four stops in any order — the cheapest single-vehicle tour still has to cross between the two clusters twice (at least 2 × 5500m extra versus splitting), which is why using both trucks wins under the default min_distance objective even though there is no vehicle_fixed_cost_weight pushing toward fewer vehicles.
Both routes are "violations": [] because there are no time windows, capacities, or skills in this request — with nothing to violate, the only thing left to optimize is total distance.
When only distance_matrix is set, FG-API uses it as a stand-in for travel time too, so arrival_time/departure_time in the response are expressed in the same units as the matrix. Supply a separate time_matrix if distance and time should diverge (e.g. highway vs. local roads).
- Time Windows — add hard and soft arrival windows to these same kinds of jobs.
- Skills & Capacities — restrict which vehicle can take which job.
- OptimizationResponse schema — full field reference for the response shown above.