Route matrix
Calculate travel distances and times from every source to every target in one request.
Signed in? Open this endpoint in the dashboard playground, which uses your organization automatically. Signed out, use Try in Playground below with your API key.
apiKey<token>Your API key from the dashboard, sent as ?apiKey=<key>.
Route Matrix configuration. The request body must be JSON.
application/json- body
mode*stringTravel profile used to calculate speeds, access rules and road restrictions. drive represents a passenger car.
Deviation (unsupported): transit and approximated_transit are rejected with 400 (no transit data); they are not accepted for matrices on Geoapify either.
"walk""hike""scooter""motorcycle""drive""light_truck""bicycle""mountain_bike""road_bike""bus""medium_truck""truck""truck_dangerous_goods""heavy_truck""long_truck"sources*array<>Origin locations. Each source produces one row in sources_to_targets.
Deviation (approximated): this service enforces 1 to 1000 sources and 1 to 1000 targets independently, not Geoapify's 1,000 total cells (sources x targets). Large matrices are computed in blocks of 150 x 150 (50 x 50 for scooter, motorcycle and bus), so very large requests are slow.
1 <= items <= 1000targets*array<>Destination locations. Each target produces one column in every sources_to_targets row. Same limits as sources.
1 <= items <= 1000avoid?array<>Road features, locations or polygons the router should avoid (up to 100 rules).
items <= 100traffic?stringTraffic model for motorized modes.
Deviation (ignored): accepted for compatibility; durations are always free-flow and the response always reports free_flow.
"free_flow""free_flow""approximated"type?stringRoute preference. balanced balances time and distance; short prioritizes distance; less_maneuvers penalizes turns and is only valid for motorized modes (400 otherwise).
"balanced""balanced""short""less_maneuvers"max_speed?numberMaximum speed for driving, truck, motorcycle, scooter and bus modes. 10 to 252 km/h with units=metric, or 6.5 to 155 mph with units=imperial; outside that range returns 400. Ignored for walking and cycling modes.
6.5 <= value <= 252units?stringUnit system for matrix distances. metric returns meters; imperial returns miles (3 decimals).
"metric""metric""imperial"Matrix rows correspond to sources; columns to targets. Distances use the requested unit system, times are in seconds.
application/json- response
sources*array<>targets*array<>sources_to_targets*array<array<>>sources_to_targets[i][j] is the route from sources[i] to targets[j].
units*string"metric""imperial"distance_units*string"meters""miles"mode*string"walk""hike""scooter""motorcycle""drive""light_truck""bicycle""mountain_bike""road_bike""bus""medium_truck""truck""truck_dangerous_goods""heavy_truck""long_truck"type?string"balanced""short""less_maneuvers"max_speed?numberEcho of the request value, present only when sent.
avoid?array<>Echo of the request rules, present only when sent.
traffic?stringAlways free_flow (see the request traffic deviation).
"free_flow""approximated"curl -X POST 'https://api.data.graphitelab.ai/v1/routematrix' \ -H 'Content-Type: application/json' \ -d '{ "mode": "drive", "sources": [ { "location": [ -86.158068, 39.768403 ] }, { "location": [ -86.1581, 39.7684 ] } ], "targets": [ { "location": [ -86.1349, 39.7817 ] }, { "location": [ -86.2, 39.75 ] } ]}'{ "sources": [ { "original_location": [ -86.158068, 39.768403 ] }, { "original_location": [ -86.1581, 39.7684 ] } ], "targets": [ { "original_location": [ -86.1349, 39.7817 ] }, { "original_location": [ -86.2, 39.75 ] } ], "sources_to_targets": [ [ { "distance": 2900, "time": 298, "source_index": 0, "target_index": 0 }, { "distance": 4956, "time": 426, "source_index": 0, "target_index": 1 } ], [ { "distance": 2907, "time": 299, "source_index": 1, "target_index": 0 }, { "distance": 4963, "time": 427, "source_index": 1, "target_index": 1 } ] ], "units": "metric", "distance_units": "meters", "mode": "drive", "type": "balanced", "traffic": "free_flow"}Full schema: API reference
POST /v1/routematrix returns the distance and travel time for every source-to-target pair. Use it to rank candidates by drive time, build cost tables for dispatch, or feed an optimizer. Send the same locations as sources and targets for a square all-to-all matrix.
How it works
The gateway validates the body and calls Valhalla's sources_to_targets API. No geocoding engine is involved, so locations are plain coordinates, not addresses.
- Valhalla limits how many pairs one request may hold, so the gateway splits large matrices into blocks of 150 x 150 locations (50 x 50 for
scooter,motorcycleandbus) and runs up to 4 blocks at a time. - If Valhalla rejects a block because one pair is unroutable (no road near a point, unconnected regions), the gateway splits that block until it isolates the pair and reports it as
null. - Road data comes from OpenStreetMap, built into Valhalla tiles.
Quick start
curl -s -X POST -H "Content-Type: application/json" -d '{"mode":"drive","sources":[{"location":[-86.158068,39.768403]},{"location":[-86.1581,39.7684]}],"targets":[{"location":[-86.1349,39.7817]},{"location":[-86.2,39.75]}]}' "https://api.data.graphitelab.ai/v1/routematrix?apiKey=$GRAPHITE_API_KEY"{
"sources": [
{ "original_location": [-86.158068, 39.768403] },
{ "original_location": [-86.1581, 39.7684] }
],
"targets": [
{ "original_location": [-86.1349, 39.7817] },
{ "original_location": [-86.2, 39.75] }
],
"sources_to_targets": [
[
{ "distance": 2900, "time": 298, "source_index": 0, "target_index": 0 },
{ "distance": 4956, "time": 426, "source_index": 0, "target_index": 1 }
],
[
{ "distance": 2907, "time": 299, "source_index": 1, "target_index": 0 },
{ "distance": 4963, "time": 427, "source_index": 1, "target_index": 1 }
]
],
"units": "metric",
"distance_units": "meters",
"mode": "drive",
"type": "balanced",
"traffic": "free_flow"
}Parameters
The request is a JSON body. Each location is {"location": [longitude, latitude]}.
| Name | Type | Required | What it does |
|---|---|---|---|
mode | string | yes | Travel profile, for example drive, walk, bicycle, truck. transit and approximated_transit return 400. |
sources | array | yes | 1 to 1000 origins. Each produces one row of sources_to_targets. |
targets | array | yes | 1 to 1000 destinations. Each produces one column in every row. |
avoid | array | no | Up to 100 rules: tolls, ferries, highways, locations, polygons. |
type | string | no | balanced (default), short, or less_maneuvers (motorized modes only). |
units | string | no | metric (default, meters) or imperial (miles, 3 decimals). |
max_speed | number | no | Speed cap for motorized modes: 10 to 252 km/h, or 6.5 to 155 mph with imperial. |
traffic | string | no | free_flow or approximated. Accepted and ignored. |
Response
sources_to_targets[i][j] describes the route from sources[i] to targets[j].
| Field | Meaning |
|---|---|
distance | Whole meters, or miles to 3 decimals with units=imperial. null when no route exists. |
time | Travel time in seconds. null when no route exists. |
source_index, target_index | Positions in sources and targets. |
units, distance_units | The unit system and the unit name (meters or miles) used for distance. |
traffic | Always free_flow. |
This endpoint returns coordinates only, so there is no place_id, datasource or rank.
Differences from Geoapify
-
approximated: the limit is 1000 sources and 1000 targets, checked separately. Geoapify caps a regular call at 1,000 cells in total. There are no per-pair or sum-of-distances limits.
-
ignored:
trafficis accepted and ignored. Durations are always free-flow. -
empty:
sourcesandtargetscarryoriginal_locationonly, with no road-snappedlocation. -
ignored (in
avoid):green_zones,transfersandcountry_codesare accepted and have no effect.
Also not supported: transit and approximated_transit modes (400), and asynchronous batch calls (/batch is not in v1).
US and Canada notes
Valhalla covers the US and Canada, and the examples run in Indianapolis and Montreal. Routing quality follows OpenStreetMap road coverage and restrictions.
More examples
A second 2 x 2 drive matrix, in Montreal.
curl -s -X POST -H "Content-Type: application/json" -d '{"mode":"drive","sources":[{"location":[-73.5673,45.5017]},{"location":[-73.58,45.51]}],"targets":[{"location":[-73.6,45.53]},{"location":[-73.55,45.49]}]}' "https://api.data.graphitelab.ai/v1/routematrix?apiKey=$GRAPHITE_API_KEY"{
"sources_to_targets": [
[
{ "distance": 5855, "time": 540, "source_index": 0, "target_index": 0 },
{ "distance": 1944, "time": 153, "source_index": 0, "target_index": 1 }
],
[
{ "distance": 5098, "time": 557, "source_index": 1, "target_index": 0 },
{ "distance": 4834, "time": 508, "source_index": 1, "target_index": 1 }
]
],
"units": "metric",
"distance_units": "meters",
"mode": "drive"
}Errors and limits
| Status | When |
|---|---|
| 400 | Invalid body: missing mode, sources or targets, more than 1000 of either, bad coordinates, an unsupported mode, max_speed out of range, less_maneuvers on a non-motorized mode. |
| 401 | Missing or invalid apiKey. |
| 500 | Internal error. |
| 502 | An upstream engine returned a bad response. |
| 503 | An upstream engine is unavailable. |
Limits: 1 to 1000 sources, 1 to 1000 targets, up to 100 avoid rules. Very large matrices are computed in blocks, so they are slow.
No known gateway issue in .harness/phases/18/progress.md affects this endpoint. The open bugs there concern /routing admin areas, reverse geocoding and boundaries.
Try in Playground (signed in to your Graphite Lab organization). Signed out, use the paste-your-key widget on the reference page.