# Getting started

The Fentlig API serves Swedish public procurement notices as JSON. It is
read-only: every operation is a `GET`, and every operation lives under `/v1` on
`https://api.fentlig.se`. The OpenAPI document is at
[https://api.fentlig.se/openapi.json](https://api.fentlig.se/openapi.json).

## Your first call

Fetching one notice needs no key. Address it by the slug from its page on
fentlig.se, which is the part of the address after `/upphandling/`:

```bash
curl "https://api.fentlig.se/v1/notices?slug=frukt-gronsaker-och-potatis-20eea2cdb1"
```

The response, shortened:

```json
{
  "data": {
    "id": "20eea2cd-b1c6-431d-8bcb-b4fa100d4204",
    "slug": "frukt-gronsaker-och-potatis-20eea2cdb1",
    "title": "Frukt, grönsaker och potatis",
    "buyerName": "Region Sörmland",
    "status": "concluded",
    "awardOutcome": {
      "awardDate": "2026-02-06T00:00:00.000Z",
      "bidCount": 6,
      "awardedValue": 17200000,
      "currency": "SEK",
      "lotCount": 2,
      "lotsAwarded": 2,
      "lotsNotAwarded": 0,
      "lotsUndecided": 0
    }
  },
  "coverage": {
    "sources": ["TED", "Fentlig"],
    "attribution": [
      {
        "source": "TED",
        "text": "Uppgifter: Tenders Electronic Daily (TED), Europeiska unionens publikationsbyrå | Bearbetning: Fentlig AB"
      }
    ],
    "dataAsOf": "2026-09-29T10:29:55.000Z",
    "isComplete": false
  }
}
```

Reading a single notice, by slug or by id, needs no key. Searching and
listing need a key, which requires the Koncern plan. See
[Authentication](/utvecklare/authentication).

```bash
curl "https://api.fentlig.se/v1/notices?status=open&cpv=45" \
  -H "Authorization: Bearer $FENTLIG_API_KEY"
```

To use the data from an AI assistant, on any plan, see
[MCP server](/utvecklare/mcp).

## The response envelope

A listing carries three keys:

```json
{
  "data": [],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 0,
    "totalPages": 0,
    "hasNextPage": false,
    "hasPreviousPage": false
  },
  "coverage": {
    "sources": ["TED"],
    "attribution": [],
    "dataAsOf": "2026-09-29T10:29:55.000Z",
    "isComplete": false
  }
}
```

`data` holds an array on a listing and an object on a single notice.
`pagination` appears on a listing.

`coverage` names the sources, how current they are, and the attribution to
reproduce if you republish. See
[Coverage and attribution](/utvecklare/coverage).

Ignore keys you do not recognise; fields can be added inside `/v1`. See
[Versioning and changes](/utvecklare/versioning).

## Filtering

Filters combine with AND. The values inside one comma-separated filter combine
with OR, so `region=SE11,SE12` matches either.

| Parameter | Takes | Notes |
| --- | --- | --- |
| `search` | Free text | Matches title and description. Notices are in Swedish, so search in Swedish. |
| `status` | `planned`, `open`, `closed`, `concluded` | Comma-separated. Omit for all four. |
| `region` | NUTS codes | Prefix match, so `SE11` covers `SE110`. Includes notices that can be performed anywhere in Sweden. |
| `nationwide` | `include`, `exclude` | `exclude` leaves out the notices that can be performed anywhere in the country. |
| `cpv` | CPV codes, 2 to 8 digits | Prefix match. `45` is construction work. |
| `buyer` | Buyer names | Substring match: `Karlskrona` finds `Karlskrona kommun`. |
| `buyer_id` | Buyer ids (UUIDs) | Exact match on the buyer. |
| `deadline_after`, `deadline_before` | RFC 3339 timestamps | The bid deadline. |
| `publication_after`, `publication_before` | RFC 3339 timestamps | When a notice of the procurement was published. |
| `min_value`, `max_value` | Numbers | The estimated value, in the notice's own currency. |
| `exclude_past_deadline` | `true` | Leaves out notices whose deadline has passed. |
| `closed_without_award` | `true` | Procurements where at least one lot ended with no contract awarded. |
| `country` | `all`, or alpha-2 codes | The buyer's country. Defaults to Sweden. |
| `foreign_buyers` | `include`, `exclude` | `include` adds notices from buyers outside those countries whose work is performed in them. |
| `sort` | `publication_desc`, `deadline_asc`, `value_desc` | See below. |

The four statuses:

- `planned` announces an intention to buy. There is nothing to bid on yet.
- `open` accepts bids until its deadline.
- `closed` is past its deadline with no result published.
- `concluded` has a published result. The result is either an award or a
  discontinued procedure. `awardOutcome` says how many lots were awarded, not
  awarded, or not yet decided.

If you omit `sort`, you get the newest first. If `status=open` is the only
status you asked for, you get the soonest deadline first.

These filters need a scope on the key. Without it, the request answers 403.

| Filter | Scope |
| --- | --- |
| `evaluation_method`, `quality_weight_min`, `contract_months_min`, `contract_months_max` | `analysis:read` |
| `document_search`: free text matched against the tender documents instead of the notice | `documents:read` |

Every parameter, with its type and limits, is on the
[reference page for this endpoint](/utvecklare/referens/get-notices).

Open construction work in Stockholm and Uppsala counties with a bid deadline
in the first quarter of 2027:

```bash
curl -H "Authorization: Bearer $FENTLIG_API_KEY" \
  "https://api.fentlig.se/v1/notices?status=open&cpv=45&region=SE11,SE12&deadline_after=2027-01-01T00:00:00Z&deadline_before=2027-03-31T23:59:59Z"
```

## Addressing a notice

A notice has two addresses that return the same body. `id` is a UUID and
never changes. `slug` is the last part of the public address and changes when
the title does. Store the `id`.

```bash
curl "https://api.fentlig.se/v1/notices/20eea2cd-b1c6-431d-8bcb-b4fa100d4204"
```

## Pagination

`page` starts at 1 and `limit` is at most 50. `pagination.total` is the exact
count for your filters.

Only the first 1,000 results can be paged; a page past that answers 400.
Narrow the set with `status`, `region` or `cpv` and page through each part.

## Errors

Failures are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem
documents, served as `application/problem+json`. A valid id that matches no
published notice answers 404:

```json
{
  "type": "https://fentlig.se/developers/errors/notice-not-found",
  "title": "Notice not found",
  "status": 404,
  "instance": "/v1/notices/00000000-0000-4000-8000-000000000000",
  "code": "NOTICE_NOT_FOUND"
}
```

A value that is not an id answers 400:

```json
{
  "type": "https://fentlig.se/developers/errors/validation-error",
  "title": "Invalid request",
  "status": 400,
  "detail": "`id` must be a notice id, which is a UUID. To look a notice up by the slug in its address on fentlig.se, use GET /v1/notices?slug=.",
  "instance": "/v1/notices/f4161f6d",
  "code": "VALIDATION_ERROR"
}
```

Branch on `code`, which is stable. `type` links to the page for that error.
`detail` describes this occurrence. See the
[error reference](/utvecklare/fel).
