50,000 free calls a month, card-free. Get an API key →

Documentation menu
docs / analysis · raw .md

Analysis APIs: isochrones, matrices, along-route search, map matching, safety cameras, incidents & weather

Eight endpoints answer the fleet questions that sit around routing rather than on it: where can this vehicle get to in 30 minutes? (POST /isochrone), what are the travel times between every depot and every stop? (POST /matrix), what can I stop at on the way, and what does the detour really cost? (POST /route/along), which roads did this GPS trace actually drive? (POST /trace_route, POST /trace_attributes), which safety cameras sit on this route? (POST /v1/cameras/along), which closures or lane restrictions sit on this route? (POST /v1/incidents/along), and what will the weather be doing when I actually get there? (POST /v1/weather/along). The first five accept the same costing models as POST /route, including truck costing with dimensional and ADR constraints (ADR is the European agreement on carriage of dangerous goods by road; tunnel codes B–E restrict which tunnels a hazmat load may use; see conventions for the full table). An isochrone for a 44-tonne hazmat artic is the area that lorry can legally reach, not the area a car could.

Availability

Status, honestly: the hosted gateway at https://api.mapmap.ai is live: sign up for a key, or run the self-host distro. Base URL and authentication conventions are on the conventions page.

Shared conventions

  • You need an API key (Authorization: Bearer snk_…; get one in the quickstart).
  • Locations everywhere on this page are { "lat": …, "lon": … } objects with named keys, so there is no coordinate-order ambiguity.
  • costing is one of auto, truck, bus, motor_scooter, motorcycle, bicycle, pedestrian.
  • costing_options is passed to the engine verbatim, keyed by costing name. For trucks: {"truck": {"height": 4.0, "width": 2.55, "length": 16.5, "weight": 40.0, "axle_load": 9.0, "axle_count": 5, "hazmat": true, "top_speed": 90.0, "adr_tunnel_code": "C"}}; dimensions in metres, weights in tonnes, speed in km/h. adr_tunnel_code is honoured by our ADR-extended engine; a stock engine ignores unknown keys, so requests stay portable.
  • Requests bill by size: a matrix bills one call per started block of 25 elements (sources × targets), capped at 10,000 elements per request; an isochrone bills one call per contour (max 10 per request); an along-route search bills like a matrix on its detour budget (4 × max_detours elements, max 25 detours), plus one call when it computes the route for you. The class is Standard normally, Premium when the body carries "costing": "truck". See pricing for the per-class rates.

Isochrones: POST /isochrone

Reachability contours from an origin: how far can you get within a time or distance budget. Use it for delivery-coverage maps, depot placement, "can we serve this postcode in under 45 minutes?" checks, and driver-hours planning. The response is GeoJSON you can drop straight onto a map.

Parameters

FieldTypeRequiredDescription
locationsarray of {lat, lon}yes (≥ 1)Origin(s) the reachability is computed from.
costingstringyesCosting model (see shared conventions).
contoursarrayyes (≥ 1)Each contour is a time in minutes or a distance in kilometres; a contour with neither is a 400.
polygonsbooleannoReturn polygons instead of the default linestrings.
denoisenumberno01; higher drops smaller contour islands.
generalizenumbernoGeometry simplification tolerance in metres.
show_locationsbooleannoInclude the input locations as GeoJSON points in the response.
costing_optionsobjectnoVerbatim costing options, e.g. {"truck": {…}}.
idstringnoOpaque identifier echoed back.

Example

Where can a 4-metre-high truck get to from a London depot in 10 and 20 minutes:

sh
export BASE=https://api.mapmap.ai
export API_KEY=snk_...

curl -fsS -X POST "$BASE/isochrone" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "locations": [{ "lat": 51.5074, "lon": -0.1278 }],
  "costing": "truck",
  "contours": [{ "time": 10 }, { "time": 20 }],
  "polygons": true,
  "costing_options": { "truck": { "height": 4.0 } }
}'

Response (truncated)

A GeoJSON FeatureCollection, one feature per contour, returned from the routing engine. Each feature's properties carry the contour value (in the unit you asked for: minutes or km) and a metric of "time" or "distance":

json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": { "contour": 10.0, "metric": "time" },
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[-0.14, 51.50], [-0.11, 51.50], [-0.11, 51.52], [-0.14, 51.50]]]
      }
    }
  ]
}

(GeoJSON geometry itself is [lon, lat]; that is the GeoJSON standard, not ours.)

