Route Enhancement API
Use the Route Enhancement API to get the GeoJSON geometry of a GPX or KML file. You can extend it with elevation, road surface and highway type, travel time estimates, or turn-by-turn instructions by adding extra parameters.
Base URL
https://enhance.maptoolkit.net/routeUse a POST request when the geometry is large, otherwise a GET request.
Authentication
Add your API key as ?api_key=YOUR_API_KEY to every request. See Authentication for details.
Parameters
Route Data
| Parameter | Type | Required | Description |
|---|---|---|---|
gpx | URL | Yes (or kml/geometry) | URL to a GPX file. |
kml | URL | Yes (or gpx/geometry) | URL to a KML file. |
geometry | GeoJSON string | Yes (or gpx/kml) | A GeoJSON LineString or MultiLineString. |
Data to add
| Parameter | Type | Required | Description |
|---|---|---|---|
elevation | 0 or 1 | No | Include an elevation profile in the response. Default: 0. |
surface | 0, 1 or object | No | Include road surface and highway type data. Pass a JSON object to tune the map match. Default: 0. |
timings | 0 or 1 | No | Include travel time estimates. Default: 0. |
mapmatch | 0 or 1 | No | Include turn-by-turn directions matched to the road network. Default: 0. |
Configuration Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
routeType | string | No | Vehicle type: car, bike, or foot. |
language | string | No | Language for turn instructions, as a 2-letter code (e.g., en). |
cache | 0 or 1 | No | Use cached results if available. Default: 1. |
callback | string | No | Wrap the response in a JSONP callback. |
Response
GeoJSON from input route
GET https://enhance.maptoolkit.net/route?kml=https://maptoolkit.net/export/ts_demo_tours/_1603292819.kml&api_key=YOUR_API_KEYResponse:
{
"geometry": {
"type": "MultiLineString",
"coordinates": [
[
[15.460422, 47.349047, 1181],
[15.460593, 47.349027, 1179],
...
]
]
}
}Coordinates are [longitude, latitude, elevation_in_meters].
Add Elevation Data
GET https://enhance.maptoolkit.net/route?geometry={"type":"LineString","coordinates":[[10,50],[10.1,50.1]]}&elevation=1&api_key=YOUR_API_KEYResponse:
{
"geometry": { "type": "MultiLineString", "coordinates": [[...]] },
"elevation": [{
"samples": [289, 292, 302, 305, 299],
"distance": 13230.5,
"ascent": 220,
"descent": 160,
"yrange": [200, 600],
"labels": {
"x": [[0, 0], [0.23, 3], [0.45, 6]],
"y": [[0.25, 300], [0.5, 400], [0.75, 500]]
}
}]
}Elevation samples are taken approximately every 100 meters. yrange gives the min/max values for the y-axis. Label values are relative positions (0-1) paired with the label value.
Add Surface Data
surface=1 returns the road surface and highway type under each part of the route, so you
can tell a reader how much of a ride is asphalt and how much is gravel, colour a route line
by what it runs on, or filter a set of tours down to the ones a road bike can handle.
GET https://enhance.maptoolkit.net/route?kml=https://example.com/route.kml&surface=1&api_key=YOUR_API_KEYResponse:
{
"geometry": { "type": "MultiLineString", "coordinates": [[...]] },
"surface": [[
{ "from": 0.053, "to": 0.088, "highway": "road", "surface": "asphalt" },
{ "from": 0.088, "to": 0.106, "highway": "road", "surface": "paved" },
{ "from": 0.106, "to": 0.241, "highway": "path", "surface": "unpaved" }
]]
}| Field | Description |
|---|---|
from | Start of this segment as a relative position along the route. 0 is the start, 1 is the end. |
to | End of this segment as a relative position along the route. |
highway | Road or path type for this segment. |
surface | Surface material for this segment. |
surface is an array of arrays. There is one inner array per segment of the
MultiLineString geometry, in the same order, so a route split into three segments returns
three arrays. Reading response.surface[0] and assuming it covers the whole route is correct
only for a single-segment route.
Positions are relative, not meters. from and to are fractions of the route, so
turning a surface breakdown into distances means multiplying by the total length. Segments are
contiguous and do not overlap, which makes a percentage breakdown a matter of summing
to - from per surface value.
Tuning the map match
Surface data is not stored against your geometry. The service map-matches each part of the route onto the road network and reads the tags off whatever it matched. A recorded track that drifts, or a line drawn across open ground, can match a road it never touched.
surface therefore also accepts a JSON object instead of 1:
GET https://enhance.maptoolkit.net/route?kml=https://example.com/route.kml&surface={"mapMatchingThreshold":0.02}&api_key=YOUR_API_KEY| Option | Type | Description |
|---|---|---|
mapMatchingThreshold | number | Largest relative distance deviation that still counts as a match. A segment is classified as other when abs(1 - matchDistance / origDistance) reaches the threshold. Default: 0.05. |
Lower the threshold to reject doubtful matches, which trades coverage for confidence and
returns more other. Raise it when a clean track keeps coming back as other.
other in either field means no confident match, not an unusual road. Treat it as unknown
rather than as a surface type, because presenting it as a real category is how a route ends up
described as unpaved when nothing is known about it.
Highway types:
motorway, primary, road, street, pedestrian, cycleway, path, hiking, mountain_hiking, otherSurface types:
asphalt, paved, unpaved, natural, alpine, otherAdd Travel Time Estimates
GET https://enhance.maptoolkit.net/route?geometry={"type":"LineString","coordinates":[[10,50],[10.1,50.1]]}&timings=1&api_key=YOUR_API_KEYResponse:
{
"geometry": { "type": "MultiLineString", "coordinates": [[...]] },
"timings": [{
"walking": 300,
"cycling": 100,
"cycling_offroad": 110,
"cycling_racing": 50
}]
}Times are in seconds, calculated from route distance and elevation.
Add Turn-by-Turn Directions
Map matching generates turn-by-turn directions matched to the road network.
GET https://enhance.maptoolkit.net/route?kml=https://example.com/route.kml&mapmatch=1&language=en&api_key=YOUR_API_KEYResponse:
{
"geometry": { "type": "MultiLineString", "coordinates": [[...]] },
"mapmatch": [[{
"instructions": [{
"distance": 336.799,
"name": "Kaufbeurer Straße",
"text": "Turn right onto Kaufbeurer Straße",
"sign": 2,
"time": 67357,
"coordinate": [47.83471, 10.82657],
"tags": { "surface": "asphalt", "highway": "road" }
}],
"geometry": {
"type": "LineString",
"coordinates": [[10.82623, 47.83423], ...]
}
}]]
}Errors
Errors return the HTTP status code with a plain-text message in the body.
| Status | Meaning |
|---|---|
403 | The API key is missing (Access denied!) or not recognized (Api-key not found!). See First Steps. |
400 | The geometry or a parameter could not be parsed, or an upstream service failed. |
Common 400 messages
| Message | Cause |
|---|---|
no valid geometry found, LineString & MultiLineString's are allowed | geometry is missing or has an unsupported GeoJSON type. |
invalid geometry type | The GeoJSON parsed, but is not a LineString or MultiLineString. |
param '{name}' seems to be a JSON String, but it cannot be parsed: {message} | A parameter value starts with { but is not valid JSON. |
routing request failed: {message} | The upstream surface or map-matching service returned an error. |
Rate limits
Limits are monthly request quotas tied to your plan rather than a per-second rate: the
service returns no RateLimit-* or Retry-After headers. Current allowances are on the
pricing page.
The free Basic plan has a hard limit, so requests are refused once it is reached. On Pro and Ultra, requests past the allowance are billed rather than refused. Enterprise plans use a flexible limit.
Calls to this API count toward your plan’s API request allowance, which is the smaller of the two allowances.
Pricing
Self-service plans (Basic, Pro, and Ultra) are billed through RapidAPI; see RapidAPI for how to subscribe and authenticate. Plan limits and prices are listed on the Maptoolkit pricing page. Enterprise customers call the native hosts directly with a Maptoolkit API key.