Documentation
Getting started
The base URL, a first call that needs no key, the response envelope, filtering, and how to address a notice.
Updated View as Markdown
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.
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/:
curl "https://api.fentlig.se/v1/notices?slug=frukt-gronsaker-och-potatis-20eea2cdb1"The response, shortened:
{
"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.
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.
The response envelope
A listing carries three keys:
{
"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.
Ignore keys you do not recognise; fields can be added inside /v1. See
Versioning and changes.
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:
plannedannounces an intention to buy. There is nothing to bid on yet.openaccepts bids until its deadline.closedis past its deadline with no result published.concludedhas a published result. The result is either an award or a discontinued procedure.awardOutcomesays 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.
Open construction work in Stockholm and Uppsala counties with a bid deadline in the first quarter of 2027:
curl -H "Authorization: Bearer $FENTLIG_API_KEY" \
"https://api.fentlig.se/v1/notices?status=open&cpv=45®ion=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.
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 problem
documents, served as application/problem+json. A valid id that matches no
published notice answers 404:
{
"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:
{
"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.