Skip to content
Route Enhancement API

Route Enhancement API

On this page

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/route

Use 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

ParameterTypeRequiredDescription
gpxURLYes (or kml/geometry)URL to a GPX file.
kmlURLYes (or gpx/geometry)URL to a KML file.
geometryGeoJSON stringYes (or gpx/kml)A GeoJSON LineString or MultiLineString.

Data to add

ParameterTypeRequiredDescription
elevation0 or 1NoInclude an elevation profile in the response. Default: 0.
surface0, 1 or objectNoInclude road surface and highway type data. Pass a JSON object to tune the map match. Default: 0.
timings0 or 1NoInclude travel time estimates. Default: 0.
mapmatch0 or 1NoInclude turn-by-turn directions matched to the road network. Default: 0.

Configuration Parameters

ParameterTypeRequiredDescription
routeTypestringNoVehicle type: car, bike, or foot.
languagestringNoLanguage for turn instructions, as a 2-letter code (e.g., en).
cache0 or 1NoUse cached results if available. Default: 1.
callbackstringNoWrap the response in a JSONP callback.
densify0 or 1NoAdd vertices to the geometry, see Densified geometry. Default: 1 with elevation.
densifyGapnumberNoMaximum distance between vertices in meters when densify is on. Minimum 50. Default: 100.

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_KEY

Response:

{
  "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_KEY

Response:

{
  "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.

Densified geometry

With elevation=1 the returned geometry is densified as well: vertices are added so that no two are more than densifyGap meters apart (default 100, minimum 50). The original vertices are kept, so a sparse line drawn with a few clicks comes back with an elevation value along its whole length, which is what an elevation profile drawn from the geometry needs.

Elevations of the added vertices come from the terrain model. If every vertex of the input already has an elevation, for example a recorded GPS track, the added vertices are interpolated between those values instead, so recorded and modeled heights are not mixed.

Pass densify=0 to keep the input vertices unchanged:

GET https://enhance.maptoolkit.net/route?geometry={"type":"LineString","coordinates":[[10,50],[10.1,50.1]]}&elevation=1&densify=0&api_key=YOUR_API_KEY

densify has no effect without elevation.

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, color 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_KEY

Response:

{
  "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" }
  ]]
}
FieldDescription
fromStart of this segment as a relative position along the route. 0 is the start, 1 is the end.
toEnd of this segment as a relative position along the route.
highwayRoad or path type for this segment.
surfaceSurface 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
OptionTypeDescription
mapMatchingThresholdnumberLargest 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, other

Surface types:

asphalt, paved, unpaved, natural, alpine, other

Add 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_KEY

Response:

{
  "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_KEY

Response:

{
  "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.

StatusMeaning
403The API key is missing (Access denied!) or not recognized (Api-key not found!). See First Steps.
400The geometry or a parameter could not be parsed, or an upstream service failed.

Common 400 messages

MessageCause
no valid geometry found, LineString & MultiLineString's are allowedgeometry is missing or has an unsupported GeoJSON type.
invalid geometry typeThe 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 have no request 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.