Skip to content

Map Matching

Free

Starter

Standard

Professional

Our map matching API turns your sequence of points into complete routes that follow the road network. Here are some of the things you can do with map matching:

  • Bring your own routes to a turn-by-turn guidance system like Ferrostar.
  • Turn mobile GPS traces into routable instructions later (ex: favorite cycling routes).
  • Create full route guidance from low-resolution itineraries.

Map matching works with all of our costing models (yes, even buses and golf carts!), and generates maneuver instructions in over 25 languages.

Endpoint: https://api.stadiamaps.com/map_match/v1

Example Code

Installation Instructions

The Stadia Maps JavaScript/TypeScript SDK is available for any package manager that supports the npm registry.

npm install @stadiamaps/api
yarn add @stadiamaps/api
bun add @stadiamaps/api
import { RoutingApi, Configuration, MapMatchRequest } from '@stadiamaps/api';

// If you are writing for a backend application or can't use domain-based auth,
// then you'll need to add your API key like so:
// 
// const config = new Configuration({ apiKey: "YOUR-API-KEY" });
// You can also use our EU endpoint to keep traffic within the EU using the basePath option:
// const config = new Configuration({ basePath: "https://api-eu.stadiamaps.com" });
// const api = new RoutingApi(config);
const api = new RoutingApi();

// A map match request using an encoded polyline. You can also use a list of coordinates to specify the shape.
const req: MapMatchRequest = {
    id: "mapMatch",
    encodedPolyline: "_grbgAh~{nhF?lBAzBFvBHxBEtBKdB?fB@dBZdBb@hBh@jBb@x@\\|@x@pB\\x@v@hBl@nBPbCXtBn@|@z@ZbAEbAa@~@q@z@QhA]pAUpAVhAPlAWtASpAAdA[dASdAQhAIlARjANnAZhAf@n@`A?lB^nCRbA\\xB`@vBf@tBTbCFbARzBZvBThBRnBNrBP`CHbCF`CNdCb@vBX`ARlAJfADhA@dAFdAP`AR`Ah@hBd@bBl@rBV|B?vB]tBCvBBhAF`CFnBXtAVxAVpAVtAb@|AZ`Bd@~BJfA@fAHdADhADhABjAGzAInAAjAB|BNbCR|BTjBZtB`@lBh@lB\\|Bl@rBXtBN`Al@g@t@?nAA~AKvACvAAlAMdAU`Ac@hAShAI`AJ`AIdAi@bAu@|@k@p@]p@a@bAc@z@g@~@Ot@Bz@f@X`BFtBXdCLbAf@zBh@fBb@xAb@nATjAKjAW`BI|AEpAHjAPdAAfAGdAFjAv@p@XlAVnA?~A?jAInAPtAVxAXnAf@tBDpBJpBXhBJfBDpAZ|Ax@pAz@h@~@lA|@bAnAd@hAj@tAR~AKxAc@xAShA]hAIdAAjA]~A[v@BhB?dBSv@Ct@CvAI~@Oz@Pv@dAz@lAj@~A^`B^|AXvAVpAXdBh@~Ap@fCh@hB\\zBN`Aj@xBFdA@jALbAPbAJdAHdAJbAHbAHfAJhALbA\\lBTvBAdC@bC@jCKjASbC?`CM`CDpB\\xAj@tB\\fA\\bAVfAJdAJbAXz@L|BO`AOdCDdA@~B\\z@l@v@l@v@l@r@j@t@b@x@b@r@z@jBVfCJdAJdANbCPfCF|BRhBS~BS`AYbAe@~BQdA",
    costing: "pedestrian",
    units: DistanceUnit.Mi,
    linearReferences: true
};
const res = await api.mapMatch({ mapMatchRequest: req });
Installation Instructions

The Stadia Maps Python SDK is available through any package manager that supports PyPi.

pip install stadiamaps
poetry add stadiamaps
import os
import stadiamaps
from stadiamaps.rest import ApiException

# You can also use our EU endpoint to keep traffic within the EU like so:
# configuration = stadiamaps.Configuration(host="https://api-eu.stadiamaps.com")
configuration = stadiamaps.Configuration()

# Configure API key authentication (ex: via environment variable).
configuration.api_key['ApiKeyAuth'] = os.environ["API_KEY"]

