Developers
Get started with Kartpunkt's API
Kartpunkt gives your system Swedish road distances, routes and addresses from Trafikverket's NVDB and Lantmäteriet's address register. You call the API from your backend with a key, or let your coding agent write the integration. Every answer names its source and network snapshot.
Documentation
- For coding agents: https://kartpunkt.se/llms.txt. What a coding agent needs to write the integration: the operations, the fields, the error codes and the rules.
- The OpenAPI document: https://api.kartpunkt.se/openapi.json, for tools that generate clients and for reading the schema.
- API reference: https://api.kartpunkt.se/docs, the same document to read in the browser. There you try each operation with a test key you enter there, in your own browser.
- The API's base URL:
https://api.kartpunkt.se
The documents are generated from the API's definition. The fields and error codes are there, not here.
Three steps to a first answer
- Sign in with your Google account (the page is in Swedish). The first sign-in creates your account.
- Create a test key on your account page. It works at once and is shown once, when you create it.
- Put the key in the environment variable
KARTPUNKT_API_KEYin your own environment, for example in the project's.env. Your code sends it asAuthorization: Bearerorx-api-key.
Check the key: the call below answers 200 with a test key.
curl https://api.kartpunkt.se/v1/postcodes/75331 \
-H "Authorization: Bearer $KARTPUNKT_API_KEY"Prompt for your coding agent
Paste the prompt into your coding agent. It reads llms.txt, takes the key from KARTPUNKT_API_KEY and writes the code.
Prompt
Integrate Kartpunkt (road distances, routes and official Swedish addresses via API) into this project. 1. Read https://kartpunkt.se/llms.txt before you write code. 2. The API key is in the environment variable KARTPUNKT_API_KEY. Never ask me to paste it into the chat. Check it first with GET /v1/postcodes/75331, which must return 200. 3. Start with POST /v1/distances from our origins to our destinations. Store each road distance with provenance.network.id, so it is documented and dated with its network snapshot. 4. Show the attribution from the answer wherever the results are shown.
Test keys and live keys
A test key starts with kp_test_. You create it yourself on your account page, with no approval. It answers from the synthetic dataset: made-up addresses with the same shapes as live data, and every answer is marked provenance.synthetic: true. The mark is about the addresses: the road network is NVDB's real one with either key, and provenance.sources says synthetic for the synthetic addresses and nvdb for the road network.
A live key starts with kp_live_ and answers from Lantmäteriet's address register, once the account is approved for live access and has a paid plan; until then it is refused with a code and a message that says what to do. Live access needs an agreement with us: ask through the contact page (in Swedish) or at hej@kartpunkt.se.
Switching from a test key to a live key changes only the key in KARTPUNKT_API_KEY. The code stays the same.
The key stays on your server
A key is shown once, when you create it. We store it only hashed and cannot show it again. Put it in KARTPUNKT_API_KEY, where your code and your coding agent find it.
Never paste the key into a chat with a coding agent, an email or code. If a key ends up somewhere it should not, revoke it on your account page and create a new one.
Call the API from your backend, never from code that runs in the browser. There the key would leak to anyone who opens the page, and the API does not answer calls from browsers.
Answers and errors
Every answer has provenance; a job has it in its answer once the job is done. provenance holds each source's attribution in attribution, and synthetic says a test key answered: the addresses are then synthetic, the road network NVDB's. Each address has a stable id and a ready-made address line in label. Store the id and the label, and show the attribution where the addresses are shown.
Store provenance.network.id with the answer: it documents and dates the answer with its network snapshot. While that snapshot is served, the same call with network: { id } gives the same answer. An older snapshot cannot be computed on today.
Every error is { error, message, doc_url }: a stable error code and a message that says what to do next, written so that a coding agent can follow it, with a link to the code's page; an invalid request also names the field in param. The fields and error codes are in llms.txt.
Limits
Calls are limited per minute and per day, so that no one can copy the address register. A call over a limit gets the answer 429 with the header Retry-After: wait that many seconds and try again. The plans and what they include are on the pricing page.
A call also has a largest size. A larger call gets an error that says what to change, and the same input runs as a job, from Bas. Per call: route optimization and plan checks take 25 stops, 12 shipments, 5 vehicles and 30 locations directly, and 500 stops, 250 shipments, 50 vehicles and 600 locations as a job; a route takes 10 via points; distances take 1,000 origins and 200 origins × destinations directly (20 with a barrier), 50,000 and 50,000 as a job; a matrix 625 cells directly (8 origins with a vehicle, prefer or avoid) and 10,000 as a job; reach 4 times or distances per call. The other operations' limits are in llms.txt.
Operations
The operations are under /v1, in the API's groups: addresses, roads, routes and distances, planning, registries and network snapshots, and jobs. Each operation has its own page in the API reference.
Search
searchAddressesFrom text to addresses, even misspelled.
Reverse geocoding
reverseGeocodeFrom a coordinate to the nearest addresses and the road beside them.
Validation
validateAddressReports what is wrong in an address, without changing it.
Address changes
checkAddressChangesWhich of your addresses have changed or closed since a date.
Postnummer lookup
lookupPostcodeFrom a postnummer to its postorter and kommuner.
Address lookup
lookupAddressFrom a stored address id to the address today or on a date.
Snap
snapPointsMoves points onto the nearest road a profile may travel.
Roads
listRoadsNearbyThe roads around a point, with the facts you ask for.
Road reference
getRoadA road by its road reference, which points to the same road in every delivery where the link remains.
Route
planRouteThe route for a profile, in route segments with a road reference and facts; avoids stretches you send.
Road distances
measureDistancesFrom many origins to a few destinations, such as customers to a depot: distance and time along the road network, with the facts you ask for.
Nearest bus stops
findNearestThe bus stops nearest each point along the road network.
Matrix
buildMatrixDistance and time between each origin and each destination.
Reach
measureReachThe area reached within a number of minutes or metres from a point.
Trace
matchTraceMatches a GPS trace to the roads it followed; the trace is not stored.
Route optimization
optimizeRoutesWhich vehicle visits which stops, in what order and when, within capacities and time windows.
Plan check
checkPlanYour own routes, in your order, with what breaks capacities, time windows and other conditions.
Profiles
listProfilesThe profiles routes are computed for: walking, car (shortest and fastest way), lorry and bus.
Facts
listFactsNVDB's facts a route or a road can report, with their kind and unit.
Sources
listSourcesEach data source with its licence, its attribution and what its data may be used for.
Vector tiles
listTilesetsNVDB's roads as vector tiles to draw in your own map.
Network snapshots
listNetworksThe road network's snapshots: the one served now and the older ones kept.
Network snapshot
getNetworkOne network snapshot: its NVDB date and which profiles it can answer.
Jobs
submitJobSubmits a job.
Jobs
getJobStatus and answer.
Jobs
cancelJobCancels.
Route optimization: an example
Send the vehicles and the stops to /v1/optimize and get back which vehicle drives to which stops, in what order and when. Every stop no vehicle takes is in unassigned with an error code that says what to change.
A plan at once:
curl -X POST https://api.kartpunkt.se/v1/optimize \
-H "Authorization: Bearer $KARTPUNKT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"vehicles":[{"id":"fordon-1","start":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"end":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"capacity":{"seats":8},"time_window":{"start":"2026-10-12T06:45:00+02:00","end":"2026-10-12T08:15:00+02:00"}},{"id":"fordon-2","start":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"end":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"capacity":{"seats":8},"time_window":{"start":"2026-10-12T06:45:00+02:00","end":"2026-10-12T08:15:00+02:00"}}],"stops":[{"id":"s1","at":{"lat":63.720434,"lon":19.348226,"street":"Skolvägen"},"pickup":{"seats":2},"service_s":60,"time_windows":[{"start":"2026-10-12T07:00:00+02:00","end":"2026-10-12T07:45:00+02:00"}]},{"id":"s2","at":{"lat":63.724508,"lon":19.339523,"street":"Norrgatan"},"pickup":{"seats":2},"service_s":60,"time_windows":[{"start":"2026-10-12T07:00:00+02:00","end":"2026-10-12T07:45:00+02:00"}]},{"id":"s3","at":{"lat":63.724205,"lon":19.337043,"street":"Snedgatan"},"pickup":{"seats":1},"service_s":60,"time_windows":[{"start":"2026-10-12T07:00:00+02:00","end":"2026-10-12T07:45:00+02:00"}]}],"objective":"vehicles_then_duration","profile":"car_fastest"}'A larger plan as a job: the same input under "operation": "optimize". Read the job until its status is succeeded; the plan is in result.
JOB_ID=$(curl -X POST https://api.kartpunkt.se/v1/jobs \
-H "Authorization: Bearer $KARTPUNKT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"operation":"optimize","input":{"vehicles":[{"id":"fordon-1","start":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"end":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"capacity":{"seats":8},"time_window":{"start":"2026-10-12T06:45:00+02:00","end":"2026-10-12T08:15:00+02:00"}},{"id":"fordon-2","start":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"end":{"lat":63.72059,"lon":19.342166,"street":"Skolvägen"},"capacity":{"seats":8},"time_window":{"start":"2026-10-12T06:45:00+02:00","end":"2026-10-12T08:15:00+02:00"}}],"stops":[{"id":"s1","at":{"lat":63.720434,"lon":19.348226,"street":"Skolvägen"},"pickup":{"seats":2},"service_s":60,"time_windows":[{"start":"2026-10-12T07:00:00+02:00","end":"2026-10-12T07:45:00+02:00"}]},{"id":"s2","at":{"lat":63.724508,"lon":19.339523,"street":"Norrgatan"},"pickup":{"seats":2},"service_s":60,"time_windows":[{"start":"2026-10-12T07:00:00+02:00","end":"2026-10-12T07:45:00+02:00"}]},{"id":"s3","at":{"lat":63.724205,"lon":19.337043,"street":"Snedgatan"},"pickup":{"seats":1},"service_s":60,"time_windows":[{"start":"2026-10-12T07:00:00+02:00","end":"2026-10-12T07:45:00+02:00"}]}],"objective":"vehicles_then_duration","profile":"car_fastest"}}' | jq -r .id)
curl https://api.kartpunkt.se/v1/jobs/$JOB_ID \
-H "Authorization: Bearer $KARTPUNKT_API_KEY"Jobs are included from Bas. On the free Utvecklare plan, POST /v1/jobs answers 403 live_access_required, even with a test key; there a plan goes directly to /v1/optimize, within the limits above.
The points are in the synthetic dataset's made-up town Ruttby; with a key they are computed on NVDB's real roads, so the answer is a different plan from the one recorded on /prova (in Swedish). The limits for a plan at once and for a job are under Limits.