{"info":{"title":"Kartpunkt API","version":"1.0.0","description":"Swedish addresses (Lantmäteriet's register): search/autocomplete, reverse geocoding, validation, change tracking and postnummer lookup; and the road network (Trafikverket's NVDB) as primitives: snap, roads with facts, routes, many-to-one distances, matrices, reach and GPS traces, on named profiles, with jobs for large inputs. Every answer names the network snapshot it was computed on.\n\nAuth: send your key as `Authorization: Bearer <key>` (or `x-api-key`), read from the KARTPUNKT_API_KEY environment variable.\n\nkp_test_ keys answer from a synthetic dataset (responses carry provenance.synthetic: true). kp_live_ keys answer from Lantmäteriet's address register (live data) on an account approved for live data, which needs an agreement, and 403 live_access_required on an account not approved. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.\n\nServer-side only: call the API from your backend, never from browser code (the key would leak, and CORS is not enabled).\n\nEvery successful response carries provenance: the sources it read, each with its licence, attribution text and date, and the network snapshot; display the attribution texts with the data. Errors are { error, message, param?, doc_url }; the message says what to do next, param names the field refused on invalid_request, and doc_url is the code's page.\n\nTry it at https://api.kartpunkt.se/docs: enter a kp_test_ key in the page's authentication field. It stays in your browser tab and is sent only to this API. Never paste a key into a chat with a coding agent.\n\nError codes: unauthorized (401), quota_exceeded (429), live_access_required (403), live_data_unavailable (503), invalid_request (400), method_not_allowed (405), not_found (404), address_closed (404), address_unknown (404), routing_unavailable (503), network_unavailable (404), network_not_active (409), network_pruned (410), snapshot_engine_unavailable (409), profile_unavailable (409), use_not_permitted (403), too_large (413), job_not_found (404), idempotency_conflict (409), unassigned_unreachable (422), unassigned_skills (422), unassigned_capacity (422), unassigned_time_window (422), unassigned_max_travel (422), unassigned_max_ride (422), unassigned_no_room (422), unassigned_address_id (422), unassigned_not_in_plan (422), plan_capacity (422), plan_time_window (422), plan_vehicle_time_window (422), plan_skills (422), plan_max_travel (422), plan_shipment_order (422), plan_max_ride (422), internal_error (500)."},"tags":[{"name":"registries","description":"What the API answers on: profiles, facts, sources and their terms, vector tiles, and the network snapshots."},{"name":"addresses","description":"Swedish addresses from Lantmäteriet's register: search, reverse geocoding, validation, change tracking and postnummer."},{"name":"roads","description":"The road network (NVDB) as roads: where points meet it, roads near a point and a road by its reference, with facts."},{"name":"routing","description":"Paths along the network: a route, distances, nearest points, matrices, reachable areas and GPS traces."},{"name":"planning","description":"Vehicles' routes over stops and shipments: a plan optimized, or a given plan checked."},{"name":"jobs","description":"Any routing or planning call as a job, for inputs above the synchronous limits."}],"externalDocs":{"url":"https://kartpunkt.se/llms.txt","description":"llms.txt: the rules for agents, the recipes and this API's reference in Markdown."},"servers":[{"url":"https://api.kartpunkt.se","description":"Production (EU, Hetzner)"},{"url":"http://127.0.0.1:8788","description":"Local development"}],"components":{"schemas":{"ErrorBody":{"type":"object","description":"Every error response: a stable code, what to do next, the field refused, and the code's page. Every code has a page at https://api.kartpunkt.se/errors/{error}, listed at https://api.kartpunkt.se/errors.","externalDocs":{"url":"https://api.kartpunkt.se/errors","description":"Every error code, a page each."},"required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized","quota_exceeded","live_access_required","live_data_unavailable","invalid_request","method_not_allowed","not_found","address_closed","address_unknown","routing_unavailable","network_unavailable","network_not_active","network_pruned","snapshot_engine_unavailable","profile_unavailable","use_not_permitted","too_large","job_not_found","idempotency_conflict","unassigned_unreachable","unassigned_skills","unassigned_capacity","unassigned_time_window","unassigned_max_travel","unassigned_max_ride","unassigned_no_room","unassigned_address_id","unassigned_not_in_plan","plan_capacity","plan_time_window","plan_vehicle_time_window","plan_skills","plan_max_travel","plan_shipment_order","plan_max_ride","internal_error"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"What went wrong and what to do next, written for the caller (often an AI agent)."},"param":{"type":"string","description":"On invalid_request: the input field refused, by its path in the body (`origins.0.point.lat`, `prefer`). Absent on every other code."},"doc_url":{"type":"string","format":"uri","description":"The code's page: what it means and what to do (https://api.kartpunkt.se/errors/{error})."}}},"Profile":{"type":"string","enum":["walk","car_shortest","car_fastest","hgv","bike"],"description":"A routing profile: who travels, which roads they may use and how a path is weighed. GET /v1/profiles lists them."},"Provenance":{"type":"object","properties":{"synthetic":{"type":"boolean","description":"true on every answer to a kp_test_ key: its addresses are synthetic, and its road network is NVDB's real one. false on a kp_live_ key's answers."},"network":{"description":"The network snapshot the answer was computed on. Left out where the answer read no network, as on GET /v1/profiles.","type":"object","properties":{"id":{"type":"string","description":"The snapshot's id. Send it as network.id to answer on the same network while it is served."},"dataset":{"type":"string","const":"nvdb","description":"The dataset the network is built from: always 'nvdb'."},"source_date":{"description":"The NVDB delivery date (YYYY-MM-DD) the snapshot was built from.","type":["string","null"]},"built_at":{"type":"string","description":"When the snapshot was built (RFC 3339)."},"graph":{"description":"The routing graph's snapshot id where the answer used one; null where it did not.","type":["string","null"]},"profile":{"$ref":"#/components/schemas/Profile"},"experimental":{"description":"true when the profile the answer was computed on is not yet verified on the national network, as bike since 2026-10-15. Present with profile, which is left out where the answer used none.","type":"boolean"}},"required":["id","dataset","source_date","built_at","graph"],"additionalProperties":false},"sources":{"type":"array","items":{"$ref":"#/components/schemas/SourceRef"},"description":"Every source the answer read, each with its licence and the attribution to show."}},"required":["synthetic","sources"],"additionalProperties":false,"description":"What an answer was computed from: the network snapshot, the sources read, and whether its data is synthetic. Store it with the answer. Show every sources[].attribution wherever the data is shown (ADR-0016)."},"SourceRef":{"type":"object","properties":{"id":{"type":"string","description":"The source: 'nvdb', 'lantmateriet' or 'synthetic', as GET /v1/sources lists them."},"name":{"type":"string","description":"The source's name."},"licence":{"type":"string","description":"The source's licence, as an SPDX id."},"attribution":{"type":"string","description":"The credit to show wherever the data is shown or passed on. The X-Data-Attribution header holds an ASCII copy; show this text."},"url":{"type":"string","description":"The licence's URL."},"updated":{"description":"The date (YYYY-MM-DD) of the source delivery the answer rests on; null where it is not known.","type":["string","null"]},"as_of":{"description":"The instant (RFC 3339) the address ids were read at, where an answer resolved them. On optimize and check, it is when an id without its own as_of was resolved; for a job, when it was submitted. Send it as as_of to answer on the same addresses again.","type":"string"}},"required":["id","name","licence","attribution","url","updated"],"additionalProperties":false,"description":"A data source an answer read, with how to credit it."},"Address":{"type":"object","properties":{"id":{"type":"string","description":"Stable id: 'se.lm.' and Lantmäteriet's own object identity of the address (belägenhetsadress), so it is the register's, not Kartpunkt's; 'se.syn.' and a code for a kp_test_ key. Store it with the label; it survives weekly refreshes. Street-level hits have ids starting 'street:'."},"type":{"type":"string","enum":["address","street"],"description":"'address' for an address point; 'street' for a street-level hit, answered while no house number is typed."},"label":{"type":"string","description":"Display label, e.g. 'Östra Ågatan 12B, 753 31 Uppsala'."},"street":{"description":"Street name or address area (gatunamn / adressområde); null when it has none.","type":["string","null"]},"number":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"House number (adressplatsnummer); null on street-level hits."},"letter":{"description":"Letter after the number (bokstavstillägg), e.g. 'B'; null when there is none.","type":["string","null"]},"postcode":{"description":"Postnummer, five digits without space; null when it has none.","type":["string","null"]},"locality":{"description":"Postort; null when it has none.","type":["string","null"]},"municipality_code":{"description":"Kommunkod, four digits, e.g. '0380' for Uppsala; null when it has none.","type":["string","null"]},"municipality":{"description":"Kommun name; null when it has none.","type":["string","null"]},"county_code":{"description":"Länskod, two digits as the register delivers it (e.g. '03' for Uppsala län); null when the address has none.","type":["string","null"]},"county":{"description":"Län name as SCB writes it, e.g. 'Uppsala län'; null when county_code is null or is not one of Sweden's 21 län codes.","type":["string","null"]},"district_code":{"description":"Distriktskod, six digits as the register delivers it, e.g. '215134'; null when the address has none. A street-level hit has one only when every address on the street lies in the same distrikt.","type":["string","null"]},"district":{"description":"Distrikt name as the register delivers it, the folkbokföring district since 2016; null when the address has none. A street-level hit has one only when district_code does.","type":["string","null"]},"point":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude, WGS84 decimal degrees, -90 to 90.","examples":[59.3293]},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude, WGS84 decimal degrees, -180 to 180.","examples":[18.0686]}},"required":["lat","lon"],"additionalProperties":false,"description":"Where the address point lies, WGS84. For street-level hits, the street's centre."},"position_type":{"anyOf":[{"type":"string","enum":["building","entrance","driveway","plot","approximate","other"]},{"type":"null"}],"description":"What the address point marks: 'building', 'entrance', 'driveway', 'plot' (the property), 'approximate' or 'other'. null when the source does not say, and always on street-level hits. An 'approximate' point was placed only roughly, not to the metre. It describes the point only and never changes ranking or validation."},"match_type":{"anyOf":[{"type":"string","enum":["exact","corrected","partial"]},{"type":"null"}],"description":"How the address matches the words sent. 'exact': everything sent is in the address, ignoring case, spacing, punctuation, diacritics, known abbreviations and parts not sent. 'corrected': a word was read as misspelt. 'partial': something sent, such as a postort, is not the address's. null on reverse. It never changes ranking, score, valid or issues; read an unknown value as 'partial'."},"score":{"type":"number","description":"Relevance score; only meaningful for ordering within one response."},"distance_m":{"description":"Straight-line metres (spheroidal) from the point sent. Present on reverse, and on search when near is sent.","type":"number"}},"required":["id","type","label","street","number","letter","postcode","locality","municipality_code","municipality","county_code","county","district_code","district","point","position_type","match_type","score"],"additionalProperties":false,"description":"An address point or a street-level hit."},"Point":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude, WGS84 decimal degrees.","examples":[59.3293]},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude, WGS84 decimal degrees.","examples":[18.0686]}},"required":["lat","lon"],"additionalProperties":false,"description":"A WGS84 coordinate."},"AddressChange":{"type":"object","properties":{"id":{"type":"string","description":"The id as sent."},"status":{"type":"string","enum":["unchanged","changed","closed","unknown"],"description":"'unchanged': served now, every field as it was at `since`. 'changed': served now under the same id, with a field that differs. 'closed': not served now, since a delivery removed or reserved it; the same id can be served again. 'unknown': no version of this id in this key's dataset. See the recipe \"Keeping stored addresses current\"."},"changed_on":{"description":"Only on 'changed': the date (YYYY-MM-DD) of the delivery that made the address's current version.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},"changed_fields":{"description":"Only on 'changed': the names of the address fields whose values differ from what was served at `since`, never their old values.","type":"array","items":{"type":"string","enum":["label","street","number","letter","postcode","locality","municipality_code","municipality","county_code","county","district_code","district","point","position_type"]}},"address":{"description":"Only on 'changed': the address as served now, under the same id. Its score is 1 and its match_type null, since no words were sent.","$ref":"#/components/schemas/Address"},"closed_on":{"description":"Only on 'closed': the date (YYYY-MM-DD) of the delivery that stopped serving the address.","type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"}},"required":["id","status"],"additionalProperties":false,"description":"What happened to one id sent, comparing what was served at `since` with what is served now."},"StreetPoint":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude, WGS84 decimal degrees.","examples":[59.3293]},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude, WGS84 decimal degrees.","examples":[18.0686]},"street":{"description":"The street the point lies on, e.g. 'Storgatan'. The point then meets the nearest road of that name within 500 m, else the nearest road of the network; street_matched in the answer says which. It is not stored.","type":"string","minLength":1,"maxLength":200}},"required":["lat","lon"],"additionalProperties":false,"description":"A WGS84 coordinate, with the street it lies on when known."},"NetworkSelector":{"type":"object","properties":{"id":{"description":"A snapshot's id, from GET /v1/networks or an earlier answer's provenance.network.id.","type":"string","pattern":"^snap_[a-z0-9]+$"},"as_of":{"description":"An instant (RFC 3339): the snapshot that was served then.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"additionalProperties":false,"description":"The network snapshot to answer on, by id or as_of, one of the two. Leave it out for the snapshot served now. Another snapshot's answers are computed as jobs (POST /v1/jobs), only while its graph runs on the current engine."},"RoadRef":{"type":"string","pattern":"^[^@\\s/]{1,64}@[01]\\.\\d{6}-[01]\\.\\d{6}$","examples":["1000123:45@0.250000-0.500000"]},"Vehicle":{"type":"object","properties":{"weight_t":{"description":"Gross weight, tonnes.","type":"number","exclusiveMinimum":0,"maximum":100},"axle_load_t":{"description":"Heaviest axle load, tonnes.","type":"number","exclusiveMinimum":0,"maximum":30},"height_m":{"description":"Height, metres.","type":"number","exclusiveMinimum":0,"maximum":6},"width_m":{"description":"Width, metres.","type":"number","exclusiveMinimum":0,"maximum":4},"length_m":{"description":"Length, metres.","type":"number","exclusiveMinimum":0,"maximum":40},"hazmat":{"description":"Carries dangerous goods (farligt gods).","type":"boolean"},"kind":{"description":"What the vehicle is, for the bans NVDB records per kind: 'lorry' (lastbil, tung lastbil) or 'bus' (buss). Left out, a lorry.","type":"string","enum":["lorry","bus"]},"bearing_class":{"description":"The bearing class (bärighetsklass) the vehicle needs: a road whose class carries less is kept off. Classes order by capacity, BK3 < BK2 < BK1 < BK4, not by number. Left out, bearing class is reported, not routed.","type":"string","enum":["BK1","BK2","BK3","BK4"]}},"additionalProperties":false,"description":"The vehicle's weight, dimensions and kind, on a profile whose `takes_vehicle` is true in GET /v1/profiles (hgv). A road whose recorded limit is below the vehicle's is not used. On any other profile it is refused 400 invalid_request, never ignored."},"Avoid":{"type":"object","properties":{"stretches":{"description":"Lines to keep off, at most 200 km together: a closure, roadworks, or a road you classify as unsuitable. Each is matched to the car network's roads along it, piece by piece; the answer's avoid.stretches says which matched. Keep the lists in your own system and send them with each call; nothing is stored.","maxItems":100,"type":"array","items":{"type":"object","properties":{"line":{"type":"object","properties":{"type":{"type":"string","const":"LineString","description":"Always 'LineString' (GeoJSON)."},"coordinates":{"minItems":2,"maxItems":1000,"type":"array","items":{"type":"array","prefixItems":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}],"items":false,"minItems":2,"maxItems":2},"description":"The line's positions, 2 to 1000, each [lon, lat] in WGS84 (GeoJSON order: longitude first, -180 to 180; latitude -90 to 90)."}},"required":["type","coordinates"],"additionalProperties":false,"description":"The stretch as a GeoJSON LineString in WGS84, drawn along the road it classifies. A street the line only crosses, a line drawn across a road and a separate walk or cycle path beside it are not matched."},"buffer_m":{"description":"How far from the line a road may lie and still be matched, in metres. A piece of road runs between junctions, or to where its speed limit, name or class changes. A piece is matched when 90 % of it lies within buffer_m of the line, or 90 % of the line lies along it, over more than twice buffer_m.","type":"number","minimum":1,"maximum":50},"mode":{"type":"string","enum":["along","barrier"],"description":"'along', the default: kept off travelling along the roads matched. 'barrier': also kept from crossing them at grade, at a junction on them or a passage in plan NVDB records across them, such as an övergångsställe or a cykelpassage. A bridge or tunnel stays open. A barrier holds within 2 km of the ends, wider where the path goes further."},"direction":{"description":"Which way travel on the roads matched is kept off: 'both', the default; 'along', the way the line is drawn, from its first position to its last; or 'against', the other way. A road matched one way stays open the other. With mode 'along' only: a barrier keeps off both ways.","type":"string","enum":["both","along","against"]},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["line"],"additionalProperties":false,"description":"One stretch to keep off: { line, buffer_m, mode, direction, id }."}},"facts":{"description":"Facts to keep off, each once, by their key in GET /v1/facts: the flags 'fast_road', 'unlit_outside_built_up', 'ferry', 'motorway', 'built_up', 'lit'; the ranges 'speed_limit', 'road_class', 'traffic_safety_class'; the categories 'surface', 'road_owner'.","maxItems":11,"type":"array","items":{"$ref":"#/components/schemas/AvoidFact"}},"values":{"description":"Roads to keep off by a fact's value, without drawing a line: every road of the car network with one of those numbers within 2 km of the ends, wider where the path goes further. With between, only that road between two points.","maxItems":10,"type":"array","items":{"$ref":"#/components/schemas/AvoidValue"}}},"additionalProperties":false,"description":"What the path keeps off, on any profile; left out or empty, nothing. An end on a road a barrier keeps off meets the network on its own side, unless within 1 m of it: the nearest road within 500 m a straight line reaches without crossing the barrier. See \"Keeping a path off roads: avoid, barriers and alternatives\"."},"AvoidFact":{"oneOf":[{"type":"object","properties":{"fact":{"type":"string","enum":["fast_road","unlit_outside_built_up","ferry","motorway","built_up","lit"]},"mode":{"type":"string","enum":["along","barrier"],"description":"'along', the default: kept off travelling along the roads matched. 'barrier': also kept from crossing them at grade, at a junction on them or a passage in plan NVDB records across them, such as an övergångsställe or a cykelpassage. A bridge or tunnel stays open. A barrier holds within 2 km of the ends, wider where the path goes further."},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["fact"],"additionalProperties":false},{"type":"object","properties":{"fact":{"type":"string","const":"speed_limit"},"min":{"description":"The lowest value kept off, inclusive: km/h as posted; gångfart and a road with no limit recorded are never in a range.","type":"integer","minimum":5,"maximum":150},"max":{"description":"The highest value kept off, inclusive: km/h as posted; gångfart and a road with no limit recorded are never in a range.","type":"integer","minimum":5,"maximum":150},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["fact"],"additionalProperties":false},{"type":"object","properties":{"fact":{"type":"string","const":"road_class"},"min":{"description":"The lowest value kept off, inclusive: functional class, 0 (most important) to 9.","type":"integer","minimum":0,"maximum":9},"max":{"description":"The highest value kept off, inclusive: functional class, 0 (most important) to 9.","type":"integer","minimum":0,"maximum":9},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["fact"],"additionalProperties":false},{"type":"object","properties":{"fact":{"type":"string","const":"traffic_safety_class"},"min":{"description":"The lowest value kept off, inclusive: TS-klass, 1 (mycket god) to 4 (låg).","type":"integer","minimum":1,"maximum":4},"max":{"description":"The highest value kept off, inclusive: TS-klass, 1 (mycket god) to 4 (låg).","type":"integer","minimum":1,"maximum":4},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["fact"],"additionalProperties":false},{"type":"object","properties":{"fact":{"type":"string","const":"surface"},"values":{"minItems":1,"maxItems":3,"type":"array","items":{"type":"string","enum":["paved","gravel","unknown"]},"description":"The values kept off, any of 'paved', 'gravel', 'unknown' ('unknown': where NVDB records none)."},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["fact","values"],"additionalProperties":false},{"type":"object","properties":{"fact":{"type":"string","const":"road_owner"},"values":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"string","enum":["state","municipal","private","unknown"]},"description":"The values kept off, any of 'state', 'municipal', 'private', 'unknown' ('unknown': where NVDB records none)."},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["fact","values"],"additionalProperties":false}],"description":"A fact to keep off, by its key among those GET /v1/facts marks avoidable. A flag is { fact: 'fast_road' }, with mode 'barrier' to keep from crossing those roads at grade too. A range is { fact: 'speed_limit', min: 70 }, inclusive, with min, max or both. A category is { fact: 'surface', values: ['gravel'] }."},"AvoidValue":{"type":"object","properties":{"fact":{"type":"string","enum":["road_number"],"description":"A category fact from GET /v1/facts whose roads can be kept off by their value."},"values":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":16},"description":"The values to keep off: road numbers as signed, e.g. 'E20' or '47'. Case and spaces are ignored."},"between":{"description":"Two points on the road: only the road between them is kept off. Each meets the road within 200 m. Kept off is the shortest connected run between them in whole pieces, with the road's pieces beside it within 50 m. One value only, the points at most 30 km apart. Left out, the whole road near the ends.","minItems":2,"maxItems":2,"type":"array","items":{"$ref":"#/components/schemas/Point"}},"mode":{"type":"string","enum":["along","barrier"],"description":"'along', the default: kept off travelling along the roads matched. 'barrier': also kept from crossing them at grade, at a junction on them or a passage in plan NVDB records across them, such as an övergångsställe or a cykelpassage. A bridge or tunnel stays open. A barrier holds within 2 km of the ends, wider where the path goes further."},"id":{"description":"Your own id for it, echoed in blocked_by.","type":"string","minLength":1,"maxLength":64}},"required":["fact","values"],"additionalProperties":false,"description":"A road kept off by its value, with no line drawn: { fact: 'road_number', values: ['E20', '47'], mode: 'barrier' }, or only between two points on it: { fact: 'road_number', values: ['113'], between: [{ lat, lon }, { lat, lon }] }."},"Preference":{"type":"object","properties":{"fact":{"type":"string","enum":["fast_road","unlit_outside_built_up","ferry","motorway","built_up","lit"],"description":"A flag fact from GET /v1/facts that a path can be kept off: every road or path where the fact holds."},"cost":{"default":2,"description":"What a metre with the fact costs against 1 for every other metre: more than 1, at most 10 (default 2).","type":"number","exclusiveMinimum":1,"maximum":10}},"required":["fact"],"additionalProperties":false,"description":"A fact to prefer to keep off: { fact: 'fast_road', cost: 2 }. The path whose cost is least is answered, which can be longer than the shortest."},"FactSummary":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},{"type":"object","properties":{"min":{"type":["number","null"]},"max":{"type":["number","null"]},"unknown_m":{"type":"number"}},"required":["min","max","unknown_m"],"additionalProperties":false},{"type":"number"}],"description":"A fact summed over a path: metres per value for a category, {min, max, unknown_m} for a measure, metres for a flag, or a count."},"SnapResult":{"type":"object","properties":{"snap_m":{"description":"Straight-line metres from the point sent to where it meets the profile's network; not included in distance_m. null when no road of the network lies within 500 m: the status is then no_road_near.","type":["number","null"]},"street_matched":{"description":"true when the point met a road of the street sent, false when it met the nearest road instead. null when no street was sent or no road is near.","type":["boolean","null"]}},"required":["snap_m","street_matched"],"additionalProperties":false,"description":"Where a point sent met the network."},"Segment":{"type":"object","properties":{"length_m":{"type":"number","description":"Metres of the path on this stretch."},"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The stretch of road, the same in every snapshot; null on a road with no NVDB link. The synthetic dataset has links of its own."},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Each requested fact's value on this stretch."},"geometry":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},"destination_only":{"description":"true on a stretch closed to the vehicle's through traffic, driven only because an end or a via lies in that closed zone: a first or last stretch. Closed means genomfart förbjuden, or a ban that exempts the vehicle's own errand, such as deliveries for a lorry or passengers set down for a car or a bus. Left out elsewhere.","type":"boolean","const":true}},"required":["length_m","ref","facts"],"additionalProperties":false,"description":"A stretch of a path: its metres, its road reference, the facts asked for and its line."},"Path":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by"],"additionalProperties":false,"description":"A path between two ends, and why there is none where there is none."},"TracePoint":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude, WGS84 decimal degrees.","examples":[59.3293]},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude, WGS84 decimal degrees.","examples":[18.0686]},"time":{"description":"When the fix was recorded (RFC 3339 with an offset).","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["lat","lon"],"additionalProperties":false,"description":"A GPS fix: a WGS84 coordinate, with its time when known."},"AddressIdPlace":{"type":"object","properties":{"address_id":{"type":"string","maxLength":100,"pattern":"^se\\.(lm|syn)\\.[A-Za-z0-9-]{1,80}$","description":"An address id from the register, as an earlier answer's id gave it (POST /v1/addresses/validate, GET /v1/geocode): 'se.lm.' and Lantmäteriet's object identity for a kp_live_ key, 'se.syn.' and a code for a kp_test_ key. Resolved to the address's point and street to meet the network; the answer never holds them."},"as_of":{"description":"Resolve the version of the address served at this instant (RFC 3339 with offset), e.g. the date your plan was made, so the same plan answers the same again; left out, the version served now. Versions are kept 24 months.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["address_id"],"additionalProperties":false},"Unreachable":{"type":"object","properties":{"place":{"type":"string","enum":["stop","pickup","delivery"],"description":"Which of the task's places is out of reach: the stop's, or the shipment's pickup or delivery."},"met":{"anyOf":[{"type":"object","properties":{"point":{"description":"Where the point meets the network: the nearest point of the road.","$ref":"#/components/schemas/Point"},"snap_m":{"type":"number","description":"Straight-line metres from the point sent to where it meets the network; never part of a distance_m."},"street_matched":{"description":"true when the point met a road of the street sent, false when it met the nearest road instead. null when no street was sent or no road is near.","type":["boolean","null"]},"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The road's reference at that point, its two measures equal; null on a road with no NVDB link."},"road":{"type":"object","properties":{"name":{"description":"The road's name in NVDB; null where it has none.","type":["string","null"]},"number":{"description":"The road's number (vägnummer): 'E6' for a europaväg, else digits; null where it has none.","type":["string","null"]}},"required":["name","number"],"additionalProperties":false,"description":"The road the point meets."}},"required":["point","snap_m","street_matched","ref","road"],"additionalProperties":false},{"type":"null"}],"description":"Where the place met the network, as POST /v1/snap answers it, with the road's RoadRef. With engine_not_met, the road the engine would not use; with avoid, the kept-off road the place lies on, if any. null with no_road_near."},"cause":{"type":"string","enum":["no_road_near","engine_not_met","no_path","avoid"],"description":"'no_road_near': no road of the profile within 500 m. 'engine_not_met': met on the road in met, which the engine does not use for this vehicle; Kartpunkt's error. 'no_path': met, but no path joins it to a vehicle's start, or in a checked plan to a neighbouring visit. 'avoid': only roads avoid keeps off reach it, or it lies on one."}},"required":["place","met","cause"],"additionalProperties":false,"description":"Why a task no vehicle takes is out of reach, and where its place met the network."},"Unresolved":{"type":"object","properties":{"place":{"type":"string","enum":["stop","pickup","delivery"],"description":"Which of the task's places: the stop's, or the shipment's pickup or delivery (the first that failed)."},"cause":{"type":"string","enum":["closed","unknown"],"description":"'closed': a delivery of the register closed the address on or before the instant asked (as_of, else now). 'unknown': no version of the id in this key's dataset then (never issued, past the 24 months kept, opened only later, or the other dataset's)."}},"required":["place","cause"],"additionalProperties":false},"Job":{"oneOf":[{"type":"object","properties":{"operation":{"type":"string","const":"route"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"via":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/SnapResult"}},{"type":"null"}],"description":"Where each via point met the network, in order; null when none was sent."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"avoid":{"anyOf":[{"type":"object","properties":{"stretches":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"matched":{"type":"boolean","description":"Whether the stretch matched a road. false: none lies along it (drawn where NVDB no longer has a road): redraw it."}},"required":["matched"],"additionalProperties":false}},{"type":"null"}],"description":"Per avoid.stretches entry, in order; null when none was sent."},"values":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"between":{"type":"array","items":{"type":"object","properties":{"point":{"description":"Where the point met the road: the nearest point of a piece with the value.","$ref":"#/components/schemas/Point"},"snap_m":{"type":"number","description":"Straight-line metres from the point sent to there."}},"required":["point","snap_m"],"additionalProperties":false},"description":"Where each of the two points met the road, in the order sent."},"roads":{"type":"array","items":{"type":"object","properties":{"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The stretch kept off: an NVDB link and the part of it, measures ascending; null on a road with no NVDB link."},"length_m":{"type":"number","description":"Metres of the stretch."}},"required":["ref","length_m"],"additionalProperties":false},"description":"The road kept off between the points, from the first point's end of the run to the second's: the run's pieces and those beside it, each whole."},"length_m":{"type":"number","description":"Metres of road kept off, all of roads together."}},"required":["between","roads","length_m"],"additionalProperties":false,"description":"What an avoid.values entry with between matched."},{"type":"null"}]}},{"type":"null"}],"description":"Per avoid.values entry, in order: with between, the road it matched, to check against the closure's wording; null for an entry without between. null when no avoid.values was sent."}},"required":["stretches","values"],"additionalProperties":false},{"type":"null"}],"description":"What avoid matched; null without avoid."},"alternatives":{"anyOf":[{"type":"object","properties":{"unrestricted":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","segments"],"additionalProperties":false}},"additionalProperties":false},{"type":"null"}],"description":"The alternatives asked for, by label: unrestricted, the route without avoid with its own segments. null when none was."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","via","segments","avoid","alternatives","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"distances"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"avoid":{"anyOf":[{"type":"object","properties":{"stretches":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"matched":{"type":"boolean","description":"Whether the stretch matched a road. false: none lies along it (drawn where NVDB no longer has a road): redraw it."}},"required":["matched"],"additionalProperties":false}},{"type":"null"}],"description":"Per avoid.stretches entry, in order; null when none was sent."},"values":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"between":{"type":"array","items":{"type":"object","properties":{"point":{"description":"Where the point met the road: the nearest point of a piece with the value.","$ref":"#/components/schemas/Point"},"snap_m":{"type":"number","description":"Straight-line metres from the point sent to there."}},"required":["point","snap_m"],"additionalProperties":false},"description":"Where each of the two points met the road, in the order sent."},"roads":{"type":"array","items":{"type":"object","properties":{"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The stretch kept off: an NVDB link and the part of it, measures ascending; null on a road with no NVDB link."},"length_m":{"type":"number","description":"Metres of the stretch."}},"required":["ref","length_m"],"additionalProperties":false},"description":"The road kept off between the points, from the first point's end of the run to the second's: the run's pieces and those beside it, each whole."},"length_m":{"type":"number","description":"Metres of road kept off, all of roads together."}},"required":["between","roads","length_m"],"additionalProperties":false,"description":"What an avoid.values entry with between matched."},{"type":"null"}]}},{"type":"null"}],"description":"Per avoid.values entry, in order: with between, the road it matched, to check against the closure's wording; null for an entry without between. null when no avoid.values was sent."}},"required":["stretches","values"],"additionalProperties":false},{"type":"null"}],"description":"What avoid matched; null without avoid."},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"description":"The id sent with it, or null.","type":["string","null"]},"snap_m":{"description":"Straight-line metres from the point sent to where it meets the profile's network; not included in distance_m. null when no road of the network lies within 500 m: the status is then no_road_near.","type":["number","null"]},"street_matched":{"description":"true when the point met a road of the street sent, false when it met the nearest road instead. null when no street was sent or no road is near.","type":["boolean","null"]}},"required":["id","snap_m","street_matched"],"additionalProperties":false},"description":"Where each destination met the network, in the order sent, ignoring avoid. A destination with snap_m null answers every origin no_road_near."},"results":{"type":"array","items":{"type":"object","properties":{"id":{"description":"The id sent with this result's origin, or null.","type":["string","null"]},"destination_id":{"description":"The id sent with this result's destination, or null.","type":["string","null"]},"destination_index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"This result's destination, its index in destinations from 0."},"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"alternatives":{"anyOf":[{"type":"object","properties":{"unrestricted":{"$ref":"#/components/schemas/Path"}},"additionalProperties":false},{"type":"null"}],"description":"The alternatives answered, by label: unrestricted, the path without avoid (the shortest, or with prefer the least costly, of all). null when none was."}},"required":["id","destination_id","destination_index","status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","alternatives"],"additionalProperties":false}},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["avoid","destinations","results","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"matrix"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"distances_m":{"type":"array","items":{"type":"array","items":{"type":["number","null"]}},"description":"[origin][destination]: metres along the network, null where no path joins them."},"durations_s":{"type":"array","items":{"type":"array","items":{"type":["number","null"]}},"description":"[origin][destination]: seconds at free flow, null where no path joins them."},"statuses":{"type":"array","items":{"type":"array","items":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."}},"description":"[origin][destination]: each cell's status, as a path's."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["distances_m","durations_s","statuses","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"reach"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"areas":{"type":"array","items":{"type":"object","properties":{"duration_s":{"description":"This area's budget in seconds when the budget was duration_s; else null.","type":["number","null"]},"distance_m":{"description":"This area's budget in metres when the budget was distance_m; else null.","type":["number","null"]},"geometry":{"type":"object","properties":{"type":{"type":"string","const":"Polygon"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"number"}}}}},"required":["type","coordinates"],"additionalProperties":false}},"required":["duration_s","distance_m","geometry"],"additionalProperties":false}},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["areas","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"trace"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["ok","not_matched"],"description":"'ok' when the trace was matched to the network; 'not_matched' when no path along it was found."},"distance_m":{"description":"Metres along the network of the path matched; null when not matched.","type":["number","null"]},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path matched."},"geometry":{"description":"The whole matched path as one line, in the format asked; null when not matched. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["status","distance_m","segments","facts","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"nearest"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"]},"origin":{"description":"Where the point met the profile's network; snap_m null when it met none, and nearest is then [].","$ref":"#/components/schemas/SnapResult"},"nearest":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["bus_stop"],"description":"What to find: 'bus_stop', NVDB's bus stops (hållplatser) of the network snapshot."},"id":{"type":"string","description":"Its id in the network snapshot."},"name":{"type":["string","null"]},"point":{"description":"Its position.","$ref":"#/components/schemas/Point"},"distance_m":{"type":"number","description":"Metres along the network by the profile, between where the point and it meet the network."},"duration_s":{"description":"Free-flow seconds of that route by the profile.","type":["number","null"]}},"required":["kind","id","name","point","distance_m","duration_s"],"additionalProperties":false},"description":"Nearest first; [] when none is within max_distance_m or the point met no road."}},"required":["id","origin","nearest"],"additionalProperties":false}},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["results","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"optimize"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"routes":{"type":"array","items":{"type":"object","properties":{"vehicle_id":{"type":"string","description":"The vehicle's id, as sent."},"visits":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["start","stop","pickup","delivery","end"],"description":"'start' and 'end' are the vehicle's own; 'stop' a stop; 'pickup' and 'delivery' a shipment's two places."},"id":{"description":"The stop's or shipment's id; null at the vehicle's start and end.","type":["string","null"]},"snap_m":{"description":"Straight-line metres from the point sent to where it meets the profile's network; not included in distance_m. null when no road of the network lies within 500 m: the status is then no_road_near.","type":["number","null"]},"street_matched":{"description":"Whether the place met the road of the street sent; null without a street, and at a place sent as an address id (the answer holds nothing of the address).","type":["boolean","null"]},"arrival":{"description":"RFC 3339, in the offset of the vehicle's time_window; null without one.","type":["string","null"]},"departure":{"description":"RFC 3339: arrival, any wait and service_s later; null without a time_window.","type":["string","null"]},"arrival_s":{"type":"number","description":"Seconds from leaving start to arriving here."},"waiting_s":{"type":"number","description":"Seconds waited here for a time window to open."},"service_s":{"type":"number","description":"Seconds of service here, as sent."},"load":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"On board when leaving here, per dimension named in the request."},"distance_m":{"type":"number","description":"Metres along the network from the previous visit (0 at start)."},"duration_s":{"type":"number","description":"Seconds driving from the previous visit at free flow (0 at start)."}},"required":["kind","id","snap_m","street_matched","arrival","departure","arrival_s","waiting_s","service_s","load","distance_m","duration_s"],"additionalProperties":false},"description":"In the order driven: start, each task, end (where one was sent)."},"legs":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"},"description":"The leg's stretches with their road references (ADR-0013), in order; with geometry, each with its line."}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","segments"],"additionalProperties":false}},{"type":"null"}],"description":"With facts or geometry, one leg from each visit to the next, in order: visits.length − 1 of them. null without either."},"distance_m":{"type":"number","description":"Metres along the network over the whole route."},"duration_s":{"type":"number","description":"Seconds from leaving start to the last visit: driving, waiting and service."},"waiting_s":{"type":"number","description":"Seconds waited for time windows to open, over the route."},"service_s":{"type":"number","description":"Seconds of service, over the route."}},"required":["vehicle_id","visits","legs","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false},"description":"One per vehicle used, in the order of vehicles sent; a vehicle given no task is left out."},"unassigned":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["stop","shipment"],"description":"Whether id names a stop or a shipment."},"id":{"type":"string","description":"The stop's or shipment's id, as sent."},"reason":{"type":"string","enum":["unassigned_address_id","unassigned_unreachable","unassigned_skills","unassigned_capacity","unassigned_time_window","unassigned_max_travel","unassigned_max_ride","unassigned_no_room"],"description":"Why, as a code from ERRORS: its page at /errors/{code} says what to change."},"message":{"type":"string","description":"The code's message from ERRORS, saying what to change; with unassigned_unreachable, the message for its cause."},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With unassigned_unreachable where only roads kept off by avoid reach the task: what in avoid is in the way, as route's blocked_by. null otherwise."},"unreachable":{"description":"With unassigned_unreachable: which place, where it met the network and why no vehicle reaches it. Absent with every other reason. met is null for a place sent as an address id: the answer holds no point of the address.","$ref":"#/components/schemas/Unreachable"},"unresolved":{"description":"With unassigned_address_id: which place's address id resolved to no address served, and why. Absent with every other reason.","$ref":"#/components/schemas/Unresolved"}},"required":["kind","id","reason","message","blocked_by"],"additionalProperties":false},"description":"Every stop and shipment no vehicle takes, with why. [] when every one is in a route."},"totals":{"type":"object","properties":{"vehicles_used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Vehicles with at least one task."},"distance_m":{"type":"number","description":"Metres, every route's together."},"duration_s":{"type":"number","description":"Seconds, every route's duration_s together."},"waiting_s":{"type":"number","description":"Seconds waited, every route's together."},"service_s":{"type":"number","description":"Seconds of service, every route's together."}},"required":["vehicles_used","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false,"description":"The routes summed."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["routes","unassigned","totals","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"check_plan"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"feasible":{"type":"boolean","description":"true when no route breaks anything and every task is in a route: violations and unassigned are both []."},"routes":{"type":"array","items":{"type":"object","properties":{"vehicle_id":{"type":"string","description":"The vehicle's id, as sent."},"visits":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["start","stop","pickup","delivery","end"],"description":"'start' and 'end' are the vehicle's own; 'stop' a stop; 'pickup' and 'delivery' a shipment's two places."},"id":{"description":"The stop's or shipment's id; null at the vehicle's start and end.","type":["string","null"]},"snap_m":{"description":"Straight-line metres from the point sent to where it meets the profile's network; not included in distance_m. null when no road of the network lies within 500 m: the status is then no_road_near.","type":["number","null"]},"street_matched":{"description":"Whether the place met the road of the street sent; null without a street, and at a place sent as an address id (the answer holds nothing of the address).","type":["boolean","null"]},"arrival":{"description":"RFC 3339, in the offset of the vehicle's time_window; null without one.","type":["string","null"]},"departure":{"description":"RFC 3339: arrival, any wait and service_s later; null without a time_window.","type":["string","null"]},"arrival_s":{"type":"number","description":"Seconds from leaving start to arriving here."},"waiting_s":{"type":"number","description":"Seconds waited here for a time window to open."},"service_s":{"type":"number","description":"Seconds of service here, as sent."},"load":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"On board when leaving here, per dimension named in the request."},"distance_m":{"type":"number","description":"Metres along the network from the previous visit (0 at start)."},"duration_s":{"type":"number","description":"Seconds driving from the previous visit at free flow (0 at start)."}},"required":["kind","id","snap_m","street_matched","arrival","departure","arrival_s","waiting_s","service_s","load","distance_m","duration_s"],"additionalProperties":false},"description":"In the order driven: start, each task, end (where one was sent)."},"legs":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"},"description":"The leg's stretches with their road references (ADR-0013), in order; with geometry, each with its line."}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","segments"],"additionalProperties":false}},{"type":"null"}],"description":"With facts or geometry, one leg from each visit to the next, in order: visits.length − 1 of them. null without either."},"distance_m":{"type":"number","description":"Metres along the network over the whole route."},"duration_s":{"type":"number","description":"Seconds from leaving start to the last visit: driving, waiting and service."},"waiting_s":{"type":"number","description":"Seconds waited for time windows to open, over the route."},"service_s":{"type":"number","description":"Seconds of service, over the route."}},"required":["vehicle_id","visits","legs","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false},"description":"One per vehicle in routes with a visit, in the order of vehicles sent: optimize's route shape, timed and loaded as optimize times and loads a plan."},"unassigned":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["stop","shipment"],"description":"Whether id names a stop or a shipment."},"id":{"type":"string","description":"The stop's or shipment's id, as sent."},"reason":{"type":"string","enum":["unassigned_not_in_plan","unassigned_address_id","unassigned_unreachable"],"description":"Why, as a code from ERRORS. unassigned_not_in_plan: no route places it. unassigned_address_id: its address id resolves to no address served, so its visits leave the route and the rest is checked as sent. unassigned_unreachable: no road joins it to the visit before or after."},"message":{"type":"string","description":"The code's message from ERRORS, saying what to change; with unassigned_unreachable, the message for its cause."},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With unassigned_unreachable where only roads kept off by avoid reach the task: what in avoid is in the way, as route's blocked_by. null otherwise."},"unreachable":{"description":"With unassigned_unreachable: which place, where it met the network and why no vehicle reaches it. Absent with every other reason. met is null for a place sent as an address id: the answer holds no point of the address.","$ref":"#/components/schemas/Unreachable"},"unresolved":{"description":"With unassigned_address_id: which place's address id resolved to no address served, and why. Absent with every other reason.","$ref":"#/components/schemas/Unresolved"}},"required":["kind","id","reason","message","blocked_by"],"additionalProperties":false},"description":"Every stop and shipment in no route, with why. [] when the plan places every one."},"violations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["plan_capacity","plan_time_window","plan_vehicle_time_window","plan_skills","plan_max_travel","plan_shipment_order","plan_max_ride"],"description":"What is broken, as a code from ERRORS: its page at /errors/{code} says what to change."},"message":{"type":"string","description":"The code's message from ERRORS."},"vehicle_id":{"type":"string","description":"The vehicle whose route breaks it."},"visit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where: the index in the answered route's visits, 0 being start. It is the visit late or over capacity, the end for the vehicle's time_window and max_travel_s, and a shipment's delivery, else its pickup."},"kind":{"type":"string","enum":["stop","pickup","delivery","end"],"description":"The kind of the visit at that index."},"id":{"description":"The stop's or shipment's id; null at the end.","type":["string","null"]},"dimension":{"description":"With plan_capacity, the dimension over capacity; null otherwise.","type":["string","null"]},"over":{"description":"By how much: seconds late or over for plan_time_window, plan_vehicle_time_window, plan_max_travel and plan_max_ride; the amount over capacity for plan_capacity; null for plan_skills and plan_shipment_order.","type":["number","null"]}},"required":["code","message","vehicle_id","visit","kind","id","dimension","over"],"additionalProperties":false},"description":"Everything the routes break, by route and visit in order. [] when the plan holds."},"totals":{"type":"object","properties":{"vehicles_used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Vehicles with at least one task."},"distance_m":{"type":"number","description":"Metres, every route's together."},"duration_s":{"type":"number","description":"Seconds, every route's duration_s together."},"waiting_s":{"type":"number","description":"Seconds waited, every route's together."},"service_s":{"type":"number","description":"Seconds of service, every route's together."}},"required":["vehicles_used","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false,"description":"The routes summed."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["feasible","routes","unassigned","violations","totals","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"address_list"},"id":{"type":"string","description":"The job's id: read it with GET /v1/jobs/{id}."},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled","expired"],"description":"Where the job is: 'queued' or 'running' until it ends 'succeeded', 'failed' or 'cancelled', then 'expired' once its result is deleted at expires_at."},"progress":{"anyOf":[{"type":"object","properties":{"done":{"type":"number","description":"Units of work done."},"total":{"type":"number","description":"Units of work in all."}},"required":["done","total"],"additionalProperties":false},{"type":"null"}],"description":"How far the running job has come. null until it first reports."},"created_at":{"type":"string","description":"When the job was submitted, RFC 3339."},"started_at":{"description":"When it last started running, RFC 3339; null until it has.","type":["string","null"]},"finished_at":{"description":"When it ended, RFC 3339; null until it has.","type":["string","null"]},"expires_at":{"description":"When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).","type":["string","null"]},"error":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","description":"A code from the error table."},"message":{"type":"string","description":"That code's message, saying what to do next."}},"required":["code","message"],"additionalProperties":false},{"type":"null"}],"description":"Why a failed job failed. null in every other status."},"result":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["validate","geocode","reverse"],"description":"The kind sent."},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"description":"The id sent with the row; null where none was.","type":["string","null"]},"status":{"type":"string","enum":["matched","ambiguous","unmatched"],"description":"'matched': the row is one address, in address. 'ambiguous': it fits several addresses; send its postnummer or postort. 'unmatched': no address is the row. The recipe Your own address list says when each applies."},"address":{"anyOf":[{"$ref":"#/components/schemas/Address"},{"type":"null"}],"description":"The address the row is, when status is 'matched'; otherwise null, never a guess or another address."},"issues":{"type":"array","items":{"type":"string","enum":["street_not_found","number_not_found","letter_not_found","postcode_mismatch","locality_mismatch","ambiguous"],"description":"A problem with the submitted address."},"description":"For validate, what POST /v1/addresses/validate says is wrong with the row (empty when it is valid); for geocode and reverse, always empty."}},"required":["id","status","address","issues"],"additionalProperties":false},"description":"One row per row sent, in the order sent."},"summary":{"type":"object","properties":{"rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"matched":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"ambiguous":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unmatched":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["rows","matched","ambiguous","unmatched"],"additionalProperties":false,"description":"Your own rows counted by status: how many of them match an address. A count of your list, never of the register."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["kind","rows","summary","provenance"],"additionalProperties":false},{"type":"null"}],"description":"The operation's answer once the job has succeeded; null in every other status."}},"required":["operation","id","status","progress","created_at","started_at","finished_at","expires_at","error","result"],"additionalProperties":false}],"description":"A job: its operation, status and times, and its result once succeeded. One form per operation, by `operation`."},"JobCheck":{"type":"object","properties":{"operation":{"type":"string","enum":["route","distances","matrix","reach","trace","nearest","optimize","check_plan","address_list"],"description":"The job's operation, as sent."},"dry_run":{"type":"boolean","const":true,"description":"The job was checked, not queued: no job was made."},"accepted":{"type":"boolean","const":true,"description":"A submit of the same body now passes every check made before the queue: the job's limits, its network and profile and, for optimize and check_plan, each vehicle's start and end on a road. Not checked: quotas (the submit may be 429 quota_exceeded) and idempotency_key (a key already used answers its job, or 409 idempotency_conflict)."},"provenance":{"description":"The network snapshot the job would run on if submitted now, or the one network names, and its source. No network, sources empty and X-Data-Attribution empty, for address_list, which reads no register when checked, and when no network is named and none is served yet.","$ref":"#/components/schemas/Provenance"}},"required":["operation","dry_run","accepted","provenance"],"additionalProperties":false,"description":"A job checked and not queued (dry_run): a submit of the same body now would be accepted."}},"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"API key: kp_test_... or kp_live_..."},"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"The same API key, as a header."}}},"security":[{"bearer":[]},{"apiKey":[]}],"openapi":"3.1.2","paths":{"/v1/profiles":{"get":{"operationId":"listProfiles","summary":"The routing profiles","description":"Every routing profile: its mode and metric, whether it takes a vehicle, and whether it is experimental. Read it before you hard-code a profile name. It costs a request and no calculation.","tags":["registries"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"profiles":{"type":"array","items":{"type":"object","properties":{"name":{"$ref":"#/components/schemas/Profile"},"mode":{"type":"string","enum":["walk","bike","car"],"description":"Who travels: 'walk' on foot, 'bike' by bicycle, 'car' a motor vehicle, hgv's lorries and buses included."},"metric":{"type":"string","enum":["shortest","fastest"],"description":"What a path minimises: 'shortest' its length, 'fastest' its free-flow duration."},"description":{"type":"string","description":"Which roads the profile uses and how it weighs a path."},"takes_vehicle":{"type":"boolean","description":"true when the profile takes a vehicle; any other profile refuses one."},"experimental":{"type":"boolean","description":"true while the profile is not yet verified on the national network; its answers then say provenance.network.experimental: true."}},"required":["name","mode","metric","description","takes_vehicle","experimental"],"additionalProperties":false},"description":"Every profile the API answers on."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["profiles","provenance"],"additionalProperties":false},"example":{"profiles":[{"name":"walk","mode":"walk","metric":"shortest","description":"On foot, the shortest way by length: every network but motorways, motortrafikleder and ferries, undirected.","takes_vehicle":false,"experimental":false},{"name":"car_shortest","mode":"car","metric":"shortest","description":"By car, the shortest way by length, keeping every forbidden direction and turn and never turning back on an edge: the shortest legal road distance.","takes_vehicle":false,"experimental":false},{"name":"car_fastest","mode":"car","metric":"fastest","description":"By car, the quickest way at free flow: NVDB's posted limit per direction, else the functional class's default; no traffic, no time of day.","takes_vehicle":false,"experimental":false},{"name":"hgv","mode":"car","metric":"fastest","description":"A heavy goods vehicle or, with vehicle.kind bus, a bus, quickest at free flow capped at 80 km/h, kept off roads and turns NVDB closes to its kind in the direction travelled (a road closed to through traffic only is driven only in the zone an end lies in, marked destination_only) and, given the vehicle, off weight, axle-load, height, width and length limits below it, roads whose bearing class carries less than its own, and dangerous-goods bans. Verified on the national NVDB delivery by the restriction tests (scripts/hgv-production.ts); a lorry ban NVDB records one way only is kept off in both directions, and time windows are reported, not routed around.","takes_vehicle":true,"experimental":false},{"name":"bike","mode":"bike","metric":"shortest","description":"By bicycle, the shortest way by length, timed at one pace of 16 km/h: the cycle network, walk and cycle paths NVDB calls cycleable and every road but motorways, motortrafikleder and ferries, keeping each forbidden direction and turn and every traffic ban NVDB records for vehicles or bicycles unless it exempts bicycles (a road closed to through traffic only is ridden only in the zone an end lies in, marked destination_only). No barrier stops it. Experimental until checked on the national network.","takes_vehicle":false,"experimental":true}],"provenance":{"synthetic":true,"sources":[]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/facts":{"get":{"operationId":"listFacts","summary":"The facts catalog","description":"Every fact a path, a trace or a road can report, with its kind, unit, values, NVDB layers and whether a path can be kept off it. Read it before you hard-code a fact key. It costs a request and no calculation.","tags":["registries"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"facts":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"The fact's key, as facts and avoid.facts take it."},"kind":{"type":"string","enum":["category","measure","flag","metres","count"],"description":"How the fact is summed over a path: 'category' as metres per value, 'measure' as its min and max, 'flag' and 'metres' as metres, 'count' as a count."},"description":{"type":"string","description":"What NVDB records for the fact."},"unit":{"description":"The value's unit, e.g. 'km/h'; null where the value has none.","type":["string","null"]},"values":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The values the fact can take, where the catalog fixes them; null otherwise."},"avoidable":{"anyOf":[{"type":"string","enum":["flag","range","values"]},{"type":"null"}],"description":"How avoid.facts keeps a path off the fact: 'flag' as { fact }, 'range' as { fact, min, max }, 'values' as { fact, values }. A flag can also be preferred against in prefer. null where a path cannot be kept off it (ADR-0021 §5)."},"source":{"type":"object","properties":{"source":{"type":"string","const":"nvdb","description":"The source the fact is read from: always 'nvdb'."},"layers":{"type":"array","items":{"type":"string"},"description":"The NVDB layers the fact is read from."}},"required":["source","layers"],"additionalProperties":false,"description":"Where the fact is read from."}},"required":["key","kind","description","unit","values","avoidable","source"],"additionalProperties":false},"description":"Every fact in the catalog."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["facts","provenance"],"additionalProperties":false},"example":{"facts":[{"key":"speed_limit","kind":"category","description":"The posted speed limit in the direction travelled, in km/h; metres where none is recorded (or gångfart) are under \"none\".","unit":"km/h","values":null,"avoidable":"range","source":{"source":"nvdb","layers":["NVDB_DK_O_48_Hastighetsgrans"]}},{"key":"road_owner","kind":"category","description":"Who maintains the road (väghållare): the state, a municipality or a private owner.","unit":null,"values":["state","municipal","private","unknown"],"avoidable":"values","source":{"source":"nvdb","layers":["NVDB_DK_O_2_Vaghallare"]}},{"key":"surface","kind":"category","description":"The wearing course (slitlager): paved or gravel.","unit":null,"values":["paved","gravel","unknown"],"avoidable":"values","source":{"source":"nvdb","layers":["NVDB_DK_O_30_Slitlager"]}},{"key":"road_class","kind":"category","description":"Trafikverket's functional road class (funktionell vägklass), 0 (most important) to 9.","unit":null,"values":null,"avoidable":"range","source":{"source":"nvdb","layers":["NVDB_DK_O_38_FunkVagklass"]}},{"key":"network","kind":"category","description":"The network the path is on: the road network (bil), a cycle path (cykel) or a footpath (gang).","unit":null,"values":["bil","cykel","gang"],"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_88_Vagtrafiknat"]}},{"key":"gcm_type","kind":"category","description":"The kind of walk or cycle way NVDB records (GCM-vägtyp), e.g. walk_and_cycle_path (gång- och cykelbana), footway (gångbana), cycle_lane (cykelfält), pedestrian_crossing (övergångsställe), cycle_crossing (cykelpassage), cycle_priority_crossing (cykelöverfart), unmarked_crossing (gatupassage utan utmärkning), steps (trappa), square (torg/öppen yta); metres with none recorded, as on most roads, are under \"none\", and a value the catalog does not map under \"unknown\".","unit":null,"values":["walk_and_cycle_path","walk_and_cycle_path_divided","mandatory_cycle_path","cycle_lane","footway","pedestrian_crossing","cycle_crossing","cycle_and_pedestrian_crossing","cycle_priority_crossing","cycle_priority_and_pedestrian_crossing","unmarked_crossing","steps","lift","square","other_cycleable","other_not_cycleable","none","unknown"],"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_99_GCM_vagtyp"]}},{"key":"municipality","kind":"category","description":"The kommun the road lies in, by its four-digit code.","unit":null,"values":null,"avoidable":null,"source":{"source":"nvdb","layers":["AGGREGAT_O_5_KommunLanReg"]}},{"key":"road_name","kind":"category","description":"The road's name: its gatunamn, else its övrigt vägnamn; metres on roads with neither are under \"none\".","unit":null,"values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_20_Gatunamn","NVDB_DK_O_21_Ovrigt_vagnamn"]}},{"key":"road_number","kind":"category","description":"The road's number as signed (vägnummer), e.g. E4 or 364; metres on unnumbered roads are under \"none\".","unit":null,"values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_111_Vagnummer"]}},{"key":"traffic_safety_class","kind":"category","description":"Trafikverket's traffic safety class for the stretch (TS-klass), as delivered.","unit":null,"values":null,"avoidable":"range","source":{"source":"nvdb","layers":["VIS_DK_O_62_Trafiksakerhet_Stracka"]}},{"key":"built_up","kind":"flag","description":"Metres inside a built-up area (tättbebyggt område).","unit":"m","values":null,"avoidable":"flag","source":{"source":"nvdb","layers":["NVDB_DK_O_40_TattbebyggtOmrade"]}},{"key":"lit","kind":"flag","description":"Metres on a lit walk or cycle path (GCM belyst); roads carry no lighting record.","unit":"m","values":null,"avoidable":"flag","source":{"source":"nvdb","layers":["NVDB_DK_O_97_GCM_belyst"]}},{"key":"separated","kind":"flag","description":"Metres on a walk or cycle path NVDB records as separated from motor traffic (GCM-separation). NVDB records separation on the path's own link, so it says whether the link walked or cycled is separated; it never says whether a road has a path beside it, so metres along a road's carriageway are not counted because a separate path runs beside it.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_98_GCM_separation"]}},{"key":"ferry","kind":"flag","description":"Metres on a ferry route (färjeled).","unit":"m","values":null,"avoidable":"flag","source":{"source":"nvdb","layers":["NVDB_DK_O_23_Farjeled"]}},{"key":"motorway","kind":"flag","description":"Metres on a motorway or a motortrafikled.","unit":"m","values":null,"avoidable":"flag","source":{"source":"nvdb","layers":["NVDB_DK_O_19_Motorvag","NVDB_DK_O_18_Motortrafikled"]}},{"key":"fast_road","kind":"flag","description":"Road exposure: metres along the carriageway of a road whose limit is 70 km/h or more in either direction, functional road class 9 left out; a separate walk or cycle path beside it does not count.","unit":"m","values":null,"avoidable":"flag","source":{"source":"nvdb","layers":["NVDB_DK_O_48_Hastighetsgrans","NVDB_DK_O_38_FunkVagklass"]}},{"key":"speed_limit_varies","kind":"flag","description":"Metres where NVDB records a deviating limit (by time, season or vehicle) beside the base one.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_48_Hastighetsgrans"]}},{"key":"unlit_outside_built_up","kind":"metres","description":"Road exposure: exact metres outside a built-up area (tättbebyggt område); on a road every such metre, on a walk or cycle path only those not lit.","unit":"m","values":null,"avoidable":"flag","source":{"source":"nvdb","layers":["NVDB_DK_O_40_TattbebyggtOmrade","NVDB_DK_O_97_GCM_belyst"]}},{"key":"traffic_volume","kind":"measure","description":"Annual average daily traffic (ÅDT) of the road travelled or crossed, in vehicles a day.","unit":"vehicles/day","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["TRAFIK_DK_O_105_Trafik"]}},{"key":"crossings_without_refuge","kind":"count","description":"Road exposure: crossings of a road whose limit is 60 km/h or more where NVDB records no refuge (refug); a crossing counts where the path covers at least half the edge it lies on.","unit":null,"values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_27_GCM_passage"]}},{"key":"crossings_unknown_refuge","kind":"count","description":"Road exposure: crossings of a road whose limit is 60 km/h or more where NVDB records the refuge as unknown, or not at all; a crossing counts where the path covers at least half the edge it lies on.","unit":null,"values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_27_GCM_passage"]}},{"key":"level_crossings","kind":"count","description":"Level crossings of a railway (plankorsning).","unit":null,"values":null,"avoidable":null,"source":{"source":"nvdb","layers":["AGGREGAT_O_76_Plankorsning_vag_jarnvag"]}},{"key":"max_weight","kind":"measure","description":"The lowest gross-weight limit on the path (Begränsat bruttovikt, or a ForbudTrafik row with a Totalvikt), in tonnes.","unit":"t","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_*_BegrBruttovikt","NVDB_DK_O_12_ForbudTrafik"]}},{"key":"max_axle_load","kind":"measure","description":"The lowest axle-load limit on the path (Begränsat axeltryck), in tonnes.","unit":"t","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_*_BegrAxelBoggiTryck"]}},{"key":"max_height","kind":"measure","description":"The lowest free height under an obstacle on the path (Höjdhinder), in metres.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_*_Hojdhinder45dm"]}},{"key":"max_width","kind":"measure","description":"The lowest vehicle-width limit on the path, in metres.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_*_BegrFordBredd"]}},{"key":"max_length","kind":"measure","description":"The lowest vehicle-length limit on the path, in metres.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_*_BegrFordLangd"]}},{"key":"bearing_class","kind":"category","description":"Bärighetsklass (BK1-BK4): metres per class. A class limits gross weight through axle-spacing tables (Trafikförordningen 4 kap.), so it is reported, never turned into one weight.","unit":null,"values":["BK1","BK2","BK3","BK4","unknown"],"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_1_Barighet"]}},{"key":"hgv_banned","kind":"flag","description":"Metres where NVDB's traffic bans close the road to a lorry (lastbil, tung lastbil) in the direction travelled: closed, or closed but to line traffic. A road closed to a lorry's through traffic only is not counted: a lorry may drive it to or from an end there.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_12_ForbudTrafik"]}},{"key":"hazmat_banned","kind":"flag","description":"Metres where transport of dangerous goods (farligt gods) is banned.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_*_InskrTranspFarligtGods"]}},{"key":"hazmat_route","kind":"flag","description":"Metres on a recommended route for dangerous goods.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_*_RekomVagFarligtGods"]}},{"key":"timed_restriction","kind":"flag","description":"Metres under a traffic ban with a time window (ForbudTrafik): routes are not kept off these; the caller checks the window.","unit":"m","values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_12_ForbudTrafik"]}},{"key":"structure","kind":"category","description":"Whether the path is on a bridge (överfart), passes under something (underfart) or is in a tunnel (Bro och tunnel): metres per value, \"none\" off any.","unit":null,"values":["bridge","underpass","tunnel","none"],"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_25_Bro_och_tunnel"]}},{"key":"barriers","kind":"count","description":"Barriers across the road (Väghinder: a bollard, a gate, a cycle barrier, blocks) the path passes; one that stops the profile is never on its route.","unit":null,"values":null,"avoidable":null,"source":{"source":"nvdb","layers":["NVDB_DK_O_37_Vaghinder"]}}],"provenance":{"synthetic":true,"sources":[]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/sources":{"get":{"operationId":"listSources","summary":"The data sources and their terms","description":"Every data source: its licence, its attribution, the uses its terms allow and how long job results derived from it are kept. Read it before you hard-code a source id or a retention time. It costs a request and no calculation.","tags":["registries"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The source's id, as provenance.sources[].id names it."},"name":{"type":"string","description":"The source's name."},"licence":{"type":"string","description":"The source's licence, as an SPDX id."},"attribution":{"type":"string","description":"The credit to show wherever the source's data is shown or passed on."},"url":{"type":"string","description":"The licence's URL."},"terms":{"type":"string","description":"The licence or approval the allowed uses rest on."},"uses":{"type":"array","items":{"type":"string","enum":["single_answer","bulk","aggregate","derived_product","id_resolution","own_list"]},"description":"What the terms allow: 'single_answer' a few records per answer, 'bulk' many in one answer or job result, 'aggregate' counts or statistics over them, 'derived_product' a published dataset made from them. 'id_resolution' resolves stored address ids inside an answer that never returns the addresses, and 'own_list' matches the caller's own list row by row."},"result_ttl_hours":{"type":"number","description":"Hours a job result derived from the source is kept."}},"required":["id","name","licence","attribution","url","terms","uses","result_ttl_hours"],"additionalProperties":false},"description":"Every source an answer can read."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["sources","provenance"],"additionalProperties":false},"example":{"sources":[{"id":"lantmateriet","name":"Lantmäteriet, Belägenhetsadress","licence":"CC-BY-4.0","attribution":"Belägenhetsadress Nedladdning, vektor ©Lantmäteriet, bearbetad av Kartpunkt, CC BY 4.0","url":"https://creativecommons.org/licenses/by/4.0/","terms":"Lantmäteriet decision LM2026/143671 of 2026-10-13 (docs/compliance.md §6): single answers via API, no extraction, no joins with owner or resident data, history at most 24 months; supplement LM2026/159999 of 2026-10-13 (docs/compliance.md §2b): route optimization from stored address ids (use 2), at most 1 000 a call and 50 000 a job, answering routes, times and the caller's own ids, never the addresses; and the customer's own list (use 3) validated, geocoded or reverse geocoded as a job of at most 50 000 rows, one row out per row in, only to that customer, its input deleted when the job ends and its result kept at most 7 days","uses":["single_answer","id_resolution","own_list"],"result_ttl_hours":24},{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","terms":"CC0 1.0: no restriction","uses":["single_answer","bulk","aggregate","derived_product"],"result_ttl_hours":168},{"id":"synthetic","name":"Kartpunkt synthetic test addresses","licence":"CC0-1.0","attribution":"Syntetiska testadresser från Kartpunkt, inte verkliga adresser.","url":"https://creativecommons.org/publicdomain/zero/1.0/","terms":"Kartpunkt's own, made up: no restriction","uses":["single_answer","bulk","aggregate","derived_product","id_resolution","own_list"],"result_ttl_hours":168}],"provenance":{"synthetic":true,"sources":[]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/tilesets":{"get":{"operationId":"listTilesets","summary":"NVDB vector tiles","description":"The NVDB vector tiles of the network served now: a PMTiles archive for MapLibre, with speed limit, owner, surface, class, bearing class, lighting, fast_road and vehicle limits on each road and path. Use it to draw the network on a map; for one road's facts, use GET /v1/roads/{ref}. It costs a request and no calculation.","tags":["registries"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"tilesets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The tileset's name."},"url":{"type":"string","description":"The PMTiles archive: read it with the pmtiles protocol in MapLibre. The URL names its snapshot and never changes; CORS is open (NVDB is CC0)."},"network":{"type":"string","description":"The network snapshot the tiles were built from."},"source_date":{"description":"The NVDB delivery date (YYYY-MM-DD) of that snapshot.","type":["string","null"]},"minzoom":{"type":"number","description":"The lowest zoom level the archive holds."},"maxzoom":{"type":"number","description":"The highest zoom level the archive holds."},"layers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The vector layer's id."},"attributes":{"type":"array","items":{"type":"string"},"description":"The attributes its features carry."}},"required":["id","attributes"],"additionalProperties":false},"description":"The archive's vector layers."}},"required":["name","url","network","source_date","minzoom","maxzoom","layers"],"additionalProperties":false},"description":"The tilesets of the network served now; empty while none is built."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["tilesets","provenance"],"additionalProperties":false},"example":{"tilesets":[],"provenance":{"synthetic":true,"sources":[]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer read, the body's `provenance.network.id`; sent only when the answer read one (absent when no network snapshot was read).","schema":{"type":"string"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/networks":{"get":{"operationId":"listNetworks","summary":"Network snapshots","description":"The road network's snapshots, newest first, a page at a time: each NVDB build, its status, when it was served and which profiles it answers. Read it to choose a snapshot for network.id; a pruned one is not listed. It costs a request and no calculation.","tags":["registries"],"parameters":[{"in":"query","name":"limit","schema":{"description":"Snapshots per page; 50 when left out.","type":"integer","minimum":1,"maximum":100},"allowEmptyValue":true,"allowReserved":true,"description":"Snapshots per page; 50 when left out."},{"in":"query","name":"cursor","schema":{"description":"The previous page's next_cursor, unchanged. Left out, the first page.","type":"string","minLength":1,"maxLength":200},"allowEmptyValue":true,"allowReserved":true,"description":"The previous page's next_cursor, unchanged. Left out, the first page."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"networks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The snapshot's id, as network.id takes it."},"status":{"type":"string","enum":["building","staged","validated","active","retired","archived","failed"],"description":"Where the snapshot is in its life (ADR-0010): 'building', 'staged' and 'validated' before it serves; 'active' the one served now; 'retired' and 'archived' served before, answered as jobs; 'failed' a build that stopped."},"source":{"type":"string","description":"The dataset the snapshot is built from: 'nvdb'."},"source_date":{"description":"The NVDB delivery date (YYYY-MM-DD) the snapshot was built from.","type":["string","null"]},"built_at":{"type":"string","description":"When the snapshot was built (RFC 3339)."},"active_from":{"description":"When the snapshot was first served (RFC 3339); null if it never was.","type":["string","null"]},"active_until":{"description":"When the snapshot stopped being served (RFC 3339); null while it is served, or if it never was.","type":["string","null"]},"profiles":{"type":"array","items":{"$ref":"#/components/schemas/Profile"},"description":"The profiles this snapshot can answer."}},"required":["id","status","source","source_date","built_at","active_from","active_until","profiles"],"additionalProperties":false},"description":"The snapshots on this page, newest first."},"next_cursor":{"description":"Send it as cursor for the next page; null on the last page.","type":["string","null"]},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["networks","next_cursor","provenance"],"additionalProperties":false},"example":{"networks":[{"id":"snap_01k7exampleexampleexample0","status":"active","source":"synthetic","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","active_from":"2026-10-13T07:01:00.000Z","active_until":null,"profiles":["walk","car_shortest","car_fastest","bike"]}],"next_cursor":null,"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:02:00.000Z","graph":"snap_01k7exampleexampleexample0"},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer read, the body's `provenance.network.id`; sent only when the answer read one (absent when no network snapshot was read).","schema":{"type":"string"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/networks/{id}":{"get":{"operationId":"getNetwork","summary":"One network snapshot","description":"One network snapshot with its manifest: the NVDB deliveries, counts, layers read and code version it was built from. Use it to see what a stored provenance.network.id was built from; GET /v1/networks lists them all. It costs a request and no calculation.","tags":["registries"],"parameters":[{"in":"path","required":true,"name":"id","schema":{"type":"string","pattern":"^snap_[a-z0-9]+$","description":"The network snapshot's id, from GET /v1/networks or an answer's provenance.network.id."},"example":"snap_01k7exampleexampleexample0","description":"The network snapshot's id, from GET /v1/networks or an answer's provenance.network.id."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The snapshot's id, as network.id takes it."},"status":{"type":"string","enum":["building","staged","validated","active","retired","archived","failed"],"description":"Where the snapshot is in its life (ADR-0010): 'building', 'staged' and 'validated' before it serves; 'active' the one served now; 'retired' and 'archived' served before, answered as jobs; 'failed' a build that stopped."},"source":{"type":"string","description":"The dataset the snapshot is built from: 'nvdb'."},"source_date":{"description":"The NVDB delivery date (YYYY-MM-DD) the snapshot was built from.","type":["string","null"]},"built_at":{"type":"string","description":"When the snapshot was built (RFC 3339)."},"active_from":{"description":"When the snapshot was first served (RFC 3339); null if it never was.","type":["string","null"]},"active_until":{"description":"When the snapshot stopped being served (RFC 3339); null while it is served, or if it never was.","type":["string","null"]},"profiles":{"type":"array","items":{"$ref":"#/components/schemas/Profile"},"description":"The profiles this snapshot can answer."},"manifest":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"How the snapshot was built: deliveries, counts, the layers read, the code version."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["id","status","source","source_date","built_at","active_from","active_until","profiles","manifest","provenance"],"additionalProperties":false},"example":{"id":"snap_01k7exampleexampleexample0","status":"active","source":"synthetic","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","active_from":"2026-10-13T07:01:00.000Z","active_until":null,"profiles":["walk","car_shortest","car_fastest","bike"],"manifest":{"counts":{"edges":17824,"stops":405,"turns":1,"vertices":17827},"test_network":"synthetic-v20:150000"},"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:02:00.000Z","graph":"snap_01k7exampleexampleexample0"},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/geocode":{"post":{"operationId":"searchAddresses","summary":"Search and autocomplete locations","description":"Typo-tolerant search and autocomplete of Swedish addresses from the text a person types: street-level hits until a house number is typed, then address points. To check a whole address a person entered, use `POST /v1/addresses/validate`; to turn a coordinate into an address, `POST /v1/reverse-geocode`.","tags":["addresses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"q":{"type":"string","maxLength":200,"description":"Free text as typed, street first, e.g. 'Östra Ågatan 12B, Uppsala' or 'ostra agatan 12 b'. Typos, å/ä/ö without diacritics and abbreviations such as sthlm and gbg are handled. Only 8 distinct words are read, a postnummer as one; past them every hit is match_type 'partial'."},"limit":{"default":10,"description":"The most results to answer, best first.","type":"integer","minimum":1,"maximum":20},"near":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude, WGS84 decimal degrees, -90 to 90.","examples":[59.3293]},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude, WGS84 decimal degrees, -180 to 180.","examples":[18.0686]}},"required":["lat","lon"],"additionalProperties":false,"description":"A focus point: nearer same-named streets rank first, and every result gets distance_m."},"municipality_codes":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"string","pattern":"^\\d{4}$"},"description":"Kommunkoder, SCB's four-digit codes, e.g. ['0380']: only hits whose municipality_code is one of them are answered. They narrow before ranking, so a small kommun's street is found past same-named streets elsewhere. Only Sweden's 290 codes are accepted, a repeated one read once. A kommun with no address in the key's dataset answers results: []. Left out: all of Sweden."},"types":{"default":["address"],"description":"What to look for: addresses today, from Lantmäteriet's Belägenhetsadress or, for a kp_test_ key, the synthetic dataset. Place names and POIs join later.","minItems":1,"type":"array","items":{"type":"string","enum":["address"]}}},"required":["q"],"additionalProperties":false},"example":{"q":"Östra Ågatan, Uppsala"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/Address"},"description":"Best matches first; street-level hits until a house number is typed."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["results","provenance"],"additionalProperties":false},"example":{"results":[{"id":"street:218126d23ec3b9e4df37b27ef664b288","type":"street","label":"Östra Ågatan, 757 97 Uppsala","street":"Östra Ågatan","number":null,"letter":null,"postcode":"75797","locality":"Uppsala","municipality_code":"0380","municipality":"Uppsala","county_code":"03","county":"Uppsala län","district_code":"903801","district":"Uppsala norra","point":{"lat":59.922464897740404,"lon":17.582517826500723},"position_type":null,"match_type":"exact","score":6.596714830317912}],"provenance":{"synthetic":true,"sources":[{"id":"synthetic","name":"Kartpunkt synthetic test addresses","licence":"CC0-1.0","attribution":"Syntetiska testadresser från Kartpunkt, inte verkliga adresser.","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/reverse-geocode":{"post":{"operationId":"reverseGeocode","summary":"The nearest addresses to a point","description":"The nearest current addresses to a WGS84 point, nearest first with their `distance_m`, and the nearest named road beside them as `road`. For a vehicle's position, choose the street it drove on from the results yourself: see the recipe \"A vehicle's position\". To find addresses from text, use `POST /v1/geocode`.","tags":["addresses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"point":{"$ref":"#/components/schemas/Point"},"types":{"default":["address"],"description":"What to look for: addresses today, from Lantmäteriet's Belägenhetsadress or, for a kp_test_ key, the synthetic dataset. Place names and POIs join later.","minItems":1,"type":"array","items":{"type":"string","enum":["address"]}},"limit":{"default":1,"description":"How many addresses to return, nearest first. With distinct 'street' it counts streets: limit 3 answers the three nearest streets, one address each.","type":"integer","minimum":1,"maximum":10},"radius_m":{"default":2000,"description":"Ignore addresses farther away than this, in straight-line metres. For a vehicle's position send \"radius_m\": 5000, \"distinct\": \"street\", \"limit\": 3, as the recipe \"A vehicle's position\" does: 5000 m reaches the nearest address from a rural road.","type":"number","exclusiveMinimum":0,"maximum":50000},"distinct":{"description":"Send 'street' for one address per street: the nearest address of each street within radius_m, nearest first, with limit counting streets. A street is one street name in one postort and one kommun, as a search street hit is. Left out, several results can lie on one street.","type":"string","enum":["street"]}},"required":["point"],"additionalProperties":false},"example":{"point":{"lat":59.922385,"lon":17.582745},"limit":3}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/Address"},"description":"The nearest addresses, nearest first."},"road":{"anyOf":[{"type":"object","properties":{"name":{"description":"The road's street name (gatunamn), the name addresses on it are formed with, else its other official name. null when the road has only a number.","type":["string","null"]},"road_number":{"description":"The road's number as signed, e.g. 'E6' for a europaväg or '158'; null when it has none. Never null together with name.","type":["string","null"]},"road_class":{"anyOf":[{"type":"integer","minimum":0,"maximum":9},{"type":"null"}],"description":"Functional road class (funktionell vägklass) as Trafikverket delivers it, from 0 for the roads most important to the whole network, such as motorways, to 9 for the least. 0-2 is a through road and 6-9 a local street; null when the road has none."},"speed_limit_kmh":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}],"description":"The base speed limit (hastighetsgräns) in km/h, e.g. 50, where the road is nearest the point, whatever the time of the call. A point has no direction of travel, so where the two directions have different limits this is the lower. null where no limit is recorded or the limit is gångfart (walking pace), which has no figure."},"speed_limit_varies":{"type":"boolean","description":"true when the stretch also has a limit that applies only at certain times or to certain vehicles, which this answer does not carry: a summer limit, a lower limit on weekday mornings or one for heavy vehicles. false otherwise, and when no limit is recorded."},"owner":{"anyOf":[{"type":"string","enum":["state","municipal","private"]},{"type":"null"}],"description":"The road owner (väghållare), who keeps the road where it is nearest the point: 'state' (Trafikverket), 'municipal' (a kommun) or 'private' (a road association, a company or a person). The kind only, never who; null where none is recorded."},"surface":{"anyOf":[{"type":"string","enum":["paved","gravel"]},{"type":"null"}],"description":"The surface (slitlager) where the road is nearest the point: 'paved' or 'gravel'; null where none is recorded."},"municipality_code":{"description":"Kommunkod, four digits, e.g. '1080': the kommun NVDB registers the road's stretch in, never the kommun's name; null where none is recorded. It is a fact about the road, so near a kommun border it can differ from results[0].municipality_code.","type":["string","null"]},"distance_m":{"type":"number","description":"Straight-line metres (spheroidal) from the point sent to the nearest point of the road."},"fallback":{"type":"boolean","description":"true when no address in results is within 50 m of the point: results is empty, or its first address is farther away."}},"required":["name","road_number","road_class","speed_limit_kmh","speed_limit_varies","owner","surface","municipality_code","distance_m","fallback"],"additionalProperties":false},{"type":"null"}],"description":"The nearest road a car can use with a name or number, within 100 m of the point; null when none is, or no road graph is built yet. It is NVDB's real road for every key. It has no id and is not counted in results. To choose it or an address, see the recipe \"A vehicle's position\"."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["results","road","provenance"],"additionalProperties":false},"example":{"results":[{"id":"se.syn.syn-0000t9b","type":"address","label":"Östra Ågatan 12B, 757 97 Uppsala","street":"Östra Ågatan","number":12,"letter":"B","postcode":"75797","locality":"Uppsala","municipality_code":"0380","municipality":"Uppsala","county_code":"03","county":"Uppsala län","district_code":"903801","district":"Uppsala norra","point":{"lat":59.92238511056452,"lon":17.582745460249157},"position_type":"entrance","match_type":null,"score":1,"distance_m":0.03},{"id":"se.syn.syn-0000t9a","type":"address","label":"Östra Ågatan 12A, 757 97 Uppsala","street":"Östra Ågatan","number":12,"letter":"A","postcode":"75797","locality":"Uppsala","municipality_code":"0380","municipality":"Uppsala","county_code":"03","county":"Uppsala län","district_code":"903801","district":"Uppsala norra","point":{"lat":59.92238919581831,"lon":17.58281664402885},"position_type":"entrance","match_type":null,"score":1,"distance_m":4.03},{"id":"se.syn.syn-0000t99","type":"address","label":"Östra Ågatan 12, 757 97 Uppsala","street":"Östra Ågatan","number":12,"letter":null,"postcode":"75797","locality":"Uppsala","municipality_code":"0380","municipality":"Uppsala","county_code":"03","county":"Uppsala län","district_code":"903801","district":"Uppsala norra","point":{"lat":59.92239328453523,"lon":17.582887649142425},"position_type":"building","match_type":null,"score":1,"distance_m":8.03}],"road":{"name":"Östra Ågatan","road_number":null,"road_class":6,"speed_limit_kmh":50,"speed_limit_varies":false,"owner":"municipal","surface":"paved","municipality_code":"0380","distance_m":22.02,"fallback":false},"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0"},"sources":[{"id":"synthetic","name":"Kartpunkt synthetic test addresses","licence":"CC0-1.0","attribution":"Syntetiska testadresser från Kartpunkt, inte verkliga adresser.","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"},{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer read, the body's `provenance.network.id`; sent only when the answer read one (absent when no network snapshot was read).","schema":{"type":"string"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/addresses/validate":{"post":{"operationId":"validateAddress","summary":"Validate an address","description":"Whether an address a person entered exists as given, with the closest match, what differs in `issues` and other candidates; it is never silently corrected. For autocomplete while the person types, use `POST /v1/geocode`.","tags":["addresses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"street":{"type":"string","minLength":1,"maxLength":200,"description":"Street name or address area, e.g. 'Östra Ågatan'. Case and å/ä/ö are ignored."},"number":{"description":"House number, e.g. 12.","anyOf":[{"type":"integer","minimum":0,"maximum":99999},{"type":"null"}]},"letter":{"description":"Letter after the number, e.g. 'B'.","anyOf":[{"type":"string","maxLength":2},{"type":"null"}]},"postcode":{"description":"Postnummer, e.g. '75331' or '753 31'. Scopes the match; strongly recommended.","anyOf":[{"type":"string","maxLength":10},{"type":"null"}]},"locality":{"description":"Postort or kommun, e.g. 'Uppsala'. Scopes the match when postcode is missing.","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]}},"required":["street"],"additionalProperties":false},"example":{"street":"Östra Ågatan","number":12,"letter":"B","postcode":"75797"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","description":"true only if the address exists exactly as given (no issues)."},"match":{"anyOf":[{"$ref":"#/components/schemas/Address"},{"type":"null"}],"description":"The closest existing address, or null when the street was not found; on number_not_found, the nearest number on the street. It is never silently corrected: compare it with the input."},"issues":{"type":"array","items":{"type":"string","enum":["street_not_found","number_not_found","letter_not_found","postcode_mismatch","locality_mismatch","ambiguous"],"description":"A problem with the submitted address."},"description":"What is wrong with the input; empty when valid."},"suggestions":{"type":"array","items":{"$ref":"#/components/schemas/Address"},"description":"Other candidates: on street_not_found, search hits for the input; on number_not_found, the nearest house numbers on the street. Otherwise empty."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["valid","match","issues","suggestions","provenance"],"additionalProperties":false},"example":{"valid":true,"match":{"id":"se.syn.syn-0000t9b","type":"address","label":"Östra Ågatan 12B, 757 97 Uppsala","street":"Östra Ågatan","number":12,"letter":"B","postcode":"75797","locality":"Uppsala","municipality_code":"0380","municipality":"Uppsala","county_code":"03","county":"Uppsala län","district_code":"903801","district":"Uppsala norra","point":{"lat":59.92238511056452,"lon":17.582745460249157},"position_type":"entrance","match_type":"exact","score":1},"issues":[],"suggestions":[],"provenance":{"synthetic":true,"sources":[{"id":"synthetic","name":"Kartpunkt synthetic test addresses","licence":"CC0-1.0","attribution":"Syntetiska testadresser från Kartpunkt, inte verkliga adresser.","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/addresses/changes":{"post":{"operationId":"checkAddressChanges","summary":"What changed for stored addresses","description":"For address ids stored from earlier answers, what happened since a date: each is `unchanged`, `changed` with the fields that differ, `closed` or `unknown`. It answers only for the ids sent; there is no feed of the register. See the recipe \"Keeping stored addresses current\".","tags":["addresses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"The address ids to check, as stored from earlier answers' `id`; split a longer list into several calls. A kp_test_ key's ids are checked against the synthetic dataset only, a kp_live_ key's against the live one. An id sent twice is answered once."},"since":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"The date (YYYY-MM-DD) the stored addresses were last known current: the `provenance.sources[].updated` of the answer they came from, not the day they were stored. Changes dated after it are reported. It is at most today (UTC) and at most 24 months back, the history kept; with no stored date, send the earliest accepted."}},"required":["ids","since"],"additionalProperties":false},"example":{"ids":["se.syn.syn-0000t9b","se.syn.not-an-address","se.lm.not-in-this-dataset"],"since":"2026-10-13"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/AddressChange"},"description":"One result per distinct id sent, in the order sent. Never empty: an id with nothing to report is 'unchanged' or 'unknown'."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["results","provenance"],"additionalProperties":false},"example":{"results":[{"id":"se.syn.syn-0000t9b","status":"unchanged"},{"id":"se.syn.not-an-address","status":"unknown"},{"id":"se.lm.not-in-this-dataset","status":"unknown"}],"provenance":{"synthetic":true,"sources":[{"id":"synthetic","name":"Kartpunkt synthetic test addresses","licence":"CC0-1.0","attribution":"Syntetiska testadresser från Kartpunkt, inte verkliga adresser.","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/addresses/lookup":{"post":{"operationId":"lookupAddress","summary":"One address by its id","description":"The address one stored id names, as served now or at as_of: the id an earlier answer gave you, which is Lantmäteriet's own object identity of the address (se.lm.…), not Kartpunkt's. One id per call. A closed id is 404 address_closed, an id this key's dataset never held 404 address_unknown. To check many stored ids at once, use POST /v1/addresses/changes.","tags":["addresses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","maxLength":100,"pattern":"^se\\.(lm|syn)\\.[A-Za-z0-9-]{1,80}$","description":"One address id, as an earlier answer's id gave it: 'se.lm.' and Lantmäteriet's object identity of the belägenhetsadress for a kp_live_ key, 'se.syn.' and a code for a kp_test_ key. A street hit's id ('street:…') names no address."},"as_of":{"description":"The version of the address served at this instant (RFC 3339 with offset), e.g. when your decision was made, so the same call answers the same again; left out, or later than now, the version served now. Not more than 24 months back: versions are kept that long.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["id"],"additionalProperties":false},"example":{"id":"se.syn.syn-0000t9b"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"description":"The address the id names, as served at the instant read (score 1, match_type null: no words were sent).","$ref":"#/components/schemas/Address"},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["address","provenance"],"additionalProperties":false},"example":{"address":{"id":"se.syn.syn-0000t9b","type":"address","label":"Östra Ågatan 12B, 757 97 Uppsala","street":"Östra Ågatan","number":12,"letter":"B","postcode":"75797","locality":"Uppsala","municipality_code":"0380","municipality":"Uppsala","county_code":"03","county":"Uppsala län","district_code":"903801","district":"Uppsala norra","point":{"lat":59.92238511056452,"lon":17.582745460249157},"position_type":"entrance","match_type":null,"score":1},"provenance":{"synthetic":true,"sources":[{"id":"synthetic","name":"Kartpunkt synthetic test addresses","licence":"CC0-1.0","attribution":"Syntetiska testadresser från Kartpunkt, inte verkliga adresser.","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13","as_of":"2026-10-13T07:00:00.000Z"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"This address id is not served on the date asked (as_of, else now): a delivery of the register closed it (the address was removed from the register, or is no longer current there). The register names no successor, so find the address again with POST /v1/addresses/validate or POST /v1/geocode and store the new id; mark the old one closed rather than deleting it, since the register can serve the same id again. For the version served before it closed, send as_of an instant when it was served. No version of this address id in this key's dataset on the date asked. It was never issued, is past the 24 months of history kept, opened only after as_of, or is the other dataset's (a kp_test_ key reads only synthetic se.syn. ids, a kp_live_ key only Lantmäteriet's se.lm. ids). Send an id an earlier answer of this key gave; after going live, look your addresses up again with the kp_live_ key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["address_closed","address_unknown"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/postcodes/{postcode}":{"get":{"operationId":"lookupPostcode","summary":"A postnummer's postorter and kommuner","description":"The postorter and kommuner a postnummer is used in, with how many addresses each has, from the address register. To find or check a whole address, use `POST /v1/geocode` or `POST /v1/addresses/validate`.","tags":["addresses"],"parameters":[{"in":"path","required":true,"name":"postcode","schema":{"type":"string","pattern":"^\\d{3} ?\\d{2}$","description":"Postnummer, five digits, optionally with a space after the third (URL-encode the space)."},"example":"75797","description":"Postnummer, five digits, optionally with a space after the third (URL-encode the space)."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"postcode":{"type":"string","description":"Postnummer, five digits without space."},"localities":{"type":"array","items":{"type":"object","properties":{"locality":{"description":"Postort; null for the addresses with this postnummer that have none.","type":["string","null"]},"municipality_code":{"type":"string","description":"Kommunkod, four digits."},"municipality":{"type":"string","description":"Kommun name."},"county_code":{"description":"Länskod, two digits as the register delivers it (e.g. '03' for Uppsala län); null when the address has none.","type":["string","null"]},"county":{"description":"Län name as SCB writes it, e.g. 'Uppsala län'; null when county_code is null or is not one of Sweden's 21 län codes.","type":["string","null"]},"addresses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of current addresses with this postnummer in this postort and kommun."}},"required":["locality","municipality_code","municipality","county_code","county","addresses"],"additionalProperties":false},"description":"Postorter and kommuner the postnummer is used in, most addresses first. Usually one."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["postcode","localities","provenance"],"additionalProperties":false},"example":{"postcode":"75797","localities":[{"locality":"Uppsala","municipality_code":"0380","municipality":"Uppsala","county_code":"03","county":"Uppsala län","addresses":30}],"provenance":{"synthetic":true,"sources":[{"id":"synthetic","name":"Kartpunkt synthetic test addresses","licence":"CC0-1.0","attribution":"Syntetiska testadresser från Kartpunkt, inte verkliga adresser.","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"Nothing matches. Check the value; postnummer used only for PO boxes (box addresses) are not in the address register.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["not_found"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/snap":{"post":{"operationId":"snapPoints","summary":"Meet points to the road network","description":"Where each point meets the profile's network: the nearest point of a road within radius_m, with snap_m, the road's name and number, and its road reference there. Use it to see where a point meets the network before you route from it; for the roads around a point and their facts, use POST /v1/roads/nearby. It costs a request and no calculation.","tags":["roads"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"points":{"minItems":1,"maxItems":100,"type":"array","items":{"$ref":"#/components/schemas/StreetPoint"},"description":"The points to meet to the network, answered in results in the same order."},"profile":{"default":"car_shortest","$ref":"#/components/schemas/Profile"},"radius_m":{"default":500,"description":"The straight-line search bound round each point, in metres.","type":"number","exclusiveMinimum":0,"maximum":2000},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["points"],"additionalProperties":false},"example":{"points":[{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},{"lat":63.720632,"lon":19.345215},{"lat":63.724508,"lon":19.339523,"street":"Skolvägen"},{"lat":56,"lon":19.5}],"profile":"car_shortest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"point":{"description":"Where the point meets the network: the nearest point of the road.","$ref":"#/components/schemas/Point"},"snap_m":{"type":"number","description":"Straight-line metres from the point sent to where it meets the network; never part of a distance_m."},"street_matched":{"description":"true when the point met a road of the street sent, false when it met the nearest road instead. null when no street was sent or no road is near.","type":["boolean","null"]},"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The road's reference at that point, its two measures equal; null on a road with no NVDB link."},"road":{"type":"object","properties":{"name":{"description":"The road's name in NVDB; null where it has none.","type":["string","null"]},"number":{"description":"The road's number (vägnummer): 'E6' for a europaväg, else digits; null where it has none.","type":["string","null"]}},"required":["name","number"],"additionalProperties":false,"description":"The road the point meets."}},"required":["point","snap_m","street_matched","ref","road"],"additionalProperties":false},{"type":"null"}],"description":"null where no road of the profile's network lies within radius_m."},"description":"One per point sent, in order; null where no road of the profile's network lies within radius_m."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["results","provenance"],"additionalProperties":false},"example":{"results":[{"point":{"lat":63.7207239,"lon":19.3421866},"snap_m":14.97,"street_matched":true,"ref":"0:17733@0.016646-0.016646","road":{"name":"Skolvägen","number":null}},{"point":{"lat":63.7206325,"lon":19.3452151},"snap_m":0.05,"street_matched":null,"ref":"0:17733@0.266710-0.266710","road":{"name":"Skolvägen","number":null}},{"point":{"lat":63.7208214,"lon":19.3389572},"snap_m":411.98,"street_matched":true,"ref":"0:17732@0.499991-0.499991","road":{"name":"Skolvägen","number":null}},null],"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"car_shortest","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/roads/nearby":{"post":{"operationId":"listRoadsNearby","summary":"Roads near a point, with facts","description":"The nearest stretches of drivable road to a point within radius_m, nearest first, each with its road reference, distance_m and the facts asked for. Use it to say which road a point lies on and what NVDB records there; to meet a point to a profile's network, use POST /v1/snap. It costs a request and no calculation.","tags":["roads"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"point":{"$ref":"#/components/schemas/Point"},"radius_m":{"default":50,"description":"The straight-line search bound round the point, in metres.","type":"number","exclusiveMinimum":0,"maximum":500},"limit":{"default":5,"description":"The most roads to answer.","type":"integer","minimum":1,"maximum":20},"facts":{"description":"The facts to report on each road: see GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"geojson","description":"How lines are returned: GeoJSON, an encoded polyline at 6 decimals, or not at all.","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["point"],"additionalProperties":false},"example":{"point":{"lat":63.720632,"lon":19.345215},"radius_m":200,"limit":3,"facts":["speed_limit","surface","road_owner","road_class"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"roads":{"type":"array","items":{"type":"object","properties":{"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The stretch's road reference; null on a road with no NVDB link."},"distance_m":{"type":"number","description":"Straight-line metres, spheroidal, from the point sent to the nearest point of the road."},"name":{"description":"The road's name in NVDB; null where it has none.","type":["string","null"]},"number":{"description":"The road's number (vägnummer): 'E6' for a europaväg, else digits; null where it has none.","type":["string","null"]},"length_m":{"type":"number","description":"Metres of the stretch."},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Each fact asked for, by key, with its value on this stretch."},"geometry":{"description":"The stretch's line, in the geometry format asked; left out with geometry 'none'.","anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]}},"required":["ref","distance_m","name","number","length_m","facts"],"additionalProperties":false},"description":"The roads found, nearest first; empty when none lies within radius_m."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["roads","provenance"],"additionalProperties":false},"example":{"roads":[{"ref":"0:17733@0.000000-1.000000","distance_m":0.05,"name":"Skolvägen","number":null,"length_m":600,"facts":{"speed_limit":"40","surface":"paved","road_owner":"municipal","road_class":"7"},"geometry":{"type":"LineString","coordinates":[[19.341985,63.72073],[19.354096,63.720364]]}},{"ref":"0:17742@0.000000-1.000000","distance_m":128.03,"name":"Torggatan","number":null,"length_m":500,"facts":{"speed_limit":"40","surface":"paved","road_owner":"municipal","road_class":"7"},"geometry":{"type":"LineString","coordinates":[[19.341985,63.72073],[19.348591,63.724127]]}},{"ref":"0:17732@0.000000-1.000000","distance_m":160,"name":"Skolvägen","number":null,"length_m":300,"facts":{"speed_limit":"40","surface":"paved","road_owner":"municipal","road_class":"7"},"geometry":{"type":"LineString","coordinates":[[19.335929,63.720913],[19.341985,63.72073]]}}],"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0"},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/roads/{ref}":{"get":{"operationId":"getRoad","summary":"A road by its reference","description":"The stretches of the served network that a road reference covers, with the facts asked for. Use it to read back a ref stored from any answer; for the roads around a point, use POST /v1/roads/nearby. It costs a request and no calculation.","tags":["roads"],"parameters":[{"in":"path","required":true,"name":"ref","schema":{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},"example":"0:17733@0.266710-0.266710","description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013)."},{"in":"query","name":"facts","schema":{"description":"The facts to report on each stretch, one query parameter per key: ?facts=speed_limit&facts=surface. See GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"allowEmptyValue":true,"allowReserved":true,"style":"deepObject","explode":true,"example":["speed_limit","surface"],"description":"The facts to report on each stretch, one query parameter per key: ?facts=speed_limit&facts=surface. See GET /v1/facts. None by default."},{"in":"query","name":"geometry","schema":{"default":"geojson","description":"How lines are returned: GeoJSON, an encoded polyline at 6 decimals, or not at all.","type":"string","enum":["geojson","polyline6","none"]},"allowEmptyValue":true,"allowReserved":true,"description":"How lines are returned: GeoJSON, an encoded polyline at 6 decimals, or not at all."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"roads":{"type":"array","items":{"type":"object","properties":{"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The stretch's road reference; null on a road with no NVDB link."},"name":{"description":"The road's name in NVDB; null where it has none.","type":["string","null"]},"number":{"description":"The road's number (vägnummer): 'E6' for a europaväg, else digits; null where it has none.","type":["string","null"]},"length_m":{"type":"number","description":"Metres of the stretch."},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Each fact asked for, by key, with its value on this stretch."},"geometry":{"description":"The stretch's line, in the geometry format asked; left out with geometry 'none'.","anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]}},"required":["ref","name","number","length_m","facts"],"additionalProperties":false},"description":"The stretches of the served network that cover the ref, in measure order. Empty when that snapshot holds no road on the ref's link."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["roads","provenance"],"additionalProperties":false},"example":{"roads":[{"ref":"0:17733@0.000000-1.000000","name":"Skolvägen","number":null,"length_m":600,"facts":{"speed_limit":"40","surface":"paved"},"geometry":{"type":"LineString","coordinates":[[19.341985,63.72073],[19.354096,63.720364]]}}],"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0"},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/route":{"post":{"operationId":"planRoute","summary":"A route, with facts along it","description":"The route between two points, through any via points, on a profile: its distance, free-flow duration, segments with their road references, and the facts asked for summed over it. Its status says whether there is a route: a 200 with another status is an answer, not an error. For many origins to a few destinations use POST /v1/distances; for a GPS trace already travelled, POST /v1/trace.","tags":["routing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"origin":{"$ref":"#/components/schemas/StreetPoint"},"destination":{"$ref":"#/components/schemas/StreetPoint"},"via":{"description":"Points the route passes through, in the order sent.","maxItems":10,"type":"array","items":{"$ref":"#/components/schemas/StreetPoint"}},"profile":{"$ref":"#/components/schemas/Profile"},"vehicle":{"$ref":"#/components/schemas/Vehicle"},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"alternatives":{"description":"Other paths to answer beside the main one: 'unrestricted', the path without avoid (what the main path is measured against, e.g. a closure's detour).","maxItems":1,"type":"array","items":{"type":"string","enum":["unrestricted"]}},"facts":{"description":"The facts to report along the path: see GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"geojson","description":"How lines are returned: GeoJSON, an encoded polyline at 6 decimals, or not at all.","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["origin","destination","profile"],"additionalProperties":false},"example":{"origin":{"lat":63.724247,"lon":19.347595},"destination":{"lat":63.72059,"lon":19.342166},"profile":"walk","facts":["speed_limit","road_class","surface","fast_road"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"via":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/SnapResult"}},{"type":"null"}],"description":"Where each via point met the network, in order; null when none was sent."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"avoid":{"anyOf":[{"type":"object","properties":{"stretches":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"matched":{"type":"boolean","description":"Whether the stretch matched a road. false: none lies along it (drawn where NVDB no longer has a road): redraw it."}},"required":["matched"],"additionalProperties":false}},{"type":"null"}],"description":"Per avoid.stretches entry, in order; null when none was sent."},"values":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"between":{"type":"array","items":{"type":"object","properties":{"point":{"description":"Where the point met the road: the nearest point of a piece with the value.","$ref":"#/components/schemas/Point"},"snap_m":{"type":"number","description":"Straight-line metres from the point sent to there."}},"required":["point","snap_m"],"additionalProperties":false},"description":"Where each of the two points met the road, in the order sent."},"roads":{"type":"array","items":{"type":"object","properties":{"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The stretch kept off: an NVDB link and the part of it, measures ascending; null on a road with no NVDB link."},"length_m":{"type":"number","description":"Metres of the stretch."}},"required":["ref","length_m"],"additionalProperties":false},"description":"The road kept off between the points, from the first point's end of the run to the second's: the run's pieces and those beside it, each whole."},"length_m":{"type":"number","description":"Metres of road kept off, all of roads together."}},"required":["between","roads","length_m"],"additionalProperties":false,"description":"What an avoid.values entry with between matched."},{"type":"null"}]}},{"type":"null"}],"description":"Per avoid.values entry, in order: with between, the road it matched, to check against the closure's wording; null for an entry without between. null when no avoid.values was sent."}},"required":["stretches","values"],"additionalProperties":false},{"type":"null"}],"description":"What avoid matched; null without avoid."},"alternatives":{"anyOf":[{"type":"object","properties":{"unrestricted":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","segments"],"additionalProperties":false}},"additionalProperties":false},{"type":"null"}],"description":"The alternatives asked for, by label: unrestricted, the route without avoid with its own segments. null when none was."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","via","segments","avoid","alternatives","provenance"],"additionalProperties":false},"example":{"status":"ok","distance_m":560,"duration_s":403,"facts":{"speed_limit":{"40":560},"road_class":{"7":560},"surface":{"paved":560},"fast_road":0},"geometry":{"type":"LineString","coordinates":[[19.347581,63.724157],[19.348591,63.724127],[19.341985,63.72073],[19.342187,63.720724]]},"avoid_m":null,"snap_m":10.05,"street_matched":null,"destination_snap_m":14.97,"destination_street_matched":null,"blocked_by":null,"via":null,"segments":[{"length_m":50,"ref":"0:17736@0.833269-1.000000","facts":{"speed_limit":"40","road_class":"7","surface":"paved","fast_road":false},"geometry":{"type":"LineString","coordinates":[[19.347581,63.724157],[19.348591,63.724127]]}},{"length_m":500,"ref":"0:17742@1.000000-0.000000","facts":{"speed_limit":"40","road_class":"7","surface":"paved","fast_road":false},"geometry":{"type":"LineString","coordinates":[[19.348591,63.724127],[19.341985,63.72073]]}},{"length_m":10,"ref":"0:17733@0.000000-0.016646","facts":{"speed_limit":"40","road_class":"7","surface":"paved","fast_road":false},"geometry":{"type":"LineString","coordinates":[[19.341985,63.72073],[19.342187,63.720724]]}}],"avoid":null,"alternatives":null,"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"walk","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/distances":{"post":{"operationId":"measureDistances","summary":"Distances from many origins to their destinations","description":"Distances along the network from each origin to each of a few destinations, on a profile, each result with its own status, so an origin with no path does not fail the call. The facts asked for are summed over each path: metres and counts NVDB records, never a verdict on whether a route is safe. Use it instead of POST /v1/matrix when you need each path's facts, line or alternative; it costs a calculation per origin per destination.","tags":["routing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"destinations":{"minItems":1,"maxItems":5,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the destination, echoed in each result as destination_id.","type":"string","maxLength":64},"point":{"$ref":"#/components/schemas/StreetPoint"}},"required":["point"],"additionalProperties":false},"description":"The destinations, each answered for every origin. results holds one entry per origin per destination, each origin's together in the order of destinations: results[origin × destinations + destination]. Each result names its destination_id and destination_index."},"origins":{"minItems":1,"maxItems":1000,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the origin, echoed back.","type":"string","maxLength":64},"point":{"$ref":"#/components/schemas/StreetPoint"}},"required":["point"],"additionalProperties":false},"description":"The points to measure from, each answered in results in the order sent. Never the homes of people with protected identities (skyddade personuppgifter)."},"profile":{"$ref":"#/components/schemas/Profile"},"vehicle":{"$ref":"#/components/schemas/Vehicle"},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"alternatives":{"description":"Other paths to answer beside the main one: 'unrestricted', the path without avoid. With avoid, ['unrestricted'] unless sent; send [] for the main path alone.","maxItems":1,"type":"array","items":{"type":"string","enum":["unrestricted"]}},"facts":{"description":"The facts to report along the path: see GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"none","description":"Each ok path as a line, the alternatives' too, from where the origin meets the network to where the destination does. GeoJSON, an encoded polyline at 6 decimals, or none, the default. Simplified to within 1 m of the roads, for a map and for storing with the distance.","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["destinations","origins","profile"],"additionalProperties":false},"example":{"destinations":[{"point":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"}}],"origins":[{"id":"skolvagen","point":{"lat":63.720434,"lon":19.348226,"street":"Skolvägen"}},{"id":"norrgatan","point":{"lat":63.724508,"lon":19.339523,"street":"Norrgatan"}},{"id":"wrong-street","point":{"lat":63.716325,"lon":19.35388,"street":"Skolvägen"}},{"id":"no-road","point":{"lat":63.706412,"lon":19.339788,"street":"Ödemarken"}},{"id":"disconnected","point":{"lat":63.725224,"lon":19.282034,"street":"Skogsbyvägen"}},{"point":{"lat":63.719246,"lon":19.301609}}],"profile":"walk","facts":["fast_road","unlit_outside_built_up","speed_limit"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"avoid":{"anyOf":[{"type":"object","properties":{"stretches":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"matched":{"type":"boolean","description":"Whether the stretch matched a road. false: none lies along it (drawn where NVDB no longer has a road): redraw it."}},"required":["matched"],"additionalProperties":false}},{"type":"null"}],"description":"Per avoid.stretches entry, in order; null when none was sent."},"values":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"between":{"type":"array","items":{"type":"object","properties":{"point":{"description":"Where the point met the road: the nearest point of a piece with the value.","$ref":"#/components/schemas/Point"},"snap_m":{"type":"number","description":"Straight-line metres from the point sent to there."}},"required":["point","snap_m"],"additionalProperties":false},"description":"Where each of the two points met the road, in the order sent."},"roads":{"type":"array","items":{"type":"object","properties":{"ref":{"anyOf":[{"description":"A stretch of road: an NVDB link sequence id and the part of it covered, as measures 0 to 1 in the direction travelled. It names the same stretch in every network snapshot that holds the link (ADR-0013).","$ref":"#/components/schemas/RoadRef"},{"type":"null"}],"description":"The stretch kept off: an NVDB link and the part of it, measures ascending; null on a road with no NVDB link."},"length_m":{"type":"number","description":"Metres of the stretch."}},"required":["ref","length_m"],"additionalProperties":false},"description":"The road kept off between the points, from the first point's end of the run to the second's: the run's pieces and those beside it, each whole."},"length_m":{"type":"number","description":"Metres of road kept off, all of roads together."}},"required":["between","roads","length_m"],"additionalProperties":false,"description":"What an avoid.values entry with between matched."},{"type":"null"}]}},{"type":"null"}],"description":"Per avoid.values entry, in order: with between, the road it matched, to check against the closure's wording; null for an entry without between. null when no avoid.values was sent."}},"required":["stretches","values"],"additionalProperties":false},{"type":"null"}],"description":"What avoid matched; null without avoid."},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"description":"The id sent with it, or null.","type":["string","null"]},"snap_m":{"description":"Straight-line metres from the point sent to where it meets the profile's network; not included in distance_m. null when no road of the network lies within 500 m: the status is then no_road_near.","type":["number","null"]},"street_matched":{"description":"true when the point met a road of the street sent, false when it met the nearest road instead. null when no street was sent or no road is near.","type":["boolean","null"]}},"required":["id","snap_m","street_matched"],"additionalProperties":false},"description":"Where each destination met the network, in the order sent, ignoring avoid. A destination with snap_m null answers every origin no_road_near."},"results":{"type":"array","items":{"type":"object","properties":{"id":{"description":"The id sent with this result's origin, or null.","type":["string","null"]},"destination_id":{"description":"The id sent with this result's destination, or null.","type":["string","null"]},"destination_index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"This result's destination, its index in destinations from 0."},"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"alternatives":{"anyOf":[{"type":"object","properties":{"unrestricted":{"$ref":"#/components/schemas/Path"}},"additionalProperties":false},{"type":"null"}],"description":"The alternatives answered, by label: unrestricted, the path without avoid (the shortest, or with prefer the least costly, of all). null when none was."}},"required":["id","destination_id","destination_index","status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","alternatives"],"additionalProperties":false}},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["avoid","destinations","results","provenance"],"additionalProperties":false},"example":{"avoid":null,"destinations":[{"id":null,"snap_m":14.97,"street_matched":true}],"results":[{"id":"skolvagen","destination_id":null,"destination_index":0,"status":"ok","distance_m":300,"duration_s":216,"facts":{"fast_road":0,"unlit_outside_built_up":0,"speed_limit":{"40":300}},"avoid_m":null,"snap_m":11.96,"street_matched":true,"destination_snap_m":14.97,"destination_street_matched":true,"blocked_by":null,"alternatives":null},{"id":"norrgatan","destination_id":null,"destination_index":0,"status":"ok","distance_m":560,"duration_s":403,"facts":{"fast_road":0,"unlit_outside_built_up":0,"speed_limit":{"40":560}},"avoid_m":null,"snap_m":11.98,"street_matched":true,"destination_snap_m":14.97,"destination_street_matched":true,"blocked_by":null,"alternatives":null},{"id":"wrong-street","destination_id":null,"destination_index":0,"status":"ok","distance_m":590,"duration_s":425,"facts":{"fast_road":0,"unlit_outside_built_up":0,"speed_limit":{"40":590}},"avoid_m":null,"snap_m":450.44,"street_matched":true,"destination_snap_m":14.97,"destination_street_matched":true,"blocked_by":null,"alternatives":null},{"id":"no-road","destination_id":null,"destination_index":0,"status":"no_road_near","distance_m":null,"duration_s":null,"facts":{},"avoid_m":null,"snap_m":null,"street_matched":null,"destination_snap_m":14.97,"destination_street_matched":true,"blocked_by":null,"alternatives":null},{"id":"disconnected","destination_id":null,"destination_index":0,"status":"no_path","distance_m":null,"duration_s":null,"facts":{},"avoid_m":null,"snap_m":9.98,"street_matched":true,"destination_snap_m":14.97,"destination_street_matched":true,"blocked_by":null,"alternatives":null},{"id":null,"destination_id":null,"destination_index":0,"status":"ok","distance_m":2310,"duration_s":1663,"facts":{"fast_road":1400,"unlit_outside_built_up":1400,"speed_limit":{"40":910,"70":1400}},"avoid_m":null,"snap_m":20.02,"street_matched":null,"destination_snap_m":14.97,"destination_street_matched":true,"blocked_by":null,"alternatives":null}],"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"walk","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/nearest":{"post":{"operationId":"findNearest","summary":"What is nearest each point along the network","description":"For each point, the nearest places of a kind, bus stops today, by the profile's distance along the network, each with its metres and free-flow seconds. Use it instead of POST /v1/distances when you do not know the destinations: it finds them within max_distance_m. It costs a calculation per point.","tags":["routing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["bus_stop"],"description":"What to find: 'bus_stop', NVDB's bus stops (hållplatser) of the network snapshot."},"points":{"minItems":1,"maxItems":1000,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the point, echoed back.","type":"string","maxLength":64},"point":{"$ref":"#/components/schemas/StreetPoint"}},"required":["point"],"additionalProperties":false}},"profile":{"default":"walk","description":"The profile whose network and distance measure what is nearest (GET /v1/profiles); walk unless given.","$ref":"#/components/schemas/Profile"},"limit":{"default":3,"description":"How many per point, nearest by the profile's distance first (1-5, default 3).","type":"integer","minimum":1,"maximum":5},"max_distance_m":{"default":2000,"description":"Only what is within this many metres along the network by the profile, not in a straight line (1-5000, default 2000).","type":"number","minimum":1,"maximum":5000},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["kind","points"],"additionalProperties":false},"example":{"kind":"bus_stop","points":[{"id":"unrefuged","point":{"lat":63.733246,"lon":19.344314,"street":"Passvägen"}},{"id":"skolvagen","point":{"lat":63.720434,"lon":19.348226,"street":"Skolvägen"}},{"id":"river-bank","point":{"lat":63.716325,"lon":19.35388,"street":"Åvägen"}}],"profile":"walk","limit":2}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"]},"origin":{"description":"Where the point met the profile's network; snap_m null when it met none, and nearest is then [].","$ref":"#/components/schemas/SnapResult"},"nearest":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["bus_stop"],"description":"What to find: 'bus_stop', NVDB's bus stops (hållplatser) of the network snapshot."},"id":{"type":"string","description":"Its id in the network snapshot."},"name":{"type":["string","null"]},"point":{"description":"Its position.","$ref":"#/components/schemas/Point"},"distance_m":{"type":"number","description":"Metres along the network by the profile, between where the point and it meet the network."},"duration_s":{"description":"Free-flow seconds of that route by the profile.","type":["number","null"]}},"required":["kind","id","name","point","distance_m","duration_s"],"additionalProperties":false},"description":"Nearest first; [] when none is within max_distance_m or the point met no road."}},"required":["id","origin","nearest"],"additionalProperties":false}},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["results","provenance"],"additionalProperties":false},"example":{"results":[{"id":"unrefuged","origin":{"snap_m":20.02,"street_matched":true},"nearest":[{"kind":"bus_stop","id":"synr-stop-2","name":"Norrgatan","point":{"lat":63.7245493,"lon":19.3405435},"distance_m":1100,"duration_s":792},{"kind":"bus_stop","id":"synr-stop-1","name":"Ruttby skola","point":{"lat":63.7205328,"lon":19.342563},"distance_m":1430,"duration_s":1030}]},{"id":"skolvagen","origin":{"snap_m":11.96,"street_matched":true},"nearest":[{"kind":"bus_stop","id":"synr-stop-1","name":"Ruttby skola","point":{"lat":63.7205328,"lon":19.342563},"distance_m":280,"duration_s":202},{"kind":"bus_stop","id":"synr-stop-2","name":"Norrgatan","point":{"lat":63.7245493,"lon":19.3405435},"distance_m":810,"duration_s":583}]},{"id":"river-bank","origin":{"snap_m":20.03,"street_matched":true},"nearest":[{"kind":"bus_stop","id":"synr-stop-3","name":"Åbacken","point":{"lat":63.710061,"lon":19.3529152},"distance_m":700,"duration_s":504},{"kind":"bus_stop","id":"synr-stop-1","name":"Ruttby skola","point":{"lat":63.7205328,"lon":19.342563},"distance_m":1020,"duration_s":734}]}],"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"walk","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/matrix":{"post":{"operationId":"buildMatrix","summary":"An origins × destinations matrix","description":"Metres and free-flow seconds along the network from every origin to every destination, as grids with a status per cell; with avoid or prefer each cell is the path POST /v1/distances answers for it. Use it for a table of travel times between many places; for each path's facts or line, use POST /v1/distances. It costs at most a calculation per two cells, rounded up; Metering has the formula.","tags":["routing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"origins":{"minItems":1,"maxItems":100,"type":"array","items":{"$ref":"#/components/schemas/StreetPoint"},"description":"The points to measure from: the matrix's rows."},"destinations":{"minItems":1,"maxItems":100,"type":"array","items":{"$ref":"#/components/schemas/StreetPoint"},"description":"The points to measure to: the matrix's columns."},"profile":{"$ref":"#/components/schemas/Profile"},"vehicle":{"$ref":"#/components/schemas/Vehicle"},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["origins","destinations","profile"],"additionalProperties":false,"description":"A matrix: origins × destinations at most 625 cells synchronously, refused 413 too_large above it; up to 10000 as a job (POST /v1/jobs)."},"example":{"origins":[{"lat":63.72059,"lon":19.342166},{"lat":63.720434,"lon":19.348226},{"lat":63.675987,"lon":19.335126}],"destinations":[{"lat":63.724508,"lon":19.339523},{"lat":63.718529,"lon":19.418702},{"lat":63.725224,"lon":19.282034}],"profile":"car_fastest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"distances_m":{"type":"array","items":{"type":"array","items":{"type":["number","null"]}},"description":"[origin][destination]: metres along the network, null where no path joins them."},"durations_s":{"type":"array","items":{"type":"array","items":{"type":["number","null"]}},"description":"[origin][destination]: seconds at free flow, null where no path joins them."},"statuses":{"type":"array","items":{"type":"array","items":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."}},"description":"[origin][destination]: each cell's status, as a path's."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["distances_m","durations_s","statuses","provenance"],"additionalProperties":false},"example":{"distances_m":[[560,3790,null],[860,3490,null],[null,null,null]],"durations_s":[[50,169,null],[77,142,null],[null,null,null]],"statuses":[["ok","ok","no_path"],["ok","ok","no_path"],["no_road_near","no_road_near","no_road_near"]],"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"car_fastest","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/reach":{"post":{"operationId":"measureReach","summary":"What can be reached within a budget","description":"The area reachable from a point within each budget, seconds of travel or metres along the network, as one polygon per budget. Use it to show what lies within reach; to measure to places you know, use POST /v1/distances. It costs a calculation per budget value.","tags":["routing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"origin":{"$ref":"#/components/schemas/StreetPoint"},"profile":{"$ref":"#/components/schemas/Profile"},"budget":{"anyOf":[{"type":"object","properties":{"duration_s":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"number","exclusiveMinimum":0,"maximum":7200},"description":"Seconds of travel, one area per value: free flow by car or hgv, the profile's own pace on foot or by bike. Refused on car_shortest, which takes metres."}},"required":["duration_s"],"additionalProperties":false},{"type":"object","properties":{"distance_m":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"number","exclusiveMinimum":0,"maximum":100000},"description":"Metres along the network, one area per value, on any profile."}},"required":["distance_m"],"additionalProperties":false}],"description":"How far to reach: duration_s or distance_m, one list or the other."},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["origin","profile","budget"],"additionalProperties":false},"example":{"origin":{"lat":63.72059,"lon":19.342166},"profile":"walk","budget":{"duration_s":[120,300]}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"areas":{"type":"array","items":{"type":"object","properties":{"duration_s":{"description":"This area's budget in seconds when the budget was duration_s; else null.","type":["number","null"]},"distance_m":{"description":"This area's budget in metres when the budget was distance_m; else null.","type":["number","null"]},"geometry":{"type":"object","properties":{"type":{"type":"string","const":"Polygon"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"number"}}}}},"required":["type","coordinates"],"additionalProperties":false}},"required":["duration_s","distance_m","geometry"],"additionalProperties":false}},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["areas","provenance"],"additionalProperties":false},"example":{"areas":[{"duration_s":120,"distance_m":null,"geometry":{"type":"Polygon","coordinates":[[[19.338783582223183,63.71902855423934],[19.347314732223182,63.715174854239336],[19.348141132223184,63.72054395423933],[19.345388732223185,63.722425204239336],[19.342360632223183,63.72251670423934],[19.34225985,63.722519750000004],[19.33923175,63.72261105],[19.33895725,63.7208213],[19.33882685,63.7199712],[19.33881315,63.7198817],[19.3386828,63.7190316],[19.338783582223183,63.71902855423934]]]}},{"duration_s":300,"distance_m":null,"geometry":{"type":"Polygon","coordinates":[[[19.3331758,63.72279355],[19.3329017,63.7210038],[19.3357991,63.7200625],[19.33882685,63.7199712],[19.33881315,63.7198817],[19.3386828,63.7190316],[19.338783582223183,63.71902855423934],[19.347314732223182,63.715174854239336],[19.348141132223184,63.72054395423933],[19.345388732223185,63.722425204239336],[19.345562800000003,63.72421799999999],[19.342940300000002,63.7269493],[19.34143285,63.72703975],[19.3395066,63.7244008],[19.33923175,63.72261105],[19.336204,63.72270235],[19.3331758,63.72279355]]]}}],"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"walk","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/trace":{"post":{"operationId":"matchTrace","summary":"The roads a GPS trace travelled, with facts","description":"A GPS trace or a drawn line matched to the road network: the stretches travelled, with their road references and the facts asked for, summed. Use it for a path already travelled; for the path to travel, use POST /v1/route. It costs one calculation.","tags":["routing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"points":{"minItems":2,"maxItems":2000,"type":"array","items":{"$ref":"#/components/schemas/TracePoint"}},"profile":{"default":"car_shortest","$ref":"#/components/schemas/Profile"},"gps_accuracy_m":{"default":20,"description":"How far, in metres, a fix may lie from the road it was recorded on.","type":"number","exclusiveMinimum":0,"maximum":200},"facts":{"description":"The facts to report along the path: see GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"geojson","description":"How lines are returned: GeoJSON, an encoded polyline at 6 decimals, or not at all.","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["points"],"additionalProperties":false},"example":{"points":[{"lat":63.71824,"lon":19.423726,"time":"2026-10-13T07:00:00.000Z"},{"lat":63.718058,"lon":19.429781,"time":"2026-10-13T07:01:00.000Z"},{"lat":63.717876,"lon":19.435836,"time":"2026-10-13T07:02:00.000Z"},{"lat":63.717804,"lon":19.437852,"time":"2026-10-13T07:03:00.000Z"}],"profile":"car_shortest","facts":["speed_limit","road_number","traffic_safety_class"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","not_matched"],"description":"'ok' when the trace was matched to the network; 'not_matched' when no path along it was found."},"distance_m":{"description":"Metres along the network of the path matched; null when not matched.","type":["number","null"]},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path matched."},"geometry":{"description":"The whole matched path as one line, in the format asked; null when not matched. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["status","distance_m","segments","facts","provenance"],"additionalProperties":false},"example":{"status":"ok","distance_m":700,"segments":[{"length_m":250,"ref":"0:17778@0.166714-1.000000","facts":{"speed_limit":"80","road_number":"364","traffic_safety_class":"god"},"geometry":{"type":"LineString","coordinates":[[19.423726,63.718239],[19.428771,63.718084]]}},{"length_m":300,"ref":"0:17779@0.000000-1.000000","facts":{"speed_limit":"60","road_number":"364","traffic_safety_class":"mindre god"},"geometry":{"type":"LineString","coordinates":[[19.428771,63.718084],[19.434825,63.717898]]}},{"length_m":150,"ref":"0:17780@0.000000-0.749966","facts":{"speed_limit":"60","road_number":"364","traffic_safety_class":"mindre god"},"geometry":{"type":"LineString","coordinates":[[19.434825,63.717898],[19.437852,63.717804]]}}],"facts":{"speed_limit":{"60":450,"80":250},"road_number":{"364":700},"traffic_safety_class":{"god":250,"mindre god":450}},"geometry":{"type":"LineString","coordinates":[[19.423726,63.718239],[19.428771,63.718084],[19.434825,63.717898],[19.437852,63.717804]]},"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:00:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"car_shortest","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/optimize":{"post":{"operationId":"optimizeRoutes","summary":"Plan vehicles' routes over stops and shipments","description":"Which vehicle visits which stops and shipments, in what order and at what time, each leg timed on the network as POST /v1/route answers it. A stop or shipment no vehicle can take is answered in unassigned with a reason, never refused. To time a plan you already have, use POST /v1/optimize/check; above the synchronous limits, send the same input as a job.","tags":["planning"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"vehicles":{"minItems":1,"maxItems":5,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the vehicle, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"start":{"description":"Where the vehicle starts: a depot, a garage.","$ref":"#/components/schemas/StreetPoint"},"end":{"description":"Where it must end; left out, its route ends at its last task.","$ref":"#/components/schemas/StreetPoint"},"capacity":{"description":"What the vehicle carries at most, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"dimensions":{"description":"The vehicle's weight, axle load, height, width, length and hazmat, on a profile whose takes_vehicle is true in GET /v1/profiles (hgv). A road whose recorded limit is below it is not used for this vehicle. The plan's matrix is computed once per distinct dimensions sent.","$ref":"#/components/schemas/Vehicle"},"time_window":{"description":"When the vehicle is available: it leaves start no earlier and reaches end no later. Left out, any time: its visits' arrival and departure are null, and only the _s fields time them.","type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false},"max_travel_s":{"description":"At most this many seconds driving, from start to end; waiting and service not counted.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings: what this vehicle can do.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","start"],"additionalProperties":false}},"stops":{"default":[],"maxItems":25,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the stop, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"pickup":{"description":"Loaded at this stop and carried to the vehicle's end (passengers boarding, bins emptied into the truck), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"delivery":{"description":"Loaded at the vehicle's start and set down here (meals, parcels from the depot), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","at"],"additionalProperties":false,"description":"A place one vehicle visits once. For what is picked up at one place and set down at another, send a shipment."}},"shipments":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the shipment, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"pickup":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where the amount is picked up."},"delivery":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where it is set down: by the same vehicle, after the pickup."},"amount":{"description":"What travels from pickup to delivery, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"max_ride_s":{"description":"At most this many seconds from leaving the pickup to arriving at the delivery. Checked on the plan made without it: a shipment over it is taken out and answered unassigned_max_ride, never re-planned.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","pickup","delivery"],"additionalProperties":false}},"objective":{"default":"duration","description":"What is made least, after serving as many stops and shipments as the vehicles can: 'duration', the vehicles' driving time together; 'vehicles_then_duration', the vehicles used first, then their driving time.","type":"string","enum":["duration","vehicles_then_duration"]},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"profile":{"description":"How the legs are weighed: see GET /v1/profiles. A plan's durations are each leg's as /v1/route answers it.","$ref":"#/components/schemas/Profile"},"facts":{"description":"The facts to report over each leg: see GET /v1/facts. With facts or geometry, the answer has legs.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"none","description":"Each leg's line and its stretches with road references: GeoJSON, an encoded polyline at 6 decimals, or not at all (the default).","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["vehicles","profile"],"additionalProperties":false,"description":"A plan: vehicles, and stops or shipments for them. Above the synchronous limits the call is refused; send the same input to POST /v1/jobs with operation 'optimize'."},"example":{"vehicles":[{"id":"bus1","start":{"lat":63.720434,"lon":19.348226,"street":"Skolvägen"},"end":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"capacity":{"seats":20},"time_window":{"start":"2026-10-13T07:00:00.000Z","end":"2026-10-13T07:01:00.000Z"}}],"stops":[{"id":"s1","at":{"lat":63.724508,"lon":19.339523,"street":"Norrgatan"},"pickup":{"seats":4},"service_s":30},{"id":"s2","at":{"lat":63.718529,"lon":19.418702,"street":"Östbyvägen"},"pickup":{"seats":2},"service_s":30},{"id":"s3","at":{"lat":63.725224,"lon":19.282034,"street":"Skogsbyvägen"},"pickup":{"seats":1}}],"profile":"car_fastest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"routes":{"type":"array","items":{"type":"object","properties":{"vehicle_id":{"type":"string","description":"The vehicle's id, as sent."},"visits":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["start","stop","pickup","delivery","end"],"description":"'start' and 'end' are the vehicle's own; 'stop' a stop; 'pickup' and 'delivery' a shipment's two places."},"id":{"description":"The stop's or shipment's id; null at the vehicle's start and end.","type":["string","null"]},"snap_m":{"description":"Straight-line metres from the point sent to where it meets the profile's network; not included in distance_m. null when no road of the network lies within 500 m: the status is then no_road_near.","type":["number","null"]},"street_matched":{"description":"Whether the place met the road of the street sent; null without a street, and at a place sent as an address id (the answer holds nothing of the address).","type":["boolean","null"]},"arrival":{"description":"RFC 3339, in the offset of the vehicle's time_window; null without one.","type":["string","null"]},"departure":{"description":"RFC 3339: arrival, any wait and service_s later; null without a time_window.","type":["string","null"]},"arrival_s":{"type":"number","description":"Seconds from leaving start to arriving here."},"waiting_s":{"type":"number","description":"Seconds waited here for a time window to open."},"service_s":{"type":"number","description":"Seconds of service here, as sent."},"load":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"On board when leaving here, per dimension named in the request."},"distance_m":{"type":"number","description":"Metres along the network from the previous visit (0 at start)."},"duration_s":{"type":"number","description":"Seconds driving from the previous visit at free flow (0 at start)."}},"required":["kind","id","snap_m","street_matched","arrival","departure","arrival_s","waiting_s","service_s","load","distance_m","duration_s"],"additionalProperties":false},"description":"In the order driven: start, each task, end (where one was sent)."},"legs":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"},"description":"The leg's stretches with their road references (ADR-0013), in order; with geometry, each with its line."}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","segments"],"additionalProperties":false}},{"type":"null"}],"description":"With facts or geometry, one leg from each visit to the next, in order: visits.length − 1 of them. null without either."},"distance_m":{"type":"number","description":"Metres along the network over the whole route."},"duration_s":{"type":"number","description":"Seconds from leaving start to the last visit: driving, waiting and service."},"waiting_s":{"type":"number","description":"Seconds waited for time windows to open, over the route."},"service_s":{"type":"number","description":"Seconds of service, over the route."}},"required":["vehicle_id","visits","legs","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false},"description":"One per vehicle used, in the order of vehicles sent; a vehicle given no task is left out."},"unassigned":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["stop","shipment"],"description":"Whether id names a stop or a shipment."},"id":{"type":"string","description":"The stop's or shipment's id, as sent."},"reason":{"type":"string","enum":["unassigned_address_id","unassigned_unreachable","unassigned_skills","unassigned_capacity","unassigned_time_window","unassigned_max_travel","unassigned_max_ride","unassigned_no_room"],"description":"Why, as a code from ERRORS: its page at /errors/{code} says what to change."},"message":{"type":"string","description":"The code's message from ERRORS, saying what to change; with unassigned_unreachable, the message for its cause."},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With unassigned_unreachable where only roads kept off by avoid reach the task: what in avoid is in the way, as route's blocked_by. null otherwise."},"unreachable":{"description":"With unassigned_unreachable: which place, where it met the network and why no vehicle reaches it. Absent with every other reason. met is null for a place sent as an address id: the answer holds no point of the address.","$ref":"#/components/schemas/Unreachable"},"unresolved":{"description":"With unassigned_address_id: which place's address id resolved to no address served, and why. Absent with every other reason.","$ref":"#/components/schemas/Unresolved"}},"required":["kind","id","reason","message","blocked_by"],"additionalProperties":false},"description":"Every stop and shipment no vehicle takes, with why. [] when every one is in a route."},"totals":{"type":"object","properties":{"vehicles_used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Vehicles with at least one task."},"distance_m":{"type":"number","description":"Metres, every route's together."},"duration_s":{"type":"number","description":"Seconds, every route's duration_s together."},"waiting_s":{"type":"number","description":"Seconds waited, every route's together."},"service_s":{"type":"number","description":"Seconds of service, every route's together."}},"required":["vehicles_used","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false,"description":"The routes summed."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["routes","unassigned","totals","provenance"],"additionalProperties":false},"example":{"routes":[{"vehicle_id":"bus1","visits":[{"kind":"start","id":null,"snap_m":11.96,"street_matched":true,"arrival":"2026-10-13T07:00:00.000Z","departure":"2026-10-13T07:01:00.000Z","arrival_s":0,"waiting_s":0,"service_s":0,"load":{"seats":0},"distance_m":0,"duration_s":0},{"kind":"stop","id":"s2","snap_m":15.02,"street_matched":true,"arrival":"2026-10-13T07:02:00.000Z","departure":"2026-10-13T07:03:00.000Z","arrival_s":142,"waiting_s":0,"service_s":30,"load":{"seats":2},"distance_m":3490,"duration_s":142.3},{"kind":"stop","id":"s1","snap_m":11.98,"street_matched":true,"arrival":"2026-10-13T07:04:00.000Z","departure":"2026-10-13T07:05:00.000Z","arrival_s":392,"waiting_s":0,"service_s":30,"load":{"seats":6},"distance_m":4350,"duration_s":219.7},{"kind":"end","id":null,"snap_m":14.97,"street_matched":true,"arrival":"2026-10-13T07:06:00.000Z","departure":"2026-10-13T07:07:00.000Z","arrival_s":472,"waiting_s":0,"service_s":0,"load":{"seats":6},"distance_m":560,"duration_s":50.4}],"legs":null,"distance_m":8400,"duration_s":472,"waiting_s":0,"service_s":60}],"unassigned":[{"kind":"stop","id":"s3","reason":"unassigned_unreachable","message":"This place was met on the road in unreachable.met, but the network joins no path from a vehicle's start to it (in a checked plan, from the visit before or to the visit after it), or only a ferry does: the road lies on an island of the profile's network. Send a point on a road joined to the rest, or a vehicle that starts where it can reach it.","blocked_by":null,"unreachable":{"place":"stop","met":{"point":{"lat":63.72523,"lon":19.2818325},"snap_m":9.98,"street_matched":true,"ref":"0:17763@0.500043-0.500043","road":{"name":"Skogsbyvägen","number":null}},"cause":"no_path"}}],"totals":{"vehicles_used":1,"distance_m":8400,"duration_s":472,"waiting_s":0,"service_s":60},"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:08:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"car_fastest","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/optimize/check":{"post":{"operationId":"checkPlan","summary":"Check a given plan of vehicles' routes","description":"Your own routes, timed and loaded on the network as POST /v1/optimize times its own and in its shapes, so the two compare field for field. What a route breaks is answered in violations and a task in no route in unassigned: a broken plan is answered, never refused. To have the plan made for you, use POST /v1/optimize; above the synchronous limits, send the same input as a job.","tags":["planning"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"vehicles":{"minItems":1,"maxItems":5,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the vehicle, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"start":{"description":"Where the vehicle starts: a depot, a garage.","$ref":"#/components/schemas/StreetPoint"},"end":{"description":"Where it must end; left out, its route ends at its last task.","$ref":"#/components/schemas/StreetPoint"},"capacity":{"description":"What the vehicle carries at most, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"dimensions":{"description":"The vehicle's weight, axle load, height, width, length and hazmat, on a profile whose takes_vehicle is true in GET /v1/profiles (hgv). A road whose recorded limit is below it is not used for this vehicle. The plan's matrix is computed once per distinct dimensions sent.","$ref":"#/components/schemas/Vehicle"},"time_window":{"description":"When the vehicle is available: it leaves start no earlier and reaches end no later. Left out, any time: its visits' arrival and departure are null, and only the _s fields time them.","type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false},"max_travel_s":{"description":"At most this many seconds driving, from start to end; waiting and service not counted.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings: what this vehicle can do.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","start"],"additionalProperties":false}},"stops":{"default":[],"maxItems":25,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the stop, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"pickup":{"description":"Loaded at this stop and carried to the vehicle's end (passengers boarding, bins emptied into the truck), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"delivery":{"description":"Loaded at the vehicle's start and set down here (meals, parcels from the depot), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","at"],"additionalProperties":false,"description":"A place one vehicle visits once. For what is picked up at one place and set down at another, send a shipment."}},"shipments":{"default":[],"maxItems":12,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the shipment, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"pickup":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where the amount is picked up."},"delivery":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where it is set down: by the same vehicle, after the pickup."},"amount":{"description":"What travels from pickup to delivery, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"max_ride_s":{"description":"At most this many seconds from leaving the pickup to arriving at the delivery. Checked on the plan made without it: a shipment over it is taken out and answered unassigned_max_ride, never re-planned.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","pickup","delivery"],"additionalProperties":false}},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"profile":{"description":"How the legs are weighed: see GET /v1/profiles. A plan's durations are each leg's as /v1/route answers it.","$ref":"#/components/schemas/Profile"},"facts":{"description":"The facts to report over each leg: see GET /v1/facts. With facts or geometry, the answer has legs.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"none","description":"Each leg's line and its stretches with road references: GeoJSON, an encoded polyline at 6 decimals, or not at all (the default).","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"},"routes":{"maxItems":5,"type":"array","items":{"type":"object","properties":{"vehicle_id":{"type":"string","minLength":1,"maxLength":64,"description":"The vehicle's id, as sent in vehicles."},"visits":{"maxItems":49,"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["stop","pickup","delivery"],"description":"'stop' for a stop; 'pickup' or 'delivery' for a shipment's two places."},"id":{"type":"string","minLength":1,"maxLength":64,"description":"The stop's or shipment's id, as sent in stops or shipments."}},"required":["kind","id"],"additionalProperties":false},"description":"The vehicle's tasks in the order it visits them, its start and end left out: they are the vehicle's own. The kind and id of an optimize answer's visits, without start and end, are a plan."}},"required":["vehicle_id","visits"],"additionalProperties":false},"description":"The plan to check: per vehicle, its visits in order. A task in no route is answered in unassigned; an id naming nothing sent, a visit of the wrong kind, a task visited twice or a vehicle twice is refused 400 invalid_request."}},"required":["vehicles","profile","routes"],"additionalProperties":false,"description":"Vehicles, stops and shipments as POST /v1/optimize takes them, and routes: the plan to check."},"example":{"vehicles":[{"id":"bus1","start":{"lat":63.720434,"lon":19.348226,"street":"Skolvägen"},"end":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"capacity":{"seats":20},"time_window":{"start":"2026-10-13T07:00:00.000Z","end":"2026-10-13T07:01:00.000Z"}}],"stops":[{"id":"s1","at":{"lat":63.724508,"lon":19.339523,"street":"Norrgatan"},"pickup":{"seats":4},"service_s":30},{"id":"s2","at":{"lat":63.718529,"lon":19.418702,"street":"Östbyvägen"},"pickup":{"seats":2},"service_s":30},{"id":"s3","at":{"lat":63.725224,"lon":19.282034,"street":"Skogsbyvägen"},"pickup":{"seats":1}}],"profile":"car_fastest","routes":[{"vehicle_id":"bus1","visits":[{"kind":"stop","id":"s1"},{"kind":"stop","id":"s2"}]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"feasible":{"type":"boolean","description":"true when no route breaks anything and every task is in a route: violations and unassigned are both []."},"routes":{"type":"array","items":{"type":"object","properties":{"vehicle_id":{"type":"string","description":"The vehicle's id, as sent."},"visits":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["start","stop","pickup","delivery","end"],"description":"'start' and 'end' are the vehicle's own; 'stop' a stop; 'pickup' and 'delivery' a shipment's two places."},"id":{"description":"The stop's or shipment's id; null at the vehicle's start and end.","type":["string","null"]},"snap_m":{"description":"Straight-line metres from the point sent to where it meets the profile's network; not included in distance_m. null when no road of the network lies within 500 m: the status is then no_road_near.","type":["number","null"]},"street_matched":{"description":"Whether the place met the road of the street sent; null without a street, and at a place sent as an address id (the answer holds nothing of the address).","type":["boolean","null"]},"arrival":{"description":"RFC 3339, in the offset of the vehicle's time_window; null without one.","type":["string","null"]},"departure":{"description":"RFC 3339: arrival, any wait and service_s later; null without a time_window.","type":["string","null"]},"arrival_s":{"type":"number","description":"Seconds from leaving start to arriving here."},"waiting_s":{"type":"number","description":"Seconds waited here for a time window to open."},"service_s":{"type":"number","description":"Seconds of service here, as sent."},"load":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"On board when leaving here, per dimension named in the request."},"distance_m":{"type":"number","description":"Metres along the network from the previous visit (0 at start)."},"duration_s":{"type":"number","description":"Seconds driving from the previous visit at free flow (0 at start)."}},"required":["kind","id","snap_m","street_matched","arrival","departure","arrival_s","waiting_s","service_s","load","distance_m","duration_s"],"additionalProperties":false},"description":"In the order driven: start, each task, end (where one was sent)."},"legs":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","enum":["ok","no_path","ferry_required","no_road_near","avoided_road_required"],"description":"Whether a path joins the two points, and why not: 'ok', a path. 'no_road_near': no road within 500 m of an end. 'no_path': the network joins none. 'ferry_required': only a ferry does. 'avoided_road_required': only what avoid keeps off does, or an end lies on it. Each is a 200 answer, not an error: see \"When there is no path\"."},"distance_m":{"description":"Metres along the network between where the ends meet it; null unless status is 'ok'.","type":["number","null"]},"duration_s":{"description":"Seconds at free flow: the posted limit, capped at 80 km/h on hgv, and 5 km/h on foot. No traffic. null unless status is 'ok'.","type":["number","null"]},"facts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/FactSummary"},"description":"The facts asked for, over the path."},"geometry":{"description":"The path as one line, in the format asked; null unless status is 'ok'. Left out with geometry 'none'.","anyOf":[{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"LineString"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"number"}}}},"required":["type","coordinates"],"additionalProperties":false},{"type":"string"}]},{"type":"null"}]},"avoid_m":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"With avoid.stretches, the path's metres on each stretch, in the order sent, 0 for one it does not touch: metres, never a verdict. null without stretches, or unless status is 'ok'."},"snap_m":{"description":"Straight-line metres from the origin's point to where this path starts; without a path, to where the origin met the network. It is not included in distance_m. null only when the origin met no road within 500 m (no_road_near).","type":["number","null"]},"street_matched":{"description":"With a street sent with the origin: true when this path starts on a road of that name, false when on another. null when no street was sent, or the origin met no road.","type":["boolean","null"]},"destination_snap_m":{"description":"The same as snap_m, for the destination.","type":["number","null"]},"destination_street_matched":{"description":"The same as street_matched, for the destination.","type":["boolean","null"]},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With 'avoided_road_required', the avoid entries in the way; null otherwise. Of those the path without avoid runs along or crosses, each that, lifted alone, lets a path keep off the rest; where none alone does, all of them. Where an end meets the network on a road kept off, with no other way on, those that keep off its road."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"},"description":"The leg's stretches with their road references (ADR-0013), in order; with geometry, each with its line."}},"required":["status","distance_m","duration_s","facts","avoid_m","snap_m","street_matched","destination_snap_m","destination_street_matched","blocked_by","segments"],"additionalProperties":false}},{"type":"null"}],"description":"With facts or geometry, one leg from each visit to the next, in order: visits.length − 1 of them. null without either."},"distance_m":{"type":"number","description":"Metres along the network over the whole route."},"duration_s":{"type":"number","description":"Seconds from leaving start to the last visit: driving, waiting and service."},"waiting_s":{"type":"number","description":"Seconds waited for time windows to open, over the route."},"service_s":{"type":"number","description":"Seconds of service, over the route."}},"required":["vehicle_id","visits","legs","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false},"description":"One per vehicle in routes with a visit, in the order of vehicles sent: optimize's route shape, timed and loaded as optimize times and loads a plan."},"unassigned":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["stop","shipment"],"description":"Whether id names a stop or a shipment."},"id":{"type":"string","description":"The stop's or shipment's id, as sent."},"reason":{"type":"string","enum":["unassigned_not_in_plan","unassigned_address_id","unassigned_unreachable"],"description":"Why, as a code from ERRORS. unassigned_not_in_plan: no route places it. unassigned_address_id: its address id resolves to no address served, so its visits leave the route and the rest is checked as sent. unassigned_unreachable: no road joins it to the visit before or after."},"message":{"type":"string","description":"The code's message from ERRORS, saying what to change; with unassigned_unreachable, the message for its cause."},"blocked_by":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["stretches","facts","values"],"description":"The list in avoid the entry was sent in."},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Its index in that list, from 0."},"id":{"description":"The id sent with it; null where none was.","type":["string","null"]}},"required":["field","index","id"],"additionalProperties":false,"description":"An avoid entry in the way."}},{"type":"null"}],"description":"With unassigned_unreachable where only roads kept off by avoid reach the task: what in avoid is in the way, as route's blocked_by. null otherwise."},"unreachable":{"description":"With unassigned_unreachable: which place, where it met the network and why no vehicle reaches it. Absent with every other reason. met is null for a place sent as an address id: the answer holds no point of the address.","$ref":"#/components/schemas/Unreachable"},"unresolved":{"description":"With unassigned_address_id: which place's address id resolved to no address served, and why. Absent with every other reason.","$ref":"#/components/schemas/Unresolved"}},"required":["kind","id","reason","message","blocked_by"],"additionalProperties":false},"description":"Every stop and shipment in no route, with why. [] when the plan places every one."},"violations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["plan_capacity","plan_time_window","plan_vehicle_time_window","plan_skills","plan_max_travel","plan_shipment_order","plan_max_ride"],"description":"What is broken, as a code from ERRORS: its page at /errors/{code} says what to change."},"message":{"type":"string","description":"The code's message from ERRORS."},"vehicle_id":{"type":"string","description":"The vehicle whose route breaks it."},"visit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where: the index in the answered route's visits, 0 being start. It is the visit late or over capacity, the end for the vehicle's time_window and max_travel_s, and a shipment's delivery, else its pickup."},"kind":{"type":"string","enum":["stop","pickup","delivery","end"],"description":"The kind of the visit at that index."},"id":{"description":"The stop's or shipment's id; null at the end.","type":["string","null"]},"dimension":{"description":"With plan_capacity, the dimension over capacity; null otherwise.","type":["string","null"]},"over":{"description":"By how much: seconds late or over for plan_time_window, plan_vehicle_time_window, plan_max_travel and plan_max_ride; the amount over capacity for plan_capacity; null for plan_skills and plan_shipment_order.","type":["number","null"]}},"required":["code","message","vehicle_id","visit","kind","id","dimension","over"],"additionalProperties":false},"description":"Everything the routes break, by route and visit in order. [] when the plan holds."},"totals":{"type":"object","properties":{"vehicles_used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Vehicles with at least one task."},"distance_m":{"type":"number","description":"Metres, every route's together."},"duration_s":{"type":"number","description":"Seconds, every route's duration_s together."},"waiting_s":{"type":"number","description":"Seconds waited, every route's together."},"service_s":{"type":"number","description":"Seconds of service, every route's together."}},"required":["vehicles_used","distance_m","duration_s","waiting_s","service_s"],"additionalProperties":false,"description":"The routes summed."},"provenance":{"$ref":"#/components/schemas/Provenance"}},"required":["feasible","routes","unassigned","violations","totals","provenance"],"additionalProperties":false},"example":{"feasible":false,"routes":[{"vehicle_id":"bus1","visits":[{"kind":"start","id":null,"snap_m":11.96,"street_matched":true,"arrival":"2026-10-13T07:00:00.000Z","departure":"2026-10-13T07:01:00.000Z","arrival_s":0,"waiting_s":0,"service_s":0,"load":{"seats":0},"distance_m":0,"duration_s":0},{"kind":"stop","id":"s1","snap_m":11.98,"street_matched":true,"arrival":"2026-10-13T07:02:00.000Z","departure":"2026-10-13T07:03:00.000Z","arrival_s":77,"waiting_s":0,"service_s":30,"load":{"seats":4},"distance_m":860,"duration_s":77.4},{"kind":"stop","id":"s2","snap_m":15.02,"street_matched":true,"arrival":"2026-10-13T07:04:00.000Z","departure":"2026-10-13T07:05:00.000Z","arrival_s":327,"waiting_s":0,"service_s":30,"load":{"seats":6},"distance_m":4350,"duration_s":219.7},{"kind":"end","id":null,"snap_m":14.97,"street_matched":true,"arrival":"2026-10-13T07:06:00.000Z","departure":"2026-10-13T07:07:00.000Z","arrival_s":526,"waiting_s":0,"service_s":0,"load":{"seats":6},"distance_m":3790,"duration_s":169.3}],"legs":null,"distance_m":9000.1,"duration_s":526,"waiting_s":0,"service_s":60}],"unassigned":[{"kind":"stop","id":"s3","reason":"unassigned_not_in_plan","message":"The plan you sent places this task in no route. Add it to a vehicle's visits, or leave it out of stops and shipments if it is planned elsewhere.","blocked_by":null}],"violations":[],"totals":{"vehicles_used":1,"distance_m":9000.1,"duration_s":526,"waiting_s":0,"service_s":60},"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:08:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"car_fastest","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer was computed on, the body's `provenance.network.id`. Send it as `network: { id }` to answer on the same network later.","schema":{"type":"string"},"required":true},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"That network snapshot is not the one served now, and answers on another snapshot are computed as jobs. Send the same input to POST /v1/jobs with this operation, then read GET /v1/jobs/{id}. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_not_active","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then. Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable","routing_unavailable"]}}}}},"headers":{"Retry-After":{"description":"Sent only with routing_unavailable when another distance calculation is running: seconds to wait before sending the call again. Absent on every other 503 (live_data_unavailable, and routing_unavailable when no road graph or routing engine is available), which a wait does not fix.","schema":{"type":"integer"},"required":false}}}}}},"/v1/jobs":{"post":{"operationId":"submitJob","summary":"Run an operation as a job","description":"Any routing, planning or address-list operation run in the background, with the same input as its synchronous call up to the job limits below, on any network snapshot, retired ones included. Use it above an operation's synchronous limits or for a snapshot no longer served, then read the job with GET /v1/jobs/{id}. Jobs run behind synchronous calls and slow down under load, but never fail for it. A job is checked when it is submitted, an optimize or check_plan job's vehicle starts and ends on the network included, and refused at once as the job would fail; with dry_run: true it is only checked, answered 200 and not queued. An address_list job matches your own list of up to 50000 addresses to the register, one result row per row sent and never a list of candidates. Every row counts as one live call and one address, held when the job is submitted. A list longer than what is left of the day's address limit is 429 quota_exceeded, so split it across days. Its input is deleted when the job ends, and its result within 24 hours.","tags":["jobs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"operation":{"type":"string","const":"route"},"input":{"type":"object","properties":{"origin":{"$ref":"#/components/schemas/StreetPoint"},"destination":{"$ref":"#/components/schemas/StreetPoint"},"via":{"description":"Points the route passes through, in the order sent.","maxItems":10,"type":"array","items":{"$ref":"#/components/schemas/StreetPoint"}},"profile":{"$ref":"#/components/schemas/Profile"},"vehicle":{"$ref":"#/components/schemas/Vehicle"},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"alternatives":{"description":"Other paths to answer beside the main one: 'unrestricted', the path without avoid (what the main path is measured against, e.g. a closure's detour).","maxItems":1,"type":"array","items":{"type":"string","enum":["unrestricted"]}},"facts":{"description":"The facts to report along the path: see GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"geojson","description":"How lines are returned: GeoJSON, an encoded polyline at 6 decimals, or not at all.","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["origin","destination","profile"],"additionalProperties":false},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"distances"},"input":{"type":"object","properties":{"destinations":{"minItems":1,"maxItems":5,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the destination, echoed in each result as destination_id.","type":"string","maxLength":64},"point":{"$ref":"#/components/schemas/StreetPoint"}},"required":["point"],"additionalProperties":false},"description":"The destinations, each answered for every origin. results holds one entry per origin per destination, each origin's together in the order of destinations: results[origin × destinations + destination]. Each result names its destination_id and destination_index."},"origins":{"minItems":1,"maxItems":50000,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the origin, echoed back.","type":"string","maxLength":64},"point":{"$ref":"#/components/schemas/StreetPoint"}},"required":["point"],"additionalProperties":false}},"profile":{"$ref":"#/components/schemas/Profile"},"vehicle":{"$ref":"#/components/schemas/Vehicle"},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"alternatives":{"description":"Other paths to answer beside the main one: 'unrestricted', the path without avoid. With avoid, ['unrestricted'] unless sent; send [] for the main path alone.","maxItems":1,"type":"array","items":{"type":"string","enum":["unrestricted"]}},"facts":{"description":"The facts to report along the path: see GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"none","description":"Each ok path as a line, the alternatives' too, from where the origin meets the network to where the destination does. GeoJSON, an encoded polyline at 6 decimals, or none, the default. Simplified to within 1 m of the roads, for a map and for storing with the distance.","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["destinations","origins","profile"],"additionalProperties":false},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"matrix"},"input":{"type":"object","properties":{"origins":{"minItems":1,"maxItems":100,"type":"array","items":{"$ref":"#/components/schemas/StreetPoint"},"description":"The points to measure from: the matrix's rows."},"destinations":{"minItems":1,"maxItems":100,"type":"array","items":{"$ref":"#/components/schemas/StreetPoint"},"description":"The points to measure to: the matrix's columns."},"profile":{"$ref":"#/components/schemas/Profile"},"vehicle":{"$ref":"#/components/schemas/Vehicle"},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["origins","destinations","profile"],"additionalProperties":false,"description":"A matrix: origins × destinations at most 625 cells synchronously, refused 413 too_large above it; up to 10000 as a job (POST /v1/jobs)."},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"reach"},"input":{"type":"object","properties":{"origin":{"$ref":"#/components/schemas/StreetPoint"},"profile":{"$ref":"#/components/schemas/Profile"},"budget":{"anyOf":[{"type":"object","properties":{"duration_s":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"number","exclusiveMinimum":0,"maximum":7200},"description":"Seconds of travel, one area per value: free flow by car or hgv, the profile's own pace on foot or by bike. Refused on car_shortest, which takes metres."}},"required":["duration_s"],"additionalProperties":false},{"type":"object","properties":{"distance_m":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"number","exclusiveMinimum":0,"maximum":100000},"description":"Metres along the network, one area per value, on any profile."}},"required":["distance_m"],"additionalProperties":false}],"description":"How far to reach: duration_s or distance_m, one list or the other."},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["origin","profile","budget"],"additionalProperties":false},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"trace"},"input":{"type":"object","properties":{"points":{"minItems":2,"maxItems":2000,"type":"array","items":{"$ref":"#/components/schemas/TracePoint"}},"profile":{"default":"car_shortest","$ref":"#/components/schemas/Profile"},"gps_accuracy_m":{"default":20,"description":"How far, in metres, a fix may lie from the road it was recorded on.","type":"number","exclusiveMinimum":0,"maximum":200},"facts":{"description":"The facts to report along the path: see GET /v1/facts. None by default.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"geojson","description":"How lines are returned: GeoJSON, an encoded polyline at 6 decimals, or not at all.","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["points"],"additionalProperties":false},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"nearest"},"input":{"type":"object","properties":{"kind":{"type":"string","enum":["bus_stop"],"description":"What to find: 'bus_stop', NVDB's bus stops (hållplatser) of the network snapshot."},"points":{"minItems":1,"maxItems":50000,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the point, echoed back.","type":"string","maxLength":64},"point":{"$ref":"#/components/schemas/StreetPoint"}},"required":["point"],"additionalProperties":false}},"profile":{"default":"walk","description":"The profile whose network and distance measure what is nearest (GET /v1/profiles); walk unless given.","$ref":"#/components/schemas/Profile"},"limit":{"default":3,"description":"How many per point, nearest by the profile's distance first (1-5, default 3).","type":"integer","minimum":1,"maximum":5},"max_distance_m":{"default":2000,"description":"Only what is within this many metres along the network by the profile, not in a straight line (1-5000, default 2000).","type":"number","minimum":1,"maximum":5000},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["kind","points"],"additionalProperties":false},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"optimize"},"input":{"type":"object","properties":{"vehicles":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the vehicle, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"start":{"description":"Where the vehicle starts: a depot, a garage.","$ref":"#/components/schemas/StreetPoint"},"end":{"description":"Where it must end; left out, its route ends at its last task.","$ref":"#/components/schemas/StreetPoint"},"capacity":{"description":"What the vehicle carries at most, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"dimensions":{"description":"The vehicle's weight, axle load, height, width, length and hazmat, on a profile whose takes_vehicle is true in GET /v1/profiles (hgv). A road whose recorded limit is below it is not used for this vehicle. The plan's matrix is computed once per distinct dimensions sent.","$ref":"#/components/schemas/Vehicle"},"time_window":{"description":"When the vehicle is available: it leaves start no earlier and reaches end no later. Left out, any time: its visits' arrival and departure are null, and only the _s fields time them.","type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false},"max_travel_s":{"description":"At most this many seconds driving, from start to end; waiting and service not counted.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings: what this vehicle can do.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","start"],"additionalProperties":false}},"stops":{"default":[],"maxItems":500,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the stop, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"pickup":{"description":"Loaded at this stop and carried to the vehicle's end (passengers boarding, bins emptied into the truck), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"delivery":{"description":"Loaded at the vehicle's start and set down here (meals, parcels from the depot), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","at"],"additionalProperties":false,"description":"A place one vehicle visits once. For what is picked up at one place and set down at another, send a shipment."}},"shipments":{"default":[],"maxItems":250,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the shipment, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"pickup":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where the amount is picked up."},"delivery":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where it is set down: by the same vehicle, after the pickup."},"amount":{"description":"What travels from pickup to delivery, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"max_ride_s":{"description":"At most this many seconds from leaving the pickup to arriving at the delivery. Checked on the plan made without it: a shipment over it is taken out and answered unassigned_max_ride, never re-planned.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","pickup","delivery"],"additionalProperties":false}},"objective":{"default":"duration","description":"What is made least, after serving as many stops and shipments as the vehicles can: 'duration', the vehicles' driving time together; 'vehicles_then_duration', the vehicles used first, then their driving time.","type":"string","enum":["duration","vehicles_then_duration"]},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"profile":{"description":"How the legs are weighed: see GET /v1/profiles. A plan's durations are each leg's as /v1/route answers it.","$ref":"#/components/schemas/Profile"},"facts":{"description":"The facts to report over each leg: see GET /v1/facts. With facts or geometry, the answer has legs.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"none","description":"Each leg's line and its stretches with road references: GeoJSON, an encoded polyline at 6 decimals, or not at all (the default).","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"}},"required":["vehicles","profile"],"additionalProperties":false},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"check_plan"},"input":{"type":"object","properties":{"vehicles":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the vehicle, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"start":{"description":"Where the vehicle starts: a depot, a garage.","$ref":"#/components/schemas/StreetPoint"},"end":{"description":"Where it must end; left out, its route ends at its last task.","$ref":"#/components/schemas/StreetPoint"},"capacity":{"description":"What the vehicle carries at most, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"dimensions":{"description":"The vehicle's weight, axle load, height, width, length and hazmat, on a profile whose takes_vehicle is true in GET /v1/profiles (hgv). A road whose recorded limit is below it is not used for this vehicle. The plan's matrix is computed once per distinct dimensions sent.","$ref":"#/components/schemas/Vehicle"},"time_window":{"description":"When the vehicle is available: it leaves start no earlier and reaches end no later. Left out, any time: its visits' arrival and departure are null, and only the _s fields time them.","type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false},"max_travel_s":{"description":"At most this many seconds driving, from start to end; waiting and service not counted.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings: what this vehicle can do.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","start"],"additionalProperties":false}},"stops":{"default":[],"maxItems":500,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the stop, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"pickup":{"description":"Loaded at this stop and carried to the vehicle's end (passengers boarding, bins emptied into the truck), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"delivery":{"description":"Loaded at the vehicle's start and set down here (meals, parcels from the depot), per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","at"],"additionalProperties":false,"description":"A place one vehicle visits once. For what is picked up at one place and set down at another, send a shipment."}},"shipments":{"default":[],"maxItems":250,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Your own id for the shipment, echoed in the answer and never stored. Unique among the vehicles, stops and shipments of one request."},"pickup":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where the amount is picked up."},"delivery":{"type":"object","properties":{"at":{"anyOf":[{"$ref":"#/components/schemas/StreetPoint"},{"description":"A place given by its address id instead of coordinates.","$ref":"#/components/schemas/AddressIdPlace"}],"description":"Where: { lat, lon, street? } as every point, or { address_id, as_of? } to route from the address's own point. Each address id resolved is metered as an address read and the call as a live call (kp_live_ keys); a stop whose id resolves to no address served is answered unassigned_address_id, not refused."},"service_s":{"default":0,"description":"Seconds spent at the place, after arrival and any wait.","type":"integer","minimum":0,"maximum":86400},"time_windows":{"description":"When service may start here: one or more windows. Left out, any time within the vehicle's.","minItems":1,"maxItems":4,"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"From (RFC 3339 with offset)."},"end":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Until (RFC 3339 with offset)."}},"required":["start","end"],"additionalProperties":false,"description":"A time window: { start, end }, RFC 3339 with an offset, e.g. '2026-10-12T07:00:00+02:00'."}}},"required":["at"],"additionalProperties":false,"description":"Where it is set down: by the same vehicle, after the pickup."},"amount":{"description":"What travels from pickup to delivery, per dimension you name, e.g. { \"seats\": 4, \"wheelchair\": 1 }: whole numbers. A dimension left out is 0.","type":"object","propertyNames":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"additionalProperties":{"type":"integer","minimum":0,"maximum":1000000}},"max_ride_s":{"description":"At most this many seconds from leaving the pickup to arriving at the delivery. Checked on the plan made without it: a shipment over it is taken out and answered unassigned_max_ride, never re-planned.","type":"integer","exclusiveMinimum":0,"maximum":86400},"skills":{"description":"Your own strings, e.g. 'wheelchair_lift', 'crane': a task goes only to a vehicle with every skill it names.","maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["id","pickup","delivery"],"additionalProperties":false}},"avoid":{"$ref":"#/components/schemas/Avoid"},"prefer":{"description":"Facts to prefer to keep off, on any profile, each once. Left out or [], the profile's own weighing.","maxItems":6,"type":"array","items":{"$ref":"#/components/schemas/Preference"}},"profile":{"description":"How the legs are weighed: see GET /v1/profiles. A plan's durations are each leg's as /v1/route answers it.","$ref":"#/components/schemas/Profile"},"facts":{"description":"The facts to report over each leg: see GET /v1/facts. With facts or geometry, the answer has legs.","maxItems":34,"type":"array","items":{"type":"string","enum":["speed_limit","road_owner","surface","road_class","network","gcm_type","municipality","road_name","road_number","traffic_safety_class","built_up","lit","separated","ferry","motorway","fast_road","speed_limit_varies","unlit_outside_built_up","traffic_volume","crossings_without_refuge","crossings_unknown_refuge","level_crossings","max_weight","max_axle_load","max_height","max_width","max_length","bearing_class","hgv_banned","hazmat_banned","hazmat_route","timed_restriction","structure","barriers"],"description":"A fact from GET /v1/facts."}},"geometry":{"default":"none","description":"Each leg's line and its stretches with road references: GeoJSON, an encoded polyline at 6 decimals, or not at all (the default).","type":"string","enum":["geojson","polyline6","none"]},"network":{"$ref":"#/components/schemas/NetworkSelector"},"routes":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"vehicle_id":{"type":"string","minLength":1,"maxLength":64,"description":"The vehicle's id, as sent in vehicles."},"visits":{"maxItems":1000,"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["stop","pickup","delivery"],"description":"'stop' for a stop; 'pickup' or 'delivery' for a shipment's two places."},"id":{"type":"string","minLength":1,"maxLength":64,"description":"The stop's or shipment's id, as sent in stops or shipments."}},"required":["kind","id"],"additionalProperties":false},"description":"The vehicle's tasks in the order it visits them, its start and end left out: they are the vehicle's own. The kind and id of an optimize answer's visits, without start and end, are a plan."}},"required":["vehicle_id","visits"],"additionalProperties":false},"description":"The plan to check: per vehicle, its visits in order. A task in no route is answered in unassigned; an id naming nothing sent, a visit of the wrong kind, a task visited twice or a vehicle twice is refused 400 invalid_request."}},"required":["vehicles","profile","routes"],"additionalProperties":false},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false},{"type":"object","properties":{"operation":{"type":"string","const":"address_list"},"input":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"validate","description":"Each row validated as POST /v1/addresses/validate validates it."},"rows":{"minItems":1,"maxItems":50000,"type":"array","items":{"type":"object","properties":{"street":{"type":"string","minLength":1,"maxLength":200,"description":"Street name or address area, e.g. 'Östra Ågatan'. Case and å/ä/ö are ignored."},"number":{"description":"House number, e.g. 12.","anyOf":[{"type":"integer","minimum":0,"maximum":99999},{"type":"null"}]},"letter":{"description":"Letter after the number, e.g. 'B'.","anyOf":[{"type":"string","maxLength":2},{"type":"null"}]},"postcode":{"description":"Postnummer, e.g. '75331' or '753 31'. Scopes the match; strongly recommended.","anyOf":[{"type":"string","maxLength":10},{"type":"null"}]},"locality":{"description":"Postort or kommun, e.g. 'Uppsala'. Scopes the match when postcode is missing.","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"id":{"description":"Your own id for the row, echoed back as its result row's id.","type":"string","maxLength":64}},"required":["street"],"additionalProperties":false},"description":"Your own list, 1 to 50000 rows, each a structured address as POST /v1/addresses/validate takes it, with your id. The result has one row per row sent, in the order sent."}},"required":["kind","rows"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"geocode","description":"Each row's free text read as POST /v1/geocode reads q, answered with its single best match."},"rows":{"minItems":1,"maxItems":50000,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the row, echoed back as its result row's id.","type":"string","maxLength":64},"q":{"type":"string","maxLength":200,"description":"Free text as typed, street first, e.g. 'Östra Ågatan 12B, Uppsala' or 'ostra agatan 12 b'. Typos, å/ä/ö without diacritics and abbreviations such as sthlm and gbg are handled. Only 8 distinct words are read, a postnummer as one; past them every hit is match_type 'partial'."}},"required":["q"],"additionalProperties":false},"description":"Your own list, 1 to 50000 rows, each free text as POST /v1/geocode's q, with your id. The result has one row per row sent, in the order sent."},"municipality_codes":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"string","pattern":"^\\d{4}$"},"description":"Kommunkoder, SCB's four-digit codes, e.g. ['0380']: only hits whose municipality_code is one of them are answered. They narrow before ranking, so a small kommun's street is found past same-named streets elsewhere. Only Sweden's 290 codes are accepted, a repeated one read once. A kommun with no address in the key's dataset answers results: []. Left out: all of Sweden."}},"required":["kind","rows"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"reverse","description":"Each row's point answered with the nearest current address, as POST /v1/reverse-geocode with limit 1."},"rows":{"minItems":1,"maxItems":50000,"type":"array","items":{"type":"object","properties":{"id":{"description":"Your own id for the row, echoed back as its result row's id.","type":"string","maxLength":64},"point":{"$ref":"#/components/schemas/Point"}},"required":["point"],"additionalProperties":false},"description":"Your own list, 1 to 50000 rows, each a WGS84 point, with your id. The result has one row per row sent, in the order sent."},"radius_m":{"default":2000,"description":"Ignore addresses farther away than this, in straight-line metres. For a vehicle's position send \"radius_m\": 5000, \"distinct\": \"street\", \"limit\": 3, as the recipe \"A vehicle's position\" does: 5000 m reaches the nearest address from a rural road.","type":"number","exclusiveMinimum":0,"maximum":50000}},"required":["kind","rows"],"additionalProperties":false}]},"idempotency_key":{"description":"Your id for this submission, per account: the same key with the same input answers the job already made, and with another input 409 idempotency_conflict.","type":"string","minLength":1,"maxLength":128},"dry_run":{"description":"true: check the job as a submit would and answer 200 without queueing it. No job is made, nothing sent is kept, idempotency_key is ignored, and it counts as one request, with no calculation or address metered. Left out or false, the job is queued.","type":"boolean"}},"required":["operation","input"],"additionalProperties":false}]},"example":{"operation":"route","input":{"origin":{"lat":63.724247,"lon":19.347595},"destination":{"lat":63.72059,"lon":19.342166},"profile":"walk","facts":["speed_limit"],"geometry":"none"}}}}},"responses":{"200":{"description":"dry_run: the job was checked and not queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCheck"}}},"headers":{"X-Data-Attribution":{"description":"The attribution texts of this response's data joined by \"; \", transliterated to ASCII (å→a, ä→a, ö→o, ©→(c)) so every byte is below 0x80; empty when the answer read no licensed source. Show the body's `provenance.sources[].attribution` with the data: it is the exact text.","schema":{"type":"string"},"required":true},"X-Kartpunkt-Network":{"description":"The network snapshot this answer read, the body's `provenance.network.id`; sent only when the answer read one (absent when no network snapshot was read).","schema":{"type":"string"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"202":{"description":"The job was queued; read it with GET /v1/jobs/{id}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"},"example":{"operation":"route","id":"job_01k7exampleexampleexample0","status":"queued","progress":null,"created_at":"2026-10-13T07:00:00.000Z","started_at":null,"finished_at":null,"expires_at":null,"error":null,"result":null}}},"headers":{"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan. This operation would use a data source in a way its licence or approval does not allow (see GET /v1/sources). It cannot be answered for this key.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required","use_not_permitted"]}}}}}},"404":{"description":"No network snapshot matches that id or as_of. List the snapshots with GET /v1/networks and send one of their ids, or leave network out for the active one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_unavailable"]}}}}}},"409":{"description":"This idempotency_key was used with a different input. Use a new key for a new input, or resend the original input to get the original job. This profile cannot be answered on the network snapshot asked for: it was built without the data the profile needs (see GET /v1/profiles and the snapshot's manifest at GET /v1/networks/{id}). Use another profile or a newer snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["idempotency_conflict","profile_unavailable"]}}}}}},"410":{"description":"That network snapshot was pruned: its tables and graph are deleted, so nothing can be answered on it again, as a job or otherwise. Leave network out to answer on the active network, and store that answer's provenance.network.id; GET /v1/networks lists the snapshots that are not pruned.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["network_pruned"]}}}}}},"413":{"description":"Too much for one synchronous call. Send the same input to POST /v1/jobs with this operation and read the result from GET /v1/jobs/{id}, or split it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["too_large"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}},"/v1/jobs/{id}":{"get":{"operationId":"getJob","summary":"A job, with its result once done","description":"A job's status and progress, and once it has succeeded the operation's answer as `result`. Read it from your backend with `wait` until the job ends, as the recipe \"Running a job\" says; an account reads its own jobs only.","tags":["jobs"],"parameters":[{"in":"path","required":true,"name":"id","schema":{"type":"string","pattern":"^job_[a-z0-9]+$","description":"The job's id, from POST /v1/jobs."},"example":"job_01k7exampleexampleexample0","description":"The job's id, from POST /v1/jobs."},{"in":"query","name":"wait","schema":{"description":"Seconds to wait for the job to end, from 0 to 30: the answer comes as soon as it has succeeded, failed or been cancelled, or when the wait runs out, with the job as it is then. Left out or 0, the job as it is now.","type":"integer","minimum":0,"maximum":30},"allowEmptyValue":true,"allowReserved":true,"description":"Seconds to wait for the job to end, from 0 to 30: the answer comes as soon as it has succeeded, failed or been cancelled, or when the wait runs out, with the job as it is then. Left out or 0, the job as it is now."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"},"example":{"operation":"route","id":"job_01k7exampleexampleexample0","status":"succeeded","progress":null,"created_at":"2026-10-13T07:00:00.000Z","started_at":"2026-10-13T07:01:00.000Z","finished_at":"2026-10-13T07:02:00.000Z","expires_at":"2026-10-13T07:03:00.000Z","error":null,"result":{"status":"ok","distance_m":560,"duration_s":403,"facts":{"speed_limit":{"40":560}},"avoid_m":null,"snap_m":10.05,"street_matched":null,"destination_snap_m":14.97,"destination_street_matched":null,"blocked_by":null,"via":null,"segments":[{"length_m":50,"ref":"0:17736@0.833269-1.000000","facts":{"speed_limit":"40"}},{"length_m":500,"ref":"0:17742@1.000000-0.000000","facts":{"speed_limit":"40"}},{"length_m":10,"ref":"0:17733@0.000000-0.016646","facts":{"speed_limit":"40"}}],"avoid":null,"alternatives":null,"provenance":{"synthetic":true,"network":{"id":"snap_01k7exampleexampleexample0","dataset":"nvdb","source_date":"2026-10-13","built_at":"2026-10-13T07:04:00.000Z","graph":"snap_01k7exampleexampleexample0","profile":"walk","experimental":false},"sources":[{"id":"nvdb","name":"Trafikverket, NVDB","licence":"CC0-1.0","attribution":"Vägdata från NVDB, Trafikverket, CC0 1.0","url":"https://creativecommons.org/publicdomain/zero/1.0/","updated":"2026-10-13"}]}}}}},"headers":{"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}},"Retry-After":{"description":"Sent while the job is queued or running: seconds before reading it again, 2 without `wait` and 1 after one. Absent once it has ended.","schema":{"type":"integer"},"required":false}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"404":{"description":"No job with that id for this account, or its result has expired. Results are kept for the time GET /v1/sources gives each source; submit the job again.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["job_not_found"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}},"delete":{"operationId":"cancelJob","summary":"Cancel a job","description":"Cancels a job and answers it. A queued job is cancelled at once; a running one stops at its next unit of work, so the answer may still say running. A job that has ended is answered as it is.","tags":["jobs"],"parameters":[{"in":"path","required":true,"name":"id","schema":{"type":"string","pattern":"^job_[a-z0-9]+$","description":"The job's id, from POST /v1/jobs."},"example":"job_01k7exampleexampleexample0","description":"The job's id, from POST /v1/jobs."}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"},"example":{"operation":"route","id":"job_01k7exampleexampleexample0","status":"cancelled","progress":null,"created_at":"2026-10-13T07:00:00.000Z","started_at":null,"finished_at":"2026-10-13T07:01:00.000Z","expires_at":"2026-10-13T07:02:00.000Z","error":null,"result":null}}},"headers":{"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"400":{"description":"The request does not match the API schema. Fix the fields named in this message; see /openapi.json.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["invalid_request"]}}}}}},"401":{"description":"Missing or unknown API key. Send it as 'Authorization: Bearer <key>' (or 'x-api-key'), read from the KARTPUNKT_API_KEY environment variable. The user creates a kp_test_ key on their Kartpunkt account page (https://kartpunkt.se/konto) and puts it in KARTPUNKT_API_KEY themselves; never ask them to paste a key into chat.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["unauthorized"]}}}}}},"403":{"description":"This account is not approved for live data: kp_live_ keys are for customers under contract. Use a kp_test_ key. To ask for live access, the account holder writes to hej@kartpunkt.se, or orders a plan at https://kartpunkt.se/konto#plan.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_access_required"]}}}}}},"404":{"description":"No job with that id for this account, or its result has expired. Results are kept for the time GET /v1/sources gives each source; submit the job again.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["job_not_found"]}}}}}},"429":{"description":"Request limit or plan quota reached (per minute, per day, or the account plan's monthly quota). Wait the number of seconds in the Retry-After header, then retry; without Retry-After, no wait helps: tell the account holder what this message says.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["quota_exceeded"]}}}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the call again. Absent when no wait helps (a plan without that kind of call): then do what the message says.","schema":{"type":"integer"},"required":false},"RateLimit-Policy":{"description":"The per-minute policy (draft-ietf-httpapi-ratelimit-headers-11): '\"minute\";q=<requests per minute this key may make>;w=60'.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it: '\"minute\";r=<requests left in the current calendar minute, for this key and its account>;t=<seconds until it resets>'.","schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["internal_error"]}}}}}},"503":{"description":"Live data is not available yet: it opens when Kartpunkt's import of Lantmäteriet's address register completes. Use a kp_test_ key until then.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorBody"}],"type":"object","required":["error","message","doc_url"],"properties":{"error":{"type":"string","enum":["live_data_unavailable"]}}}}}}}}}}}