Map matching (beta)
Snap a GPS trace to the roads it was driven on and measure the drive.
trace.match
Beta
point_1k
How to call it
REST
POST
/v1/trace/match
GraphQL
query
openTraceMatch
MCP
open_trace_match
Snap a GPS trace (2-5000 points with optional times) to the roads it was driven on. Returns each point placed on its road with how far it moved, the roads driven with their lengths, the total distance and the driven line as GeoJSON. Billed per point.
Request schema
Generated from the same validation rules the endpoint runs, so a field cannot be documented without being validated.
POST /v1/trace/match
{
"type": "object",
"properties": {
"geometry": {
"type": [
"boolean",
"null"
]
},
"points": {
"type": "array",
"minItems": 2,
"maxItems": 5000,
"items": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"lat": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"lng": {
"type": "number",
"minimum": -180,
"maximum": 180
},
"time": []
},
"required": [
"lat",
"lng"
]
}
},
"profile": {
"type": [
"string",
"null"
],
"enum": [
"car",
"van",
"truck",
"bike",
"foot",
"motorcycle"
]
},
"search_radius_meters": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 200
}
},
"additionalProperties": false,
"required": [
"points"
]
}
Request example
{
"points": [
{
"id": "fix-1",
"lat": 45.498348,
"lng": -73.571617,
"time": "2026-11-02T08:00:00-05:00"
},
{
"id": "fix-2",
"lat": 45.499071,
"lng": -73.569882,
"time": "2026-11-02T08:00:21-05:00"
},
{
"id": "fix-3",
"lat": 45.501306,
"lng": -73.567881,
"time": "2026-11-02T08:00:42-05:00"
},
{
"id": "fix-4",
"lat": 45.502841,
"lng": -73.566375,
"time": "2026-11-02T08:01:03-05:00"
},
{
"id": "fix-5",
"lat": 45.502257,
"lng": -73.564575,
"time": "2026-11-02T08:01:24-05:00"
},
{
"id": "fix-6",
"lat": 45.502093,
"lng": -73.563383,
"time": "2026-11-02T08:01:45-05:00"
},
{
"id": "fix-7",
"lat": 45.502914,
"lng": -73.560675,
"time": "2026-11-02T08:02:07-05:00"
},
{
"id": "fix-8",
"lat": 45.504991,
"lng": -73.558915,
"time": "2026-11-02T08:02:28-05:00"
}
],
"profile": "car"
}
Response
What a successful call returns: the schema of `data` and `meta`, with a description of each field, followed by an example response from a real call.
POST /v1/trace/match
Response schema
{
"description": "The trace matched to roads: every point placed, the roads driven and the drive's length. Billed per point.",
"data": {
"type": "object",
"properties": {
"points": {
"type": "array",
"description": "Every point of the trace, in the order given.",
"items": {
"type": "object",
"properties": {
"index": {
"type": "integer",
"description": "The point's place in `points`, from 0."
},
"id": {
"type": [
"string",
"null"
],
"description": "The point's id, when given."
},
"type": {
"type": "string",
"enum": [
"matched",
"interpolated",
"unmatched"
],
"description": "`matched`: placed on a road; `interpolated`: placed between matched points; `unmatched`: no road found."
},
"lat": {
"type": [
"number",
"null"
],
"description": "Where the point lies on the road."
},
"lng": {
"type": [
"number",
"null"
],
"description": "Where the point lies on the road."
},
"moved_meters": {
"type": [
"number",
"null"
],
"description": "How far the point was moved onto the road."
},
"road": {
"type": [
"string",
"null"
],
"description": "The name of the road, when it has one."
}
}
}
},
"roads": {
"type": "array",
"description": "The roads driven, in order; consecutive stretches of one road are one entry.",
"items": {
"type": "object",
"properties": {
"names": {
"type": "array",
"items": {
"type": "string"
},
"description": "The road's names; empty for an unnamed road."
},
"road_class": {
"type": "string",
"description": "The road's class: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other."
},
"speed_kmh": {
"type": [
"integer",
"null"
],
"description": "The road's typical speed."
},
"distance_meters": {
"type": "integer",
"description": "Distance driven on it."
}
}
}
},
"summary": {
"type": "object",
"properties": {
"points": {
"type": "integer",
"description": "Points submitted."
},
"matched": {
"type": "integer",
"description": "Points placed on a road."
},
"interpolated": {
"type": "integer",
"description": "Points placed between matched points."
},
"unmatched": {
"type": "integer",
"description": "Points no road was found for."
},
"distance_meters": {
"type": "integer",
"description": "The length of the drive."
}
}
},
"geometry": {
"type": "object",
"description": "The driven line as a GeoJSON LineString ([lng, lat]). Absent when `geometry` is false."
}
},
"required": [
"points",
"roads",
"summary"
]
},
"meta": {
"type": "object",
"properties": {},
"additionalProperties": true
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "trace.match",
"billing": {
"unit": "point_1k",
"quantity": 8,
"counted": true
},
"data": {
"points": [
{
"index": 0,
"id": "fix-1",
"type": "matched",
"lat": 45.498367,
"lng": -73.571597,
"moved_meters": 2.6,
"road": "Rue Stanley"
},
{
"index": 1,
"id": "fix-2",
"type": "matched",
"lat": 45.499086,
"lng": -73.569893,
"moved_meters": 2,
"road": "Boulevard René-Lévesque Ouest"
},
{
"index": 2,
"id": "fix-3",
"type": "matched",
"lat": 45.50129,
"lng": -73.567829,
"moved_meters": 4.4,
"road": "Boulevard René-Lévesque Ouest"
},
{
"index": 3,
"id": "fix-4",
"type": "matched",
"lat": 45.502922,
"lng": -73.566398,
"moved_meters": 9.3,
"road": "Boulevard René-Lévesque Ouest"
},
{
"index": 4,
"id": "fix-5",
"type": "matched",
"lat": 45.502289,
"lng": -73.564558,
"moved_meters": 3.8,
"road": "Côte du Beaver Hall"
},
{
"index": 5,
"id": "fix-6",
"type": "matched",
"lat": 45.502045,
"lng": -73.563414,
"moved_meters": 5.8,
"road": "Côte du Beaver Hall"
},
{
"index": 6,
"id": "fix-7",
"type": "matched",
"lat": 45.502874,
"lng": -73.560672,
"moved_meters": 4.4,
"road": "Rue Saint-Antoine Ouest"
},
{
"index": 7,
"id": "fix-8",
"type": "matched",
"lat": 45.504973,
"lng": -73.558873,
"moved_meters": 3.8,
"road": "Rue Saint-Antoine Ouest"
}
],
"roads": [
{
"names": [
"Rue Stanley"
],
"road_class": "residential",
"speed_kmh": 30,
"distance_meters": 67
},
{
"names": [
"Boulevard René-Lévesque Ouest"
],
"road_class": "secondary",
"speed_kmh": 50,
"distance_meters": 662
},
{
"names": [
"Côte du Beaver Hall"
],
"road_class": "tertiary",
"speed_kmh": 40,
"distance_meters": 280
},
{
"names": [
"Rue du Square Victoria"
],
"road_class": "tertiary",
"speed_kmh": 30,
"distance_meters": 113
},
{
"names": [
"Rue Saint-Antoine Ouest"
],
"road_class": "secondary",
"speed_kmh": 49,
"distance_meters": 463
}
],
"summary": {
"points": 8,
"matched": 8,
"interpolated": 0,
"unmatched": 0,
"distance_meters": 1585
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-73.571598,
45.498367
],
[
-73.571479,
45.498308
],
[
-73.571149,
45.498132
],
[
-73.57112,
45.498116
],
[
-73.571049,
45.498079
],
[
-73.570902,
45.498007
],
[
-73.57085,
45.498061
],
[
-73.570706,
45.498216
],
[
-73.570579,
45.498355
],
[
-73.570424,
45.498523
],
[
-73.570397,
45.498553
],
[
-73.570284,
45.498673
],
[
-73.570199,
45.498762
],
[
-73.57015,
45.498813
],
[
-73.570117,
45.498849
],
[
-73.569971,
45.499004
],
[
-73.569894,
45.499087
],
[
-73.56974,
45.49925
],
[
-73.569719,
45.499274
],
[
-73.569664,
45.499334
],
[
-73.569541,
45.499463
],
[
-73.56949,
45.499517
],
[
-73.569244,
45.499785
],
[
-73.56917,
45.49986
],
[
-73.569046,
45.49999
],
[
-73.569027,
45.500011
],
[
-73.568971,
45.500073
],
[
-73.568916,
45.500131
],
[
-73.568903,
45.500144
],
[
-73.568534,
45.500536
],
[
-73.568133,
45.500966
],
[
-73.568076,
45.501026
],
[
-73.567829,
45.501291
],
[
-73.567507,
45.501639
],
[
-73.567451,
45.501698
],
[
-73.567428,
45.50172
],
[
-73.567354,
45.501796
],
[
-73.567236,
45.501928
],
[
-73.567174,
45.501996
],
[
-73.566956,
45.502222
],
[
-73.566784,
45.502408
],
[
-73.566768,
45.502425
],
[
-73.566724,
45.502472
],
[
-73.566681,
45.502519
],
[
-73.566667,
45.502534
],
[
-73.566643,
45.502565
],
[
-73.566585,
45.502648
],
[
-73.566541,
45.502711
],
[
-73.566415,
45.502899
],
[
-73.566399,
45.502923
],
[
-73.566333,
45.503032
],
[
-73.56619,
45.502991
],
[
-73.566159,
45.50298
],
[
-73.565696,
45.502759
],
[
-73.565468,
45.502653
],
[
-73.565065,
45.502461
],
[
-73.565035,
45.502448
],
[
-73.564962,
45.502418
],
[
-73.564879,
45.502387
],
[
-73.564846,
45.502376
],
[
-73.564782,
45.502354
],
[
-73.56471,
45.502331
],
[
-73.564633,
45.502308
],
[
-73.564543,
45.502286
],
[
-73.564463,
45.502269
],
[
-73.564401,
45.502257
],
[
-73.564333,
45.502243
],
[
-73.56429,
45.502236
],
[
-73.564205,
45.502225
],
[
-73.564119,
45.502213
],
[
-73.56408,
45.502208
],
[
-73.564013,
45.502196
],
[
-73.563923,
45.50218
],
[
-73.563849,
45.502165
],
[
-73.563775,
45.502149
],
[
-73.563712,
45.502133
],
[
-73.563632,
45.502112
],
[
-73.563552,
45.502088
],
[
-73.563477,
45.502066
],
[
-73.563379,
45.502034
],
[
-73.563277,
45.502
],
[
-73.563235,
45.501983
],
[
-73.563188,
45.501961
],
[
-73.56315,
45.50194
],
[
-73.563069,
45.501894
],
[
-73.562052,
45.50145
],
[
-73.56202,
45.501435
],
[
-73.561934,
45.501394
],
[
-73.561903,
45.50143
],
[
-73.561881,
45.501457
],
[
-73.561722,
45.501634
],
[
-73.561697,
45.501663
],
[
-73.561641,
45.501726
],
[
-73.561589,
45.501786
],
[
-73.561222,
45.50223
],
[
-73.560672,
45.502874
],
[
-73.560443,
45.503122
],
[
-73.560422,
45.503145
],
[
-73.56037,
45.503204
],
[
-73.560308,
45.503277
],
[
-73.559974,
45.50368
],
[
-73.559757,
45.503939
],
[
-73.559708,
45.503996
],
[
-73.559662,
45.504051
],
[
-73.559594,
45.504126
],
[
-73.559294,
45.504485
],
[
-73.559215,
45.504579
],
[
-73.559195,
45.504603
],
[
-73.559164,
45.50464
],
[
-73.559129,
45.504678
],
[
-73.558989,
45.50484
],
[
-73.558874,
45.504973
]
]
}
},
"meta": {
"profile": "car",
"geometry_format": "geojson"
}
}
What it costs
Thousands of points, charged fractionally.
Every billable response carries a billing block with the unit, the quantity and how the quantity was derived.
Errors it can return
| Status | Code | Description |
|---|---|---|
| 401 | TOKEN_INVALID |
Unknown or revoked API token. |
| 401 | TOKEN_MISSING |
No API token. Send Authorization: Bearer <your token>. |
| 402 | QUOTA_EXHAUSTED |
The included allowance for this capability is used up for the current period. |
| 403 | CAPABILITY_NOT_ENABLED |
This capability is not enabled for your account. See :doc to enable it. |
| 403 | CAPABILITY_NOT_GRANTED_TO_KEY |
Your account has this capability, but this API key was not granted it. |
| 403 | OPEN_API_DISABLED |
Open Platform access is not enabled for this account. |
| 422 | VALIDATION_FAILED |
The request failed validation. See details. |
| 429 | RATE_LIMITED |
Too many requests. Please retry after the indicated delay. |
| 500 | INTERNAL_ERROR |
An unexpected error occurred. Quote the request_id when contacting support. |
| 502 | ENGINE_UNAVAILABLE |
The routing engine is temporarily unreachable. Please retry. |