with stadiamaps.ApiClient(configuration) as api_client:
    # Create an instance of the API class
    api_instance = stadiamaps.RoutingApi(api_client)

    try:
        req = stadiamaps.MapMatchRequest(
            id="map_match",
            encoded_polyline="_grbgAh~{nhF?lBAzBFvBHxBEtBKdB?fB@dBZdBb@hBh@jBb@x@\\|@x@pB\\x@v@hBl@nBPbCXtBn@|@z@ZbAEbAa@~@q@z@QhA]pAUpAVhAPlAWtASpAAdA[dASdAQhAIlARjANnAZhAf@n@`A?lB^nCRbA\\xB`@vBf@tBTbCFbARzBZvBThBRnBNrBP`CHbCF`CNdCb@vBX`ARlAJfADhA@dAFdAP`AR`Ah@hBd@bBl@rBV|B?vB]tBCvBBhAF`CFnBXtAVxAVpAVtAb@|AZ`Bd@~BJfA@fAHdADhADhABjAGzAInAAjAB|BNbCR|BTjBZtB`@lBh@lB\\|Bl@rBXtBN`Al@g@t@?nAA~AKvACvAAlAMdAU`Ac@hAShAI`AJ`AIdAi@bAu@|@k@p@]p@a@bAc@z@g@~@Ot@Bz@f@X`BFtBXdCLbAf@zBh@fBb@xAb@nATjAKjAW`BI|AEpAHjAPdAAfAGdAFjAv@p@XlAVnA?~A?jAInAPtAVxAXnAf@tBDpBJpBXhBJfBDpAZ|Ax@pAz@h@~@lA|@bAnAd@hAj@tAR~AKxAc@xAShA]hAIdAAjA]~A[v@BhB?dBSv@Ct@CvAI~@Oz@Pv@dAz@lAj@~A^`B^|AXvAVpAXdBh@~Ap@fCh@hB\\zBN`Aj@xBFdA@jALbAPbAJdAHdAJbAHbAHfAJhALbA\\lBTvBAdC@bC@jCKjASbC?`CM`CDpB\\xAj@tB\\fA\\bAVfAJdAJbAXz@L|BO`AOdCDdA@~B\\z@l@v@l@v@l@r@j@t@b@x@b@r@z@jBVfCJdAJdANbCPfCF|BRhBS~BS`AYbAe@~BQdA",
            costing=stadiamaps.MapMatchCostingModel.PEDESTRIAN,
            units=stadiamaps.DistanceUnit.MI,
            linear_references=True,
        )

        res = api_instance.map_match(req)
    except ApiException as e:
        # Add your error handling here
        print("Exception when calling the Stadia Maps API: %s\n" % e)
Installation Instructions

Authenticate to GitHub Packages

We host our Maven packages on GitHub Packages. GitHub requires authentication for this, and has a guide guide on setting this up.

Add the Maven Repository

Next, add the repository to the repositories block in your Gradle build script like so.

build.gradle.kts
repositories {
    mavenCentral()

    maven {
        url = uri("https://maven.pkg.github.com/stadiamaps/stadiamaps-api-kotlin")
        credentials {
            username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
            password = project.findProperty("gpr.token") as String? ?: System.getenv("TOKEN")
        }
    }
}
build.gradle
repositories {
    mavenCentral()

    maven {
        url = uri("https://maven.pkg.github.com/stadiamaps/stadiamaps-api-kotlin")
        credentials {
            username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
            password = project.findProperty("gpr.token") ?: System.getenv("TOKEN")
        }
    }
}

Add Dependencies

Now you're ready to add the package and its dependencies.

