# Get started with Kartpunkt's API

> From sign-in to a first answer in three steps: a test key, documentation for coding agents (llms.txt, OpenAPI) and a ready prompt for your coding agent.
>
> https://kartpunkt.se/en/developers

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](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](https://api.kartpunkt.se/openapi.json), for tools that generate clients and for reading the schema.
- **API reference:** [https://api.kartpunkt.se/docs](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

1. [Sign in](https://kartpunkt.se/logga-in) with your Google account (the page is in Swedish). The first sign-in creates your account.
2. Create a test key on your [account page](https://kartpunkt.se/konto). It works at once and is shown once, when you create it.
3. Put the key in the environment variable `KARTPUNKT_API_KEY` in your own environment, for example in the project's `.env`. Your code sends it as `Authorization: Bearer` or `x-api-key`.

Check the key: the call below answers 200 with a test key.

```sh
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.

```text
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](https://kartpunkt.se/kontakt) (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](https://kartpunkt.se/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](https://kartpunkt.se/en/pricing).

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](https://kartpunkt.se/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** ([`searchAddresses`](https://api.kartpunkt.se/docs/operations/searchAddresses.md)): From text to addresses, even misspelled.
- **Reverse geocoding** ([`reverseGeocode`](https://api.kartpunkt.se/docs/operations/reverseGeocode.md)): From a coordinate to the nearest addresses and the road beside them.
- **Validation** ([`validateAddress`](https://api.kartpunkt.se/docs/operations/validateAddress.md)): Reports what is wrong in an address, without changing it.
- **Address changes** ([`checkAddressChanges`](https://api.kartpunkt.se/docs/operations/checkAddressChanges.md)): Which of your addresses have changed or closed since a date.
- **Postnummer lookup** ([`lookupPostcode`](https://api.kartpunkt.se/docs/operations/lookupPostcode.md)): From a postnummer to its postorter and kommuner.
- **Address lookup** ([`lookupAddress`](https://api.kartpunkt.se/docs/operations/lookupAddress.md)): From a stored address id to the address today or on a date.
- **Snap** ([`snapPoints`](https://api.kartpunkt.se/docs/operations/snapPoints.md)): Moves points onto the nearest road a profile may travel.
- **Roads** ([`listRoadsNearby`](https://api.kartpunkt.se/docs/operations/listRoadsNearby.md)): The roads around a point, with the facts you ask for.
- **Road reference** ([`getRoad`](https://api.kartpunkt.se/docs/operations/getRoad.md)): A road by its road reference, which points to the same road in every delivery where the link remains.
- **Route** ([`planRoute`](https://api.kartpunkt.se/docs/operations/planRoute.md)): The route for a profile, in route segments with a road reference and facts; avoids stretches you send.
- **Road distances** ([`measureDistances`](https://api.kartpunkt.se/docs/operations/measureDistances.md)): From 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** ([`findNearest`](https://api.kartpunkt.se/docs/operations/findNearest.md)): The bus stops nearest each point along the road network.
- **Matrix** ([`buildMatrix`](https://api.kartpunkt.se/docs/operations/buildMatrix.md)): Distance and time between each origin and each destination.
- **Reach** ([`measureReach`](https://api.kartpunkt.se/docs/operations/measureReach.md)): The area reached within a number of minutes or metres from a point.
- **Trace** ([`matchTrace`](https://api.kartpunkt.se/docs/operations/matchTrace.md)): Matches a GPS trace to the roads it followed; the trace is not stored.
- **Route optimization** ([`optimizeRoutes`](https://api.kartpunkt.se/docs/operations/optimizeRoutes.md)): Which vehicle visits which stops, in what order and when, within capacities and time windows.
- **Plan check** ([`checkPlan`](https://api.kartpunkt.se/docs/operations/checkPlan.md)): Your own routes, in your order, with what breaks capacities, time windows and other conditions.
- **Profiles** ([`listProfiles`](https://api.kartpunkt.se/docs/operations/listProfiles.md)): The profiles routes are computed for: walking, car (shortest and fastest way), lorry and bus.
- **Facts** ([`listFacts`](https://api.kartpunkt.se/docs/operations/listFacts.md)): NVDB's facts a route or a road can report, with their kind and unit.
- **Sources** ([`listSources`](https://api.kartpunkt.se/docs/operations/listSources.md)): Each data source with its licence, its attribution and what its data may be used for.
- **Vector tiles** ([`listTilesets`](https://api.kartpunkt.se/docs/operations/listTilesets.md)): NVDB's roads as vector tiles to draw in your own map.
- **Network snapshots** ([`listNetworks`](https://api.kartpunkt.se/docs/operations/listNetworks.md)): The road network's snapshots: the one served now and the older ones kept.
- **Network snapshot** ([`getNetwork`](https://api.kartpunkt.se/docs/operations/getNetwork.md)): One network snapshot: its NVDB date and which profiles it can answer.
- **Jobs** ([`submitJob`](https://api.kartpunkt.se/docs/operations/submitJob.md)): Submits a job.
- **Jobs** ([`getJob`](https://api.kartpunkt.se/docs/operations/getJob.md)): Status and answer.
- **Jobs** ([`cancelJob`](https://api.kartpunkt.se/docs/operations/cancelJob.md)): Cancels.

## 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:

```sh
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`.

```sh
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](https://kartpunkt.se/prova#ruttoptimering) (in Swedish). The limits for a plan at once and for a job are under [Limits](#limits).
