Doffin APIdoffin.no ↗
Access Norwegian public procurement notices from Doffin. Search tenders, fetch full notice details, and retrieve CPV category and location code hierarchies.
What is the Doffin API?
The Doffin API provides 4 endpoints for querying Norway's national public procurement database. With search_tenders you can filter notices by keyword, CPV code, publication date range, and notice type, receiving paginated results alongside facet counts. Separate endpoints return full notice detail, the complete CPV code hierarchy, and a hierarchical tree of Norwegian location codes.
curl -X POST 'https://api.parse.bot/scraper/8f7b91a3-1162-48ab-ae3d-83fd76676daa/search_tenders' \
-H 'X-API-Key: $PARSE_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"page": "1",
"limit": "5",
"query": "IT",
"sort_by": "RELEVANCE",
"statuses": "ACTIVE"
}'Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace doffin-no-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Doffin.no — Norwegian public procurement notices. Bounded, re-runnable."""
from parse_apis.doffin_no_api import Doffin, Sort, Status, NoticeType, TenderNotFound
client = Doffin()
# List CPV codes (procurement categories) — single page, capped.
for cpv in client.cpvcodes.list(limit=3):
print(cpv.id, cpv.label)
# Get the full location tree for Norway.
tree = client.locations.get_tree()
print(tree.name, f"({len(tree.children)} regions)")
# Search active IT tenders, sorted by newest publication date.
tender = client.tendersummaries.search(
query="IT", statuses=Status.ACTIVE, sort_by=Sort.PUBLICATION_DATE_DESC, limit=1
).first()
# Drill into full detail from the summary.
if tender:
try:
detail = tender.details()
print(detail.heading, detail.notice_type, detail.publication_date)
except TenderNotFound as exc:
print(f"Tender gone: {exc}")
print("exercised: cpvcodes.list / locations.get_tree / tendersummaries.search / details")
Search for procurement notices (tenders) with various filters. Returns paginated results with facets for filtering.
| Param | Type | Description |
|---|---|---|
| page | integer | Page number for pagination. |
| limit | integer | Number of results per page. |
| query | string | Search keyword to match against tender titles and descriptions. |
| types | string | Comma-separated notice type filter values. |
| pub_to | string | Publication date upper bound in ISO 8601 format (e.g. '2026-01-01T00:00:00.000Z'). |
| cpv_ids | string | Comma-separated CPV code IDs to filter by (e.g. '72000000,45000000'). |
| sort_by | string | Sort order for results. |
| pub_from | string | Publication date lower bound in ISO 8601 format (e.g. '2023-01-01T00:00:00.000Z'). |
| statuses | string | Comma-separated status filter values. |
| locations | string | Comma-separated location IDs to filter by (e.g. 'NO08,NO0A'). Use get_location_codes to discover valid IDs. |
| contract_natures | string | Comma-separated contract nature filter values. |
{
"type": "object",
"fields": {
"hits": "array of tender summary objects with id, buyer, heading, description, type, status, deadline, publicationDate",
"facets": "object containing available filter options with counts",
"activeFacets": "object showing currently active filter selections",
"numHitsTotal": "integer total number of matching results",
"numHitsAccessible": "integer maximum accessible results (capped at 1000)"
},
"sample": {
"data": {
"hits": [
{
"id": "2025-116485",
"type": "ANNOUNCEMENT_OF_INTENT",
"buyer": [
{
"id": "4c4f51be27b9d9b28b1bc80b42eb4921",
"name": "Øvre Romerike Innkjøpssamarbeid",
"organizationId": "933649768"
}
],
"status": null,
"heading": "Intensjonskunngjøring – anskaffelse av Gemini VA og Gemini Portal+",
"allTypes": [
"ANNOUNCEMENT_OF_INTENT",
"RESULT"
],
"deadline": null,
"issueDate": "2025-11-10T10:40:43Z",
"sentToTed": true,
"locationId": [
"anyw"
],
"description": "anskaffelse av Gemini VA og Gemini Portal+",
"estimatedValue": null,
"publicationDate": "2025-11-11",
"placeOfPerformance": [
"Ikke stedbunden"
],
"procurementStrategicLabels": []
}
],
"facets": {
"type": {
"items": [
{
"id": "COMPETITION",
"total": 19147
}
]
},
"status": {
"items": [
{
"id": "EXPIRED",
"total": 15403
}
]
}
},
"activeFacets": {
"type": {
"items": []
},
"buyer": {
"items": []
}
},
"numHitsTotal": 35442,
"numHitsAccessible": 1000
},
"status": "success"
}
}About the Doffin API
Searching and Filtering Procurement Notices
The search_tenders endpoint accepts a range of optional filters: query matches against tender titles and descriptions, cpv_ids accepts comma-separated CPV codes (e.g. 72000000,45000000), pub_from and pub_to bound results by ISO 8601 publication dates, and types filters by notice type (e.g. COMPETITION, ANNOUNCEMENT_OF_COMPETITION). Results are sorted by RELEVANCE, PUBLICATION_DATE_DESC, or PUBLICATION_DATE_ASC. Each page of results includes a hits array of tender summaries — covering id, buyer, heading, description, type, status, deadline, and publicationDate — along with facets showing available filter breakdowns and numHitsTotal for the full match count. Accessible results are capped at 1000 regardless of total hits.
Full Notice Detail
Passing a notice reference ID (e.g. 2025-116485) to get_tender_detail returns the complete record for that notice. The response includes structured eform sections containing the granular fields that make up the notice, buyer objects with IDs and names, noticeType, heading, description, and award information where available. Notice IDs are obtained from search_tenders results.
Reference Code Lookups
get_cpv_codes returns the full CPV (Common Procurement Vocabulary) tree used across EU and Norwegian procurement. Each node carries an id (e.g. 03000000), a stem, a human-readable label, and a children array for nested subcategories. get_location_codes returns the Norwegian geographic hierarchy from country (NO0) down to county level, with each node carrying an id, name, level integer, parent reference, and children array. These two lookups are useful for building filter UIs or mapping codes returned in tender results to readable labels.
The Doffin API is a managed, monitored endpoint for doffin.no — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when doffin.no changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official doffin.no API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Monitor new Norwegian government tenders matching specific CPV codes for a given product or service category.
- Build a tender alert system that polls
search_tendersfiltered bypub_fromto catch notices published since the last check. - Enrich procurement analytics pipelines with full notice details including buyer identity and structured eForm sections from
get_tender_detail. - Map tender results to readable category names by cross-referencing
cpv_idsagainst theget_cpv_codeshierarchy. - Visualize procurement activity by Norwegian region using location codes from
get_location_codesalongside tender search results. - Aggregate open competition notices by deadline to prioritize which tenders to bid on across multiple procurement categories.
- Identify frequent government buyers by extracting and counting
buyerobjects across a filtered set ofsearch_tendersresults.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does Doffin have an official developer API?+
How does pagination work in `search_tenders`, and is there a cap on results?+
page and limit parameters to paginate through results. The response includes numHitsTotal (total matching notices) and numHitsAccessible, which is capped at 1000. Even if a search matches more than 1000 notices, only the first 1000 are accessible through pagination. Narrowing filters — such as tighter date ranges or specific CPV codes — helps stay within that window.Does `get_tender_detail` return awarded contract values or supplier names?+
eform sections that include award information where it exists in the notice. However, not all notices carry award data — availability depends on the notice type and what the contracting authority has published. The noticeType field in the response helps identify whether an award should be present.Can I filter `search_tenders` by geographic location (region or county)?+
search_tenders inputs do not include a location filter parameter. Location codes are retrievable via get_location_codes, but they are not yet wired as a search filter. You can fork this API on Parse and revise it to add location-based filtering to the search endpoint.Are historical tenders from before 2023 accessible through the search endpoint?+
pub_from parameter accepts any ISO 8601 date, so older notices can be queried if they exist in the Doffin database. In practice, the depth of historical data depends on what Doffin has indexed. For very old notices not returned by search, there is no separate historical archive endpoint. You can fork this API on Parse and revise it to add a dedicated historical lookup if Doffin exposes that data via another route.