Deployment note: on self-hosted deployments running the GraphHopper engine (SN_ROUTING_ENGINE=graphhopper), isochrones are single-origin, cannot mix time and distance contours in one call, and the rings match your contours exactly only when they are evenly spaced. The Valhalla engine (the default) has none of these limits.

Matrix: POST /matrix

A many-to-many travel-time and distance matrix: sources are the rows, targets the columns. This is the workhorse behind dispatch decisions ("which of my 12 drivers is genuinely closest, by road, in a lorry?") and the same computation POST /optimise runs internally. Call it directly when you want the raw numbers for your own solver or ranking logic.

Parameters

FieldTypeRequiredDescription
sourcesarray of {lat, lon}yes (≥ 1)Row locations (origins).
targetsarray of {lat, lon}yes (≥ 1)Column locations (destinations).
costingstringyesCosting model (see shared conventions).
costing_optionsobjectnoVerbatim costing options, e.g. {"truck": {…}}.
unitsstringnokilometers or miles; accepted, but the gateway normalises response distances to metres regardless.
idstringnoOpaque identifier echoed back.

Example

One depot to two delivery cities, truck costing:

sh
export BASE=https://api.mapmap.ai
export API_KEY=snk_...

curl -fsS -X POST "$BASE/matrix" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "sources": [{ "lat": 51.5074, "lon": -0.1278 }],
  "targets": [
    { "lat": 52.4862, "lon": -1.8904 },
    { "lat": 53.4808, "lon": -2.2426 }
  ],
  "costing": "truck",
  "costing_options": { "truck": { "height": 4.0, "weight": 40.0 } }
}'

Response

Normalised and complete: this is the whole shape. Row-major matrices: durations in seconds, distances in metres, null where a pair is unreachable under the chosen costing:

json
{
  "durations": [[9000.0, 12500.0]],
  "distances": [[171100.0, 262000.0]]
}

durations[i][j] is from sources[i] to targets[j]. Unlike /optimise, unreachable pairs here are honest nulls, not sentinel costs.

Along-route search: POST /route/along

"Where can my driver get diesel on the way, and what does the stop really cost?" This endpoint searches your own places (the dataset you uploaded to /places) along a route, and ranks every candidate by honest detour cost: for each place inside the corridor, the routing engine measures leave-route → place → rejoin-route against staying on the route, so detour_seconds is real added driving time under your costing (a truck's detour, not a car's), never straight-line optimism. One primitive covers diesel, truck parking and any consumer POI: "coffee, adds 4 min".

Give it either a full route request (route, the same body as POST /route; the gateway computes the route for you) or an existing geometry (shape, polyline6 from a /route response leg). A cheap corridor pre-filter runs server-side first, so only the max_detours nearest candidates ever reach the matrix engine.

Parameters

FieldTypeRequiredDescription
routeobjectone of route / shapeA Valhalla-style route request (same body as POST /route, including the truck/ADR options). Detour matrices reuse its costing.
shapestringone of route / shapeAn existing route geometry as an encoded polyline with six digits of precision (polyline6).
costingstringnoshape mode only (default auto): costing model for the detour measurements.
costing_optionsobjectnoshape mode only: verbatim costing options, e.g. {"truck": {…}}.
categorystringnoOnly places carrying this category (case-insensitive), e.g. "diesel", "truck_parking", "coffee".
corridor_mnumbernoCorridor half-width in metres, 50–10,000 (default 2,000).
max_detoursintegernoCandidates ranked by detour cost, 1–25 (default 10); also the billing budget.

Example

Truck parking within 5 km of a Dover → Birmingham truck route:

sh
export BASE=https://api.mapmap.ai
export API_KEY=snk_...

curl -fsS -X POST "$BASE/route/along" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "route": {
    "locations": [
      { "lat": 51.1279, "lon": 1.3134 },
      { "lat": 52.4862, "lon": -1.8904 }
    ],
    "costing": "truck",
    "costing_options": { "truck": { "height": 4.0, "weight": 40.0 } }
  },
  "category": "truck_parking",
  "corridor_m": 5000,
  "max_detours": 10
}'

Response

Hits ranked by detour_seconds, cheapest stop first. along_fraction is how far along the route the stop sits (0 = origin, 1 = destination); offset_m its straight-line distance from the route; unreachable counts in-corridor candidates the engine could not connect:

