Routing

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.

POST
/routematrix

Authorization

queryapiKey<token>

Your API key from the dashboard, sent as ?apiKey=<key>.

Request Body

Route Matrix configuration. The request body must be JSON.

application/json
  1. body
mode*string

Travel 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.

Value in"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.

Items1 <= items <= 1000
targets*array<>

Destination locations. Each target produces one column in every sources_to_targets row. Same limits as sources.

Items1 <= items <= 1000
avoid?array<>

Road features, locations or polygons the router should avoid (up to 100 rules).

Itemsitems <= 100
traffic?string

Traffic model for motorized modes.

Deviation (ignored): accepted for compatibility; durations are always free-flow and the response always reports free_flow.

Default"free_flow"
Value in"free_flow""approximated"
type?string

Route preference. balanced balances time and distance; short prioritizes distance; less_maneuvers penalizes turns and is only valid for motorized modes (400 otherwise).

Default"balanced"
Value in"balanced""short""less_maneuvers"
max_speed?number

Maximum 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.

Range6.5 <= value <= 252
units?string

Unit system for matrix distances. metric returns meters; imperial returns miles (3 decimals).

Default"metric"
Value in"metric""imperial"

Response Body

Matrix rows correspond to sources; columns to targets. Distances use the requested unit system, times are in seconds.

application/json
  1. 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
Value in"metric""imperial"
distance_units*string
Value in"meters""miles"
mode*string
Value in"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
Value in"balanced""short""less_maneuvers"
max_speed?number

Echo of the request value, present only when sent.

avoid?array<>

Echo of the request rules, present only when sent.

traffic?string

Always free_flow (see the request traffic deviation).

Value in"free_flow""approximated"
Example Request
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      ]    }  ]}'
Example Response
{  "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"}

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, motorcycle and bus) 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]}.

NameTypeRequiredWhat it does
modestringyesTravel profile, for example drive, walk, bicycle, truck. transit and approximated_transit return 400.
sourcesarrayyes1 to 1000 origins. Each produces one row of sources_to_targets.
targetsarrayyes1 to 1000 destinations. Each produces one column in every row.
avoidarraynoUp to 100 rules: tolls, ferries, highways, locations, polygons.
typestringnobalanced (default), short, or less_maneuvers (motorized modes only).
unitsstringnometric (default, meters) or imperial (miles, 3 decimals).
max_speednumbernoSpeed cap for motorized modes: 10 to 252 km/h, or 6.5 to 155 mph with imperial.
trafficstringnofree_flow or approximated. Accepted and ignored.

Full parameter reference

Response

sources_to_targets[i][j] describes the route from sources[i] to targets[j].

FieldMeaning
distanceWhole meters, or miles to 3 decimals with units=imperial. null when no route exists.
timeTravel time in seconds. null when no route exists.
source_index, target_indexPositions in sources and targets.
units, distance_unitsThe unit system and the unit name (meters or miles) used for distance.
trafficAlways 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: traffic is accepted and ignored. Durations are always free-flow.

  • empty: sources and targets carry original_location only, with no road-snapped location.

  • ignored (in avoid): green_zones, transfers and country_codes are 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

StatusWhen
400Invalid 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.
401Missing or invalid apiKey.
500Internal error.
502An upstream engine returned a bad response.
503An 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.