# GET /v1/jobs/{id}

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

operationId: `getJob`. A job, with its result once done.
Tag: `jobs`.

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.

## Request

`GET https://api.kartpunkt.se/v1/jobs/{id}` with `Authorization: Bearer $KARTPUNKT_API_KEY`, from your backend (never browser code: the key would leak, and CORS is not enabled).

Parameters:
- `id` (path, string, pattern `^job_[a-z0-9]+$`, required): The job's id, from POST /v1/jobs.
- `wait` (query, integer 0..30, optional): 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.

## Response 200

Example:

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

The shape [Job](https://api.kartpunkt.se/docs/shapes/Job.md).

One of 9 forms, by `operation` ("route", "distances", "matrix", "reach", "trace", "nearest", "optimize", "check_plan", "address_list").

Every form:
- `id` (string): The job's id: read it with GET /v1/jobs/{id}.
- `status` ("queued" | "running" | "succeeded" | "failed" | "cancelled" | "expired"): 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` (object | null): How far the running job has come. null until it first reports.
  - `done` (number): Units of work done.
  - `total` (number): Units of work in all.
- `created_at` (string): When the job was submitted, RFC 3339.
- `started_at` (string | null): When it last started running, RFC 3339; null until it has.
- `finished_at` (string | null): When it ended, RFC 3339; null until it has.
- `expires_at` (string | null): When the result is deleted, RFC 3339; null until the job ends. Its sources' policies set how long it is kept (GET /v1/sources).
- `error` (object | null): Why a failed job failed. null in every other status.
  - `code` (string): A code from the error table.
  - `message` (string): That code's message, saying what to do next.

By `operation`:
- `operation`: "route":
  - `result` (object): the 200 answer of [POST /v1/route](https://api.kartpunkt.se/docs/operations/planRoute.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "distances":
  - `result` (object): the 200 answer of [POST /v1/distances](https://api.kartpunkt.se/docs/operations/measureDistances.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "matrix":
  - `result` (object): the 200 answer of [POST /v1/matrix](https://api.kartpunkt.se/docs/operations/buildMatrix.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "reach":
  - `result` (object): the 200 answer of [POST /v1/reach](https://api.kartpunkt.se/docs/operations/measureReach.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "trace":
  - `result` (object): the 200 answer of [POST /v1/trace](https://api.kartpunkt.se/docs/operations/matchTrace.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "nearest":
  - `result` (object): the 200 answer of [POST /v1/nearest](https://api.kartpunkt.se/docs/operations/findNearest.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "optimize":
  - `result` (object): the 200 answer of [POST /v1/optimize](https://api.kartpunkt.se/docs/operations/optimizeRoutes.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "check_plan":
  - `result` (object): the 200 answer of [POST /v1/optimize/check](https://api.kartpunkt.se/docs/operations/checkPlan.md). The operation's answer once the job has succeeded; null in every other status.
- `operation`: "address_list":
  - `result` (object | null): The operation's answer once the job has succeeded; null in every other status.
    - `kind` ("validate" | "geocode" | "reverse"): The kind sent.
    - `rows` (array of object): One row per row sent, in the order sent.
      - `id` (string | null): The id sent with the row; null where none was.
      - `status` ("matched" | "ambiguous" | "unmatched"): '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` ([Address](https://api.kartpunkt.se/docs/shapes/Address.md) | null): The address the row is, when status is 'matched'; otherwise null, never a guess or another address.
      - `issues` (array of "street_not_found" | "number_not_found" | "letter_not_found" | "postcode_mismatch" | "locality_mismatch" | "ambiguous"): For validate, what POST /v1/addresses/validate says is wrong with the row (empty when it is valid); for geocode and reverse, always empty.
    - `summary` (object): Your own rows counted by status: how many of them match an address. A count of your list, never of the register.
      - `rows` (integer)
      - `matched` (integer)
      - `ambiguous` (integer)
      - `unmatched` (integer)
    - `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.
- [`job_not_found`](https://api.kartpunkt.se/errors/job_not_found) (404): 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.
- [`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.

## Limits

none

## Metering

A request only: no calculations, no live call, no address limit.
