API reference

Every route the gateway serves, in one place. The pages before this one explain each service; this one is the table you keep open while you write the call.

The machine-readable version is /openapi.json (OpenAPI 3.1), discoverable from /.well-known/api-catalog. Generate a client from that rather than transcribing this page.

Conventions

Every route is a GET with query parameters and no request body, except the two BYOD writes. Responses are JSON except tiles (Mapbox Vector Tile bytes), terrain (PNG), glyphs and sprites.

Coordinates are longitude,latitude, in that order, in WGS84 degrees. Bounding boxes are minlng,minlat,maxlng,maxlat.

Authentication is one key in any of three places, checked in this order:

FormWhen
Authorization: Bearer um_live_…Server-side calls. The normal form.
X-API-Key: um_live_…Clients that reserve Authorization for something else.
?key=um_live_…Required for tiles, glyphs, sprites, terrain and contours: MapLibre cannot attach headers to the requests it fires for them. Stripped before the response is cached.

Auth runs before routing, so an unauthenticated request to any path returns 401, including a path that does not exist. See Authentication.

Add-ons gate the industry layers and grammars: legal-land, energy, agriculture, remote, mining. A key without the one a request needs gets 403 with the add-on named. See Layers for how to buy one.

Maps

GET /styles/{style}.json

A MapLibre style document. The one URL most applications need.

ParameterInNotes
stylepath, requiredbase, muted, outdoor, blueprint, blush, orchid, canopy, lagoon, tropic, sunset, bold, pastel. An optional -light or -dark suffix sets the mode inline.
modequerylight or dark. An unrecognised value is ignored, not rejected.
langqueryBCP 47 primary subtag, default en. fr-CA resolves to fr. Falls back to Accept-Language, then en.
themequeryA ut1. code from unmap.dev/create. A theme replaces the cartography, so it wins over the style in the path.
overlaysqueryComma-separated catalog layer ids, or byod:<id>. Overrides profile.
profilequeryenergy, agriculture, remote or mining. A composition preset, not a style.

Returns application/json: a complete style with sources, layers, glyphs, sprite and metadata.

Also 400 unknown overlay id or profile · 404 the style name does not resolve.

GET /tiles/{z}/{x}/{y}

The Canada basemap, as Mapbox Vector Tile bytes. Range-read from object storage, never through compute, which is why it is the fastest thing here.

z, x, y are path integers. ?key= is required.

Returns application/x-protobuf.

Also 204 the request is inside the archive but there is no tile there · 400 malformed coordinates, as plain text rather than JSON.

GET /terrain/{z}/{x}/{y}

MRDEM-30 elevation for hillshade, Terrarium-encoded. Used by the outdoor style. A versioned form, /terrain/{version}/{z}/{x}/{y}, exists so a rebuilt archive gets a fresh URL; those are cached immutable for a year.

Returns image/png. Also 204 no tile · 404 the archive is not configured.

GET /contours/{z}/{x}/{y}

Contour lines from the same DEM, z9 to z13, with no 0 m line. Versioned form available.

Returns application/x-protobuf. Also 204 no tile · 404 not configured.

GET /glyphs/{fontstack}/{range}.pbf

Font glyphs, for example Noto Sans Regular/0-255.pbf. Includes the faces that draw syllabics. Versioned form available. Not metered.

Returns application/x-protobuf. Also 404 unknown fontstack or range.

GET /sprite/{file}

Sprite sheet, .json or .png, with @2x variants. Versioned form available. Not metered.

Returns application/json or image/png. Also 400 unexpected extension · 404 unknown sprite.

Catalog and data

GET /layers

The whole catalog. Raw storage object keys are never exposed; incomplete coverage always is.

Returns application/json, an array of:

{
  "id": "energy.wells",
  "display_name": { "en": "Wells", "fr": "Puits" },
  "namespace": "energy",
  "source": "",
  "source_url": "",
  "licence": "",
  "attribution": "",
  "coverage": "",
  "resolution": "",
  "updated_at": "2026-09-10",
  "source_updated_at": "",
  "ingestion_version": "1",
  "schema_version": "1",
  "freshness_policy": "",
  "known_gaps": "",
  "addon": "energy",
  "archives": [{ "id": "default", "minzoom": 4, "maxzoom": 12 }],
  "source_layer": "wells"
}

An archive holding more than one vector layer also carries source_layers, the full list. The survey grids use it: survey.dls stacks sixteen levels in one file and survey.nts four, each with its own zoom range, so a client can add exactly the level it wants as its own map layer. See Layers.

Read known_gaps before you promise coverage to a user. It is written to be accurate rather than flattering: several layers cover one province, not the country.

GET /layers/{id}

One catalog row, same shape. Also 404 unknown layer.

GET /layers/{id}/{z}/{x}/{y}

Overlay tiles for one catalog layer. ?key= is required.

Returns application/x-protobuf.

Also 204 outside the layer's zoom band, or no tile there · 403 add-on required · 404 the layer is catalogued but its archive is not uploaded yet.

GET /identify

Which catalog features contain a point. This is the click handler for a map, and it is not reverse geocoding: /geocode/reverse finds the nearest named places, identify returns the polygons the point falls inside.

ParameterInNotes
atquery, requiredlng,lat. Required unless you send lon and lat separately.
lon, latqueryAlternative to at.
layersqueryComma-separated identify-capable ids. Default municipalities, which is core, so a key with no add-ons still works.
geometryquerytrue to include each hit's polygon where the row has one.

Returns application/json:

{
  "at": [-114.0719, 51.0447],
  "results": [
    { "layer": "municipalities", "id": "ab:calgary", "name": "Calgary", "province": "AB" }
  ]
}

A result carries the fields its layer has: unit and survey_system on survey cells, uwi, licence, operator and status on energy rows, pid on parcels, geometry when asked for.

Also 400 bad point or unknown layer · 403 add-on required · 502 / 503 the backing service is unreachable or still waking.

GET /data/query

Every feature of one layer inside a bounding box. One layer per call, by design.

ParameterInNotes
layerquery, requiredOne queryable catalog id.
bboxquery, requiredminlng,minlat,maxlng,maxlat. A hard filter: nothing outside it is ever returned.
limitquery1 to 50, default 50. A non-numeric value falls back to the default rather than erroring.
geometryquerytrue to include each feature's geometry.

Returns application/json: { "layer": …, "bbox": [4], "results": [ … ] }, the results in the same shape as /identify.

Also 400 unknown layer id or malformed bbox · 403 add-on required · 501 the layer is real but tile-only, with the queryable ids named in the message.

Geocoding

One URL reads the shape of your query and routes it. You do not pick a backend, and profile= has no effect here: it composes maps, not searches.

Looks likeExampleNeeds
Civic address or placeCalgarycore
Legal land, DLS or NTSNW-25-24-1-W5legal-land
Well identifier00/07-10-013-09W4/0energy
BC parcel id010-867-813agriculture
Facility by nameKaybob South with layers=facilityenergy
Rural or general deliveryRR 2 Okotokscore

GET /geocode/search

ParameterInNotes
qquery, requiredThe search text.
langqueryWhich language renders as name, default en. Every result also carries all its languages under names.
limitquery1 to 50, default 10.
bboxqueryA hard filter, not a bias.
layersquerywell or facility. Searches energy instead of civic.
geometryquerytrue to include the survey polygon on a legal-land hit.

Returns application/json, an array of places:

[
  {
    "id": "",
    "name": "Iqaluit",
    "layer": "locality",
    "lng": -68.5170,
    "lat": 63.7467,
    "score": 18.4,
    "names": { "en": "Iqaluit", "iu": "ᐃᖃᓗᐃᑦ" }
  }
]

A result may also carry distance (metres, on nearby and reverse), category, address, legal_land, uwi, licence, operator, status, pid, bbox and geometry, depending on what matched.

Also 400 q missing, or a query that is Canada Post delivery terms and no community · 403 add-on required for the LSD, UWI or PID grammars.

A successful parse with no matching row returns []. Never a guessed centroid, and never a silent fall-through to address ranking.

GET /geocode/autocomplete

Type-ahead, same routing as search. q required; lang default en; limit 1 to 50, default 8. Returns the same array.

GET /geocode/nearby

Places of a category around a point.

ParameterInNotes
categoryquery, requiredOne to eight, comma-separated. A taxonomy id (health.pharmacy) or an everyday word in English or French (pharmacy, pharmacie). Case, accents and hyphens are ignored.
nearquerylng,lat. Exactly one of near and bbox is required.
bboxqueryAnchors at the box centre with a radius that covers it. "Around here", not a clipping rectangle.
radiusqueryMetres, 50 to 50000, default 5000. An explicit radius beats one derived from bbox.
limitquery1 to 50, default 10.
langqueryDefault en.

Returns the same array, carrying distance in metres.

Also 400 an unknown category token, named in the message, or both or neither of near and bbox.

GET /geocode/reverse

What is near a coordinate, ordered by true geodesic distance. lon and lat are both required as separate parameters. lang default en; limit 1 to 50, default 5.

Routing

GET /route

Point to point across the Canada graph.

ParameterInNotes
fromquery, requiredlng,lat. One parameter, unlike /isochrone.
toquery, requiredlng,lat.
modequeryauto, car, bicycle, pedestrian or truck, default auto. car and auto are the same profile. Anything else is a 400; nothing falls back silently.
use_highwaysquery0 to 1, default 0.5. 0 avoids motorways and trunks, 1 prefers them. Driving modes only.

Truck parameters apply only with mode=truck. Sending one on another mode is a 400, not a silent ignore.

ParameterDefaultRangeUnit
height4.11up to 10metres
width2.6up to 6metres
length21.64up to 50metres
weight21.77up to 100tonnes gross
axle_load9.070 to 40tonnes per axle
axle_count52 to 20axles
hazmatfalsebooleanavoids roads that prohibit
use_truck_route00 to 1prefer designated truck routes

Returns application/json: { "distanceMeters": 42731, "durationSeconds": 2184, "geometry": { … } }.

Also 400 unknown mode, or a truck option on another mode · 422 the graph cannot connect those two points.

GET /isochrone

Travel-time bands from a point. Takes lon and lat as two separate parameters, not from.

minutes is required: comma-separated bands, fractions accepted, at most four bands and each 120 minutes or less. Same mode, use_highways and truck parameters as /route.

Returns application/geo+json, a FeatureCollection with one polygon per band.

Also 400 more than four bands, or a band over 120 minutes, with the rule named.

Your own data

Ten overlays per account, 5 MB each. See Bring your own data.

RouteDoes
POST /byodUpload application/geo+json, application/vnd.pmtiles, or multipart/form-data with a file field. ?name= sets the display name when the body is not multipart. Returns 201 with the overlay.
GET /byodYour overlays and the current quota.
GET /byod/{id}One overlay's metadata.
GET /byod/{id}/dataThe stored bytes.
GET /byod/{id}/{z}/{x}/{y}Tiles, for a PMTiles upload. Needs ?key=.
DELETE /byod/{id}Remove it. 204, no body.

An overlay is { id, name, kind, bytes, createdAt, url, overlay }, where overlay is the byod:<id> token you pass to ?overlays=.

Also 400 missing body, wrong content type, over 5 MB, or an eleventh file · 404 unknown id, or one belonging to another account.

Deleting an overlay a style still names leaves that ?overlays=byod:<id> a 404, so update the style first.

Status codes

CodeMeans
200Success.
201Created, on POST /byod.
204Not an error. A tile request inside the archive with no data at that tile. A missing archive is 404, and the two are kept distinct so a misconfiguration cannot read as empty terrain.
400Bad parameter. The message names the rule.
401No key, or an unknown or disabled key. Also returned for unknown paths, since auth runs before routing.
403The key is valid but lacks the add-on this layer or grammar needs. The body names it.
404The layer is catalogued but its archive is not uploaded, or the style name does not resolve.
422/route only: the graph cannot connect those two points.
429Rate limited. Read Retry-After.
501/data/query only: the layer is real but tile-only. The message lists the queryable ids.
502, 503A backing service is unreachable or still waking. Retry.

Every error body is { "error": "…" }, with code and addon where they apply. See Errors for the full list of codes.

Next steps

  • Layers for the catalog and how to buy an add-on.
  • Errors for what each code means and how to handle it.
  • MCP to reach the same capabilities from an AI agent.