Best visiting order for stops
optimize_routeFind the optimal visiting order for 2-10 stops by solving the traveling-salesman problem over street networks. Returns per-leg distance, duration, totals, and export links.
Instructions
Best order to visit several stops: multi-stop route ordering (a small TSP).
Answers "I have these five errands, what order costs least" — stops is a
list of 2-10 points, each a {"lat": ..., "lon": ..., "name": ...
(optional)} dict, a GERS id, or a free-text name, mixed freely. The
answer is the cheapest visiting order over the real street graph, not a
straight-line guess. Solved exactly (Held-Karp over the routed cost
matrix), so it is the optimum, not a nearest-neighbour approximation.
Any stop given by id/name that failed to resolve returns an indexed
error (stops[i]: ...) with candidates on ambiguity — the whole call
fails, not silently drops that stop.
Returns {"order": [stop indices, in visiting order], "legs":
[{"from_idx", "to_idx", "distance_m", "duration_s"}, ...],
"total_distance_m", "total_duration_s", "mode", "roundtrip", "export"}
— indices refer to the input `stops` list, and there is no
polyline/geometry — for a single pair's numbers on their own, call
`route`. export is the pocket handoff: a multi-stop Google/Apple Maps
directions URL (coordinates only — no Maps API), a GPX 1.1 document
with every stop as a waypoint, and a printable list that keeps any
names the caller passed. If a stop already carries confidence or
operating_status (from a prior place lookup), the response adds
verify_before_going naming the 1–2 weakest. Any stop given as an id or
name adds "resolved": [{"stop": i, "name", "id", "lat", "lon",
"matched_by"}, ...] for just those stops — plain {lat,lon} stops need
no echo and the key is absent when every stop was already coordinates.
keep_order=true visits the stops in the order you gave and never
reorders them: the itinerary is the caller's, and this tool supplies
routed (not straight-line) per-leg numbers, the totals, and the export
for it — one street-graph build for the whole run instead of chaining
`route` per leg. Use it whenever the order came from the user ("first
the bank, then the school, then home"); leave it off to be told the
cheapest order. The response echoes "keep_order": true, and "order" is
then just 0..n-1. start_index must stay 0 with it (there is nothing to
fix — the given order already starts where it starts), and roundtrip
still chooses whether the last leg closes back to the first stop, so a
one-way itinerary wants roundtrip=false.
start_index (default 0) is fixed as the first stop. roundtrip=true (the
default) returns to it; the closing leg is in "legs" but the start is not
repeated in "order". roundtrip=false is an open path that ends wherever
is cheapest. Omit mode to use the stored preferences mode, else drive. Same
cost model every routing tool uses; one-ways make the drive/cycle cost
matrix asymmetric and that is solved for exactly. The objective minimized
is total duration.
If some pair of stops has no route between them (disconnected road data),
the call still succeeds: that leg's numbers are a straight-line estimate,
the leg carries "estimated": true, and the response carries
"estimated": true plus a note naming the estimated legs — so a flagged
approximation, never a crash.
confirm=true after the user agreed to wait for a first-time street-graph
build (about 5–25 seconds). Pass it only after a needs_confirm reply
and they said yes. One gate for the whole call — every stop rides the
same graph, so it asks once, never once per leg. A warm or cached graph
never needs it. Omit confirm unless you just asked and they said yes.
Errors are structured, not raised: fewer than 2 or more than 10 stops, a
stop that is not a valid location reference, an out-of-range
start_index, or keep_order=true with a non-zero start_index return
{"error": "bad_request"} naming the offending stop
index; an unresolvable name/id returns {"error": "not_found"} or
{"error": "ambiguous_place", "candidates": [...]}, indexed the same way;
an unknown mode returns {"error": "unsupported_mode"}; a stop set whose
two furthest-apart stops are further apart than the mode's straight-line
cap (see `route`) returns {"error": "route_too_long"}; a stop with no
usable street node near it returns {"error": "no_graph_nearby"} naming
that stop's index.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Travel mode. Default: stored preference, else drive. | |
| stops | Yes | ||
| confirm | No | ||
| roundtrip | No | ||
| keep_order | No | ||
| start_index | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||