# POST /v1/nearest

> Index: https://kartpunkt.se/llms.txt

operationId: `findNearest`. What is nearest each point along the network.
Tag: `routing`.

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.

## Request

`POST https://api.kartpunkt.se/v1/nearest` with `Authorization: Bearer $KARTPUNKT_API_KEY` and `Content-Type: application/json`, from your backend (never browser code: the key would leak, and CORS is not enabled).

Example body:

```json
{
  "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
}
```

JSON body:

- `kind` ("bus_stop", required): What to find: 'bus_stop', NVDB's bus stops (hållplatser) of the network snapshot.
- `points` (array of object, 1..1000 items, required)
  - `id` (string, max 64 chars, optional): Your own id for the point, echoed back.
  - `point` ([StreetPoint](https://api.kartpunkt.se/docs/shapes/StreetPoint.md), required)
- `profile` ([Profile](https://api.kartpunkt.se/docs/shapes/Profile.md), optional, default "walk"): The profile whose network and distance measure what is nearest (GET /v1/profiles); walk unless given.
- `limit` (integer 1..5, optional, default 3): How many per point, nearest by the profile's distance first (1-5, default 3).
- `max_distance_m` (number 1..5000, optional, default 2000): Only what is within this many metres along the network by the profile, not in a straight line (1-5000, default 2000).
- `network` ([NetworkSelector](https://api.kartpunkt.se/docs/shapes/NetworkSelector.md), optional)

## Response 200

Example:

```json
{
  "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"
      }
    ]
  }
}
```

- `results` (array of object)
  - `id` (string | null)
  - `origin` ([SnapResult](https://api.kartpunkt.se/docs/shapes/SnapResult.md)): Where the point met the profile's network; snap_m null when it met none, and nearest is then [].
  - `nearest` (array of object): Nearest first; [] when none is within max_distance_m or the point met no road.
    - `kind` ("bus_stop"): What to find: 'bus_stop', NVDB's bus stops (hållplatser) of the network snapshot.
    - `id` (string): Its id in the network snapshot.
    - `name` (string | null)
    - `point` ([Point](https://api.kartpunkt.se/docs/shapes/Point.md)): Its position.
    - `distance_m` (number): Metres along the network by the profile, between where the point and it meet the network.
    - `duration_s` (number | null): Free-flow seconds of that route by the profile.
- `provenance` ([Provenance](https://api.kartpunkt.se/docs/shapes/Provenance.md))

## Errors

Every error is `{ error, message, param?, doc_url }`; `message` says what to do next. All codes: [https://api.kartpunkt.se/errors](https://api.kartpunkt.se/errors). What to do on each, with retries: [https://api.kartpunkt.se/docs/recipes/errors-and-retries.md](https://api.kartpunkt.se/docs/recipes/errors-and-retries.md).

- [`invalid_request`](https://api.kartpunkt.se/errors/invalid_request) (400): The request does not match the API schema. Fix the fields named in this message; see /openapi.json.
- [`unauthorized`](https://api.kartpunkt.se/errors/unauthorized) (401): 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.
- [`live_access_required`](https://api.kartpunkt.se/errors/live_access_required) (403): 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.
- [`use_not_permitted`](https://api.kartpunkt.se/errors/use_not_permitted) (403): 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.
- [`network_unavailable`](https://api.kartpunkt.se/errors/network_unavailable) (404): 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.
- [`network_not_active`](https://api.kartpunkt.se/errors/network_not_active) (409): 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}.
- [`profile_unavailable`](https://api.kartpunkt.se/errors/profile_unavailable) (409): 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.
- [`network_pruned`](https://api.kartpunkt.se/errors/network_pruned) (410): 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.
- [`too_large`](https://api.kartpunkt.se/errors/too_large) (413): 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.
- [`quota_exceeded`](https://api.kartpunkt.se/errors/quota_exceeded) (429): 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.
- [`internal_error`](https://api.kartpunkt.se/errors/internal_error) (500): Something failed on our side. Retry after a few seconds; if it keeps failing, contact hej@kartpunkt.se.
- [`live_data_unavailable`](https://api.kartpunkt.se/errors/live_data_unavailable) (503): 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.
- [`routing_unavailable`](https://api.kartpunkt.se/errors/routing_unavailable) (503): Road distances and routes are not available yet: Kartpunkt's road graph from Trafikverket's NVDB has not been built. Try again later.

## Limits

| Limit | Synchronous | As a job (`POST /v1/jobs`, `operation: "nearest"`) |
| --- | --- | --- |
| `points` | 1000 | 50000 |
| `limit` (at most) | 5 | 5 |
| `max_distance_m` (at most) | 5000 | 5000 |
| Request body | 1 MB | 8 MB |

Above a synchronous limit the call is refused (413 `too_large`, or 400 `invalid_request` naming the field); send the same input as a job.

## Metering

Calculations: 1 per point.