build.gradle.kts
dependencies {
    val retrofitVersion = "2.9.0"

    // API package
    implementation("com.stadiamaps:api:1.0.0")

    // Dependencies
    implementation("com.squareup.moshi:moshi-kotlin:1.14.0")
    implementation("com.squareup.moshi:moshi-adapters:1.14.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
    implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
    implementation("com.squareup.retrofit2:converter-moshi:$retrofitVersion")
    implementation("com.squareup.retrofit2:converter-scalars:$retrofitVersion")
}
build.gradle
dependencies {
    def retrofitVersion = "2.9.0"

    // API package
    implementation 'com.stadiamaps:api:3.0.0'

    // Dependencies
    implementation 'com.squareup.moshi:moshi-kotlin:1.15.1'
    implementation 'com.squareup.moshi:moshi-adapters:1.15.1'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0'
    implementation "com.squareup.retrofit2:retrofit:${retrofitVersion}"
    implementation "com.squareup.retrofit2:converter-moshi:${retrofitVersion}"
    implementation "com.squareup.retrofit2:converter-scalars:${retrofitVersion}"
}
// Imports (at the top of your source file; we've used some wildcard imports for simplicity)
import com.stadiamaps.api.apis.*
import com.stadiamaps.api.auth.ApiKeyAuth
import com.stadiamaps.api.infrastructure.*
import com.stadiamaps.api.models.*

// Set your API key (from an environment variable in this case)
val apiKey = System.getenv("STADIA_API_KEY") ?: throw RuntimeException("API Key not set")

// Defining the host is optional and defaults to https://api.stadiamaps.com
// You can also use our EU endpoint to keep traffic within the EU like so:
// val client = ApiClient(baseUrl = "https://api-eu.stadiamaps.com")
val client = ApiClient()
client.addAuthorization("ApiKeyAuth", ApiKeyAuth("query", "api_key", apiKey))

// Configure a service for the group of APIs we want to talk to
val service = client.createService(RoutingApi::class.java)

// Set up the request.
// Note: this code is blocking for demonstration purposes.
// If you're using Kotlin with coroutines,
// you can also use these asynchronously within suspend functions.
// Synchronous code can enqueue a callback to avoid blocking
// (you'll definitely want to do one of these instead when on the main thread of an app).
// See the docs for details: https://square.github.io/retrofit/2.x/retrofit/retrofit2/Call.html
val req = MapMatchRequest(
    id = "map_match",
    encodedPolyline = "_grbgAh~{nhF?lBAzBFvBHxBEtBKdB?fB@dBZdBb@hBh@jBb@x@\\|@x@pB\\x@v@hBl@nBPbCXtBn@|@z@ZbAEbAa@~@q@z@QhA]pAUpAVhAPlAWtASpAAdA[dASdAQhAIlARjANnAZhAf@n@`A?lB^nCRbA\\xB`@vBf@tBTbCFbARzBZvBThBRnBNrBP`CHbCF`CNdCb@vBX`ARlAJfADhA@dAFdAP`AR`Ah@hBd@bBl@rBV|B?vB]tBCvBBhAF`CFnBXtAVxAVpAVtAb@|AZ`Bd@~BJfA@fAHdADhADhABjAGzAInAAjAB|BNbCR|BTjBZtB`@lBh@lB\\|Bl@rBXtBN`Al@g@t@?nAA~AKvACvAAlAMdAU`Ac@hAShAI`AJ`AIdAi@bAu@|@k@p@]p@a@bAc@z@g@~@Ot@Bz@f@X`BFtBXdCLbAf@zBh@fBb@xAb@nATjAKjAW`BI|AEpAHjAPdAAfAGdAFjAv@p@XlAVnA?~A?jAInAPtAVxAXnAf@tBDpBJpBXhBJfBDpAZ|Ax@pAz@h@~@lA|@bAnAd@hAj@tAR~AKxAc@xAShA]hAIdAAjA]~A[v@BhB?dBSv@Ct@CvAI~@Oz@Pv@dAz@lAj@~A^`B^|AXvAVpAXdBh@~Ap@fCh@hB\\zBN`Aj@xBFdA@jALbAPbAJdAHdAJbAHbAHfAJhALbA\\lBTvBAdC@bC@jCKjASbC?`CM`CDpB\\xAj@tB\\fA\\bAVfAJdAJbAXz@L|BO`AOdCDdA@~B\\z@l@v@l@v@l@r@j@t@b@x@b@r@z@jBVfCJdAJdANbCPfCF|BRhBS~BS`AYbAe@~BQdA",
    costing = MapMatchCostingModel.pedestrian,
    linearReferences = true,
)
val res = service.mapMatch(req).execute()

if (res.isSuccessful) {
    println("Found result: ${res.body()}")
} else {
    println("Request failed with error code ${res.code()}")
}
Installation Instructions

Our Swift SDK is distributed using the Swift Package Manager (SPM). Apple's documentation shows how to add a Swift Package dependency to your Xcode project. On the Add Package screen, you can find our package by its repository URL: https://github.com/stadiamaps/stadiamaps-api-swift.

import StadiaMaps

// This setup code can go anywhere before you actually make an API call (typically in your app init)
func setupStadiaMapsAPI() {
    // Set your API key
    StadiaMapsAPI.customHeaders = ["Authorization": "Stadia-Auth YOUR-API-KEY"]
    // Optionally use our EU endpoint to keep traffic within the EU
    // StadiaMapsAPI.basePath = "https://api-eu.stadiamaps.com"
}

// This function demonstrates how to call the Stadia Maps API.
// If you have not yet adopted async/await in your Swift codebase, you can use the Task API
// to call async functions in a non-async context: https://developer.apple.com/documentation/swift/task.
func myFunction() async throws {
    let req = MapMatchRequest(
        id: "map_match",
        encodedPolyline: "_grbgAh~{nhF?lBAzBFvBHxBEtBKdB?fB@dBZdBb@hBh@jBb@x@\\|@x@pB\\x@v@hBl@nBPbCXtBn@|@z@ZbAEbAa@~@q@z@QhA]pAUpAVhAPlAWtASpAAdA[dASdAQhAIlARjANnAZhAf@n@`A?lB^nCRbA\\xB`@vBf@tBTbCFbARzBZvBThBRnBNrBP`CHbCF`CNdCb@vBX`ARlAJfADhA@dAFdAP`AR`Ah@hBd@bBl@rBV|B?vB]tBCvBBhAF`CFnBXtAVxAVpAVtAb@|AZ`Bd@~BJfA@fAHdADhADhABjAGzAInAAjAB|BNbCR|BTjBZtB`@lBh@lB\\|Bl@rBXtBN`Al@g@t@?nAA~AKvACvAAlAMdAU`Ac@hAShAI`AJ`AIdAi@bAu@|@k@p@]p@a@bAc@z@g@~@Ot@Bz@f@X`BFtBXdCLbAf@zBh@fBb@xAb@nATjAKjAW`BI|AEpAHjAPdAAfAGdAFjAv@p@XlAVnA?~A?jAInAPtAVxAXnAf@tBDpBJpBXhBJfBDpAZ|Ax@pAz@h@~@lA|@bAnAd@hAj@tAR~AKxAc@xAShA]hAIdAAjA]~A[v@BhB?dBSv@Ct@CvAI~@Oz@Pv@dAz@lAj@~A^`B^|AXvAVpAXdBh@~Ap@fCh@hB\\zBN`Aj@xBFdA@jALbAPbAJdAHdAJbAHbAHfAJhALbA\\lBTvBAdC@bC@jCKjASbC?`CM`CDpB\\xAj@tB\\fA\\bAVfAJdAJbAXz@L|BO`AOdCDdA@~B\\z@l@v@l@v@l@r@j@t@b@x@b@r@z@jBVfCJdAJdANbCPfCF|BRhBS~BS`AYbAe@~BQdA",
        costing: .pedestrian,
        linearReferences: true
    )
    let res = try await RoutingAPI.mapMatch(mapMatchRequest: req)

    // Do something with the response...
    print(res)
}
Installation Instructions

Composer

To install the package via Composer, add stadiamaps/stadiamaps-api-php to your composer.json:

{
  "require": {
    "stadiamaps/stadiamaps-api-php": "1.*"
  }
}

Then run composer install.

Manual Installation

You can also download the files manually and include autoload.php in your scripts:

<?php
require_once('/path/to/OpenAPIClient-php/vendor/autoload.php');
<?php
// use or require, depending on your installation method.

// Configure API key authorization (replace with your Stadia Maps API key)
$config = OpenAPI\Client\Configuration::getDefaultConfiguration()->setApiKey('api_key', 'YOUR-API-KEY');
// You can also use our EU endpoint to keep traffic within the EU using setHost:
// $config = Configuration::getDefaultConfiguration()->setApiKey('api_key', 'YOUR-API-KEY')->setHost('https://api-eu.stadiamaps.com');

$apiInstance = new OpenAPI\Client\Api\RoutingApi(
    new GuzzleHttp\Client(),
    $config
);

try {
    $req = new MapMatchRequest(array('id' => 'mapMatch',
        'encoded_polyline' => '_grbgAh~{nhF?lBAzBFvBHxBEtBKdB?fB@dBZdBb@hBh@jBb@x@\|@x@pB\x@v@hBl@nBPbCXtBn@|@z@ZbAEbAa@~@q@z@QhA]pAUpAVhAPlAWtASpAAdA[dASdAQhAIlARjANnAZhAf@n@`A?lB^nCRbA\xB`@vBf@tBTbCFbARzBZvBThBRnBNrBP`CHbCF`CNdCb@vBX`ARlAJfADhA@dAFdAP`AR`Ah@hBd@bBl@rBV|B?vB]tBCvBBhAF`CFnBXtAVxAVpAVtAb@|AZ`Bd@~BJfA@fAHdADhADhABjAGzAInAAjAB|BNbCR|BTjBZtB`@lBh@lB\|Bl@rBXtBN`Al@g@t@?nAA~AKvACvAAlAMdAU`Ac@hAShAI`AJ`AIdAi@bAu@|@k@p@]p@a@bAc@z@g@~@Ot@Bz@f@X`BFtBXdCLbAf@zBh@fBb@xAb@nATjAKjAW`BI|AEpAHjAPdAAfAGdAFjAv@p@XlAVnA?~A?jAInAPtAVxAXnAf@tBDpBJpBXhBJfBDpAZ|Ax@pAz@h@~@lA|@bAnAd@hAj@tAR~AKxAc@xAShA]hAIdAAjA]~A[v@BhB?dBSv@Ct@CvAI~@Oz@Pv@dAz@lAj@~A^`B^|AXvAVpAXdBh@~Ap@fCh@hB\zBN`Aj@xBFdA@jALbAPbAJdAHdAJbAHbAHfAJhALbA\lBTvBAdC@bC@jCKjASbC?`CM`CDpB\xAj@tB\fA\bAVfAJdAJbAXz@L|BO`AOdCDdA@~B\z@l@v@l@v@l@r@j@t@b@x@b@r@z@jBVfCJdAJdANbCPfCF|BRhBS~BS`AYbAe@~BQdA',
        'costing' => CostingModel::PEDESTRIAN,
        'units' => DistanceUnit::MI));
    $result = $apiInstance->mapMatch($req);
} catch (Exception $e) {
    // Add your error handling here
    echo 'Exception when calling the Stadia Maps API: ', $e->getMessage(), PHP_EOL;
}
curl -X POST -H "Content-Type: application/json" --data-raw '{
    "id": "mapMatch",
    "encoded_polyline": "_grbgAh~{nhF?lBAzBFvBHxBEtBKdB?fB@dBZdBb@hBh@jBb@x@\\|@x@pB\\x@v@hBl@nBPbCXtBn@|@z@ZbAEbAa@~@q@z@QhA]pAUpAVhAPlAWtASpAAdA[dASdAQhAIlARjANnAZhAf@n@`A?lB^nCRbA\\xB`@vBf@tBTbCFbARzBZvBThBRnBNrBP`CHbCF`CNdCb@vBX`ARlAJfADhA@dAFdAP`AR`Ah@hBd@bBl@rBV|B?vB]tBCvBBhAF`CFnBXtAVxAVpAVtAb@|AZ`Bd@~BJfA@fAHdADhADhABjAGzAInAAjAB|BNbCR|BTjBZtB`@lBh@lB\\|Bl@rBXtBN`Al@g@t@?nAA~AKvACvAAlAMdAU`Ac@hAShAI`AJ`AIdAi@bAu@|@k@p@]p@a@bAc@z@g@~@Ot@Bz@f@X`BFtBXdCLbAf@zBh@fBb@xAb@nATjAKjAW`BI|AEpAHjAPdAAfAGdAFjAv@p@XlAVnA?~A?jAInAPtAVxAXnAf@tBDpBJpBXhBJfBDpAZ|Ax@pAz@h@~@lA|@bAnAd@hAj@tAR~AKxAc@xAShA]hAIdAAjA]~A[v@BhB?dBSv@Ct@CvAI~@Oz@Pv@dAz@lAj@~A^`B^|AXvAVpAXdBh@~Ap@fCh@hB\\zBN`Aj@xBFdA@jALbAPbAJdAHdAJbAHbAHfAJhALbA\\lBTvBAdC@bC@jCKjASbC?`CM`CDpB\\xAj@tB\\fA\\bAVfAJdAJbAXz@L|BO`AOdCDdA@~B\\z@l@v@l@v@l@r@j@t@b@x@b@r@z@jBVfCJdAJdANbCPfCF|BRhBS~BS`AYbAe@~BQdA",
    "costing": "pedestrian",
    "units": "miles",
    "linear_references": true
}' "https://api.stadiamaps.com/map_match/v1?api_key=YOUR-API-KEY"

Tips and Tricks

From noisy sensors to map errors, map matching is a complicated art. Here are some tips to help you get the most out of the API.

Data Resolution

Our map matching API has significantly higher limits than you may be used to at other vendors, so you don't need to worry about reducing a complex route to fewer than 100 points. We still recommend filtering trace points that are close together (you'll get faster responses!), but you can increase the resolution if it makes sense for your use case.

Note that consecutive points should be less than 2km apart, or we'll assume you're starting a new trip.

Check out the Options

The API has configurable tracing options including the search radius for each point, an estimate of how accurate the input is, and the ability to ignore restrictions. Check out the map matching section of the interactive API reference for details.

Complementary APIs

You can get detailed road attributes along a route with the trace attributes endpoint. Additionally, you can build an elevation profile using our elevation API.

Next Steps