json
{
  "count": 2,
  "corridor_m": 5000.0,
  "route_duration_s": 10620.0,
  "route_distance_m": 273400.0,
  "unreachable": 0,
  "results": [
    {
      "place": {
        "id": "tp-ashford", "name": "Ashford Truckstop",
        "lat": 51.13, "lon": 0.85, "categories": ["truck_parking"]
      },
      "detour_seconds": 240.0,
      "detour_km": 2.6,
      "along_fraction": 0.18,
      "offset_m": 850.0
    },
    {
      "place": {
        "id": "tp-oxford", "name": "Oxford Services",
        "lat": 51.83, "lon": -1.19, "categories": ["truck_parking"]
      },
      "detour_seconds": 660.0,
      "detour_km": 8.9,
      "along_fraction": 0.71,
      "offset_m": 2300.0
    }
  ]
}

No dataset uploaded yet (or nothing inside the corridor) is a well-formed empty result (count: 0), not an error. A 404 means the deployment has no customer-places storage configured (SN_PLACES_DIR on self-host; always on for the hosted gateway). Agents get the same capability as the MCP server's search_along_route tool.

Map matching: POST /trace_route and POST /trace_attributes

Both snap a recorded GPS trace to the road network. /trace_route returns a turn-by-turn route, the same response shape as POST /route (see the API reference), for cleaning up noisy telematics tracks into displayable routes. /trace_attributes returns the matched road segments and their attributes instead, for questions like "did this vehicle use the motorway?" or mileage auditing by road actually driven.

Both take the same request body:

Parameters

FieldTypeRequiredDescription
shapearray of {lat, lon}one of shape / encoded_polylineThe GPS trace as an ordered list of points.
encoded_polylinestringone of shape / encoded_polylineThe trace as an encoded polyline with six digits of precision (polyline6).
costingstringyesCosting model (see shared conventions).
shape_matchstringnoedge_walk (exact edge sequence, no snapping), map_snap (snap each point to the most likely edge), or walk_or_snap (try the first, fall back to the second; the default).
filtersobjectno/trace_attributes only: which edge attributes to include or exclude.
unitsstringnokilometers (default) or miles, for response length fields.
costing_optionsobjectnoVerbatim costing options.
idstringnoOpaque identifier echoed back.

Example

sh
export BASE=https://api.mapmap.ai
export API_KEY=snk_...

curl -fsS -X POST "$BASE/trace_route" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "shape": [
    { "lat": 51.5074, "lon": -0.1278 },
    { "lat": 51.5090, "lon": -0.1300 }
  ],
  "costing": "auto",
  "shape_match": "map_snap"
}'

Response (truncated)

/trace_route returns the standard route response: durations in seconds, length in the trip's units (kilometres by default), leg shape as polyline6:

json
{
  "trip": {
    "locations": [
      { "lat": 51.5074, "lon": -0.1278 },
      { "lat": 51.5090, "lon": -0.1300 }
    ],
    "legs": [{
      "maneuvers": [
        { "type": 1, "instruction": "Drive north on High Street.",
          "time": 240.0, "length": 0.4,
          "begin_shape_index": 0, "end_shape_index": 2 }
      ],
      "summary": { "time": 240.0, "length": 0.4 },
      "shape": "}~ycbBhavgN..."
    }],
    "summary": { "time": 240.0, "length": 0.4 },
    "status": 0,
    "status_message": "Found route between points",
    "units": "kilometers",
    "language": "en-US"
  }
}

/trace_attributes with the same body returns an open JSON document from the engine: the matched geometry plus per-edge attributes. The exact attribute set depends on the engine and your filters; expect shape (polyline6), matched_points and an edges array:

json
{
  "shape": "}~ycbBhavgN...",
  "matched_points": [
    { "lat": 51.5074, "lon": -0.1278 },
    { "lat": 51.5090, "lon": -0.1300 }
  ],
  "edges": [
    { "way_id": 12345, "speed": 48 }
  ],
  "units": "kilometers"
}

A request with neither a non-empty shape nor an encoded_polyline is a 400.

Safety cameras along a route (POST /v1/cameras/along)

Given a route shape, returns the safety cameras on it: where each camera sits along the route, its type, and the enforced limit where known. Built for navigation clients that warn the driver on approach. The data is baked per territory at build time, and exact positions are served only where that is lawful (see the jurisdiction policy below).

Two shapes of record come back. Point devices are a single camera at a position. Corridors are a stretch of enforced road, either average-speed section control or one of Ireland's published mobile-camera zones; they carry a zone object and the arc offsets of both ends. A corridor is always one entry, never one per device.

Parameters

FieldTypeRequiredDescription
shapestringyesThe route geometry as an encoded polyline with six digits of precision (polyline6), exactly as POST /route responses emit their leg shape.
buffer_mnumbernoCorridor half-width in metres for matching cameras to the shape. Default 40; values above 100 are clamped to 100; zero, negative or non-numeric is a 400.

Example

sh
export BASE=https://api.mapmap.ai
export API_KEY=snk_...

curl -fsS -X POST "$BASE/v1/cameras/along" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "shape": "}~ycbBhavgN...",
  "buffer_m": 40
}'

Response

json
{
  "cameras": [
    { "arc_m": 1234.5, "lat": 51.5074, "lon": -0.1278,
      "kind": "fixed", "limit_kph": 48, "bearing": null,
      "limit_confidence": "osm",
      "source": "osm", "licence": "ODbL-1.0" },
    { "arc_m": 4820.0, "lat": 53.3226, "lon": -6.4235,
      "kind": "mobile_site", "limit_kph": null, "bearing": null,
      "limit_confidence": "unknown",
      "from_arc_m": 4820.0, "to_arc_m": 6320.0,
      "zone": {
        "start": [53.322565, -6.423538],
        "end": [53.329367, -6.42377],
        "length_km": 1.5,
        "geometry": [[-6.423538, 53.322565], [-6.42377, 53.329367]],
        "geometry_parts": [2]
      },
      "source": "ie-garda-current-zones", "licence": "CC-BY-4.0" }
  ],
  "attribution": [
    "Contains Irish Public Sector Data licensed under a Creative Commons Attribution 4.0 International (CC BY 4.0) licence."
  ],
  "policy": { "country": "IE", "mode": "exact" }
}
  • cameras is ordered by arc_m, the distance in metres along the shape to each camera's on-route projection. For a corridor that is its start, so a client that only understands points needs no new code.
  • kind is fixed, average (a section-control camera), redlight, mobile_site (a published mobile-enforcement zone) or unknown.
  • limit_kph is the enforced limit where the data records one, on the camera itself or its enforcement zone. null is common and honest rather than an error: Ireland's published zones carry no limit at all. Never substitute a guess.
  • limit_confidence says how far limit_kph can be trusted: official (from the enforcing authority), osm (from map tagging), inferred_from_road, or unknown; always unknown when limit_kph is null.
  • bearing, when present, is the camera facing in degrees clockwise from north. Cameras facing away from the direction of travel (more than 60 degrees off the local route bearing, typically the opposite carriageway) are filtered out server-side. Corridors carry no bearing: they are enforced in both directions.
  • attribution lists the notices owed by the data actually returned. Display them verbatim. Empty when no cameras were returned.
  • source and licence identify where each record came from, so mixed responses stay auditable.

Corridor records

Corridor entries (average section control and mobile_site zones) add:

FieldTypeDescription
zone.start / zone.end[lat, lon]The corridor's published ends. The direction carries no enforcement meaning.
zone.length_kmnumberEnforced length. Published where the authority states one, otherwise measured along the geometry.
zone.geometry[[lon, lat], …]The corridor polyline, in GeoJSON coordinate order.
zone.geometry_parts[number, …]Vertex count of each source part. Split geometry on these boundaries when drawing; joining the parts would draw connecting segments that are not in the data.
from_arc_m / to_arc_mnumberWhere the corridor starts and ends along this route, in metres. Use the interval between them, with zone.length_km and limit_kph, to run an average-speed calculation client-side. to_arc_m can be the smaller of the two when the route runs against the corridor's published direction.

One corridor is one entry. Do not expect one entry per camera on a section-control stretch, and do not alert per device: that would warn the driver several times for a single enforcement event.

Jurisdiction policy

Camera-position warnings are illegal in some countries, so the gateway applies a per-country policy server-side, before the response is built, and reports what it did in policy:

modeBehaviour
exactExact camera positions are returned.
omittedThe cameras array is empty; only the policy object is returned.

The default is off: only countries on an explicit, legally reviewed allowlist (currently the United Kingdom and Ireland) return exact. Every other jurisdiction returns omitted, researched prohibitions (France, Germany, Switzerland, Austria and others) and unresearched countries alike. On routes that cross borders, each camera is also gated on its own country, so a prohibited neighbour's cameras never appear. Clients must not work around the policy with their own camera data.

Availability and billing

Bills as one Standard call, never Premium: it is a safety feature and carries no extra charge. On deployments without camera data the endpoint answers 501 (urn:sn-gateway:problem:cameras-not-enabled); treat that as "feature not available here" and disable camera alerts, rather than as an error. Self-hosters enable it by pointing SN_CAMERAS_DIR at the per-territory cameras.json sidecars the factory's safety-cameras stage produces.

Data sources and licences

Camera data is either OpenStreetMap-derived or published by the enforcing authority, and within one territory it is always one or the other, never a mixture. That separation is a licence requirement, not a preference: combining OSM and non-OSM records of the same feature type over the same area makes the whole dataset an ODbL derivative with share-alike.

TerritorySourceLicence
IrelandAn Garda Síochána mobile safety camera zones (1,916 corridors) plus the published fixed and average-speed cameras (11 devices). OSM camera nodes are excluded.CC BY 4.0 (Irish public sector information)
Everywhere elseOpenStreetMapODbL 1.0

The attribution array in every response carries the notices the returned data obliges. Display them verbatim.

Ireland publishes no speed limits. Every Irish record has limit_kph: null with limit_confidence: "unknown", and the API will not infer one from the road: Ireland re-based its default rural limit to 60 km/h in February 2025, so an inference tuned to the old defaults would be wrong across a large part of the network. Clients must not fill the gap with a guess of their own either.

Traffic incidents along a route (POST /v1/incidents/along)

Given a route shape, returns the closures and lane restrictions on it: where each one sits along the route, its cause and severity, and the lane/time detail the source publishes. Built for the same "warn the driver on approach" use case as /v1/cameras/along, over a different data source: the National Highways Road and Lane Closures feed.

Parameters

FieldTypeRequiredDescription
shapestringyesThe route geometry as a polyline6 string, exactly as POST /route responses emit their leg shape.
buffer_mnumbernoCorridor half-width in metres. Default 60; values above 500 are clamped to 500; zero, negative or non-numeric is a 400.

Example

sh
curl -fsS -X POST "$BASE/v1/incidents/along" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "shape": "}~ycbBhavgN...", "buffer_m": 60 }'

Response

json
{
  "incidents": [
    { "id": "7-1784655805-...-close", "arc_m": 0.0,
      "from_arc_m": 0.0, "to_arc_m": 1326.1, "offset_m": 0.0,
      "side": "unknown", "cause": "roadOrCarriagewayOrLaneManagement: laneClosures",
      "severity": "lane_restriction", "closure_type": "laneClosures",
      "road": "M1", "lanes_restricted": 1, "lanes_operational": 3,
      "status": "active", "planned": false, "probable": false,
      "overall_start": "2026-07-26T10:00:00Z", "overall_end": "2026-07-27T10:00:00Z",
      "source": "uk-national-highways" }
  ],
  "sources": [
    { "source_id": "uk-national-highways",
      "network": "England strategic road network (motorways and trunk roads managed by National Highways)",
      "coverage_note": "National Highways only. Local roads, and the separate Scotland/Wales trunk-road networks, are not covered.",
      "licence": "OGL-UK-3.0" }
  ],
  "attribution": ["Contains public sector information licensed under the Open Government Licence v3.0."]
}
  • incidents is ordered by arc_m. from_arc_m/to_arc_m bound the extent of the closure's geometry that actually fell inside the corridor; for a closure spanning a long stretch of motorway this can be several kilometres; for one that only clips the corridor briefly, from_arc_m and to_arc_m sit close together.
  • severity (closure | lane_restriction | other) is derived only from the feed's own closure-type and lane-count fields, never a guess when those are absent.
  • side (left | right | unknown) is geometric: which side of the route's own local bearing the incident sits on. The feed carries no carriageway-direction field, so this is not the same question as "does this affect my direction of travel".
  • sources is always present, even with zero matching incidents. An empty incidents array means "nothing on the covered network near this route", never "the road is clear". Read coverage_note before trusting silence: National Highways covers the English strategic road network only, not local roads and not the separate Scotland/Wales networks.

Availability and billing

Bills as one Standard call, never Premium, the same posture as /v1/cameras/along. On deployments without incident data the endpoint answers 501 (urn:sn-gateway:problem:incidents-not-enabled); treat that as "feature not available here", not an error. Self-hosters enable it by pointing SN_INCIDENTS_DIR at a directory of per-source sidecar files (see docs/API.md for the sidecar shape). Licence: OGL v3.0 (UK Open Government Licence); the attribution array carries the required notice verbatim.

Weather along a route (POST /v1/weather/along)

Samples a route and returns forecast weather for each point aligned to when you actually get there, not to now. A two-hour route departing at noon reads the forecast for the departure hour at its start and the "two hours from now" hour at its end; that time alignment is the whole point: "you hit the snow band near Shap at 14:00", not "it is snowing somewhere on this route right now".

Parameters

FieldTypeRequiredDescription
shapestringyesThe route geometry as a polyline6 string.
depart_atstringnoRFC 3339 departure time. Defaults to now.
duration_snumbernoTotal route duration in seconds, used to compute each sample's ETA. Omit for a generic average-speed estimate (flagged duration_source: "estimated_default_speed" in the response); pass the real value from /route for accurate alignment.
sample_interval_mnumbernoSampling interval in metres. Default 25,000; clamped 5,000–100,000; capped at 40 samples total regardless of route length.

Example

sh
curl -fsS -X POST "$BASE/v1/weather/along" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "shape": "}~ycbBhavgN...", "depart_at": "2026-07-26T12:00:00Z", "duration_s": 7200 }'

Response

json
{
  "samples": [
    { "lat": 54.4, "lon": -2.7, "arc_m": 180000.0,
      "eta": "2026-07-26T14:00:00Z", "forecast_hour": "2026-07-26T14:00",
      "available": true, "temperature_c": -1.2, "precipitation_mm": 0.6,
      "rain_mm": 0.0, "snowfall_cm": 0.8, "precipitation_type": "snow",
      "precipitation_probability_pct": 80.0,
      "wind_speed_kph": 32.0, "wind_gusts_kph": 58.0,
      "visibility_m": 3200.0, "weather_code": 71, "severe": false }
  ],
  "depart_at": "2026-07-26T12:00:00Z", "duration_s": 7200.0,
  "duration_source": "provided", "sample_interval_m": 25000.0,
  "attribution": "Weather data by Open-Meteo.com (CC BY 4.0)"
}
  • wind_gusts_kph is the figure that matters for high-sided vehicles.
  • severe is derived only from the source's WMO weather code (thunderstorm and heavy-precipitation codes), never a proprietary severe-weather product, and omitted (not false) when the sample itself is unavailable.
  • A sample whose upstream fetch failed, or whose ETA falls beyond the source's forecast horizon, comes back "available": false with an unavailable_reason; the request never fails outright because one sample couldn't be resolved.

Availability, billing and licence

Bills as one Standard call regardless of sample count. 501 (urn:sn-gateway:problem:weather-not-enabled) unless the operator sets SN_OPEN_METEO_URL. Read this before enabling commercially: Open-Meteo's free, keyless endpoint is non-commercial use only; a commercial deployment needs an Open-Meteo paid plan or a self-hosted instance (Open-Meteo is open source, AGPLv3). See THIRD-PARTY-NOTICES.md for the full licence position. The weather data itself is CC BY 4.0; every response carrying data includes an attribution string.

Errors

Errors are application/problem+json (see conventions for the envelope and the 402-vs-429 distinction). All eight endpoints share the same table:

Statustype URNMeaningRetry?
400urn:sn-gateway:problem:bad-requestMalformed JSON; empty locations, contours, sources or targets; a contour with neither time nor distance; a trace request with no trace; an along-route request with neither/both of route and shape, or an out-of-range corridor_m/max_detours; a cameras or incidents request with an undecodable shape or a non-positive buffer_m; a weather request with a non-RFC-3339 depart_at or a non-positive duration_s/sample_interval_m.No; fix the request.
404urn:sn-gateway:problem:not-found/route/along only: customer places are not configured on this deployment.No.
501urn:sn-gateway:problem:cameras-not-enabled / …:incidents-not-enabled / …:weather-not-enabledThe corresponding endpoint's data source is not configured on this deployment.No. Feature-detect and disable that feature.
401urn:sn-gateway:problem:unauthorizedMissing or invalid API key.No.
429urn:sn-gateway:problem:quota-exceeded or …:rate-limitedMonthly quota or rate limit; rate-limited bodies carry retry_after (seconds).After retry_after.
502urn:sn-gateway:problem:upstream-errorThe routing engine failed or returned garbage.Yes; transient.

Note that /v1/weather/along never returns a 502 for a per-sample upstream failure: it degrades that sample to "available": false inside a normal 200 response instead (see above).

Next steps

  • Route optimisation: the solver that consumes these matrices, with the same truck/ADR constraints
  • API reference: the full endpoint surface, including POST /route (whose response /trace_route shares) and the /places API that feeds along-route search
  • Conventions: base URL, auth, error envelope, ADR tunnel codes
  • MCP server: the matrix and search_along_route tools expose these capabilities to agents