API Reference
The API is served on two versions with different authentication:
/api/v1/*— Data endpoints authenticated with theX-API-KEYheader./api/v2/*— The same data endpoints authenticated with a HelseID access token (DPoP).
GET /api/v1/treatment-group
Returns treatment group data. Supports filtering and version-based diff queries.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
since-version |
long |
No | If provided, returns only items changed since this version. If omitted, returns all active items. |
versioned |
bool |
No | Default false. When false (and since-version is omitted), returns the unversioned response shape (no currentVersion/version/lastChanged/isDeleted). When true, returns the versioned envelope documented below. Ignored — treated as true — whenever since-version is provided, since a diff response requires version metadata. Not listed in Swagger/OpenAPI; kept for backward compatibility with existing integrations. |
name |
string |
No | Returns active treatment groups with matching name. |
disease-group |
string |
No | Returns active treatment groups that contains the disease group. |
jurisdiction |
string |
No | Returns active treatment groups that contains the jurisdiction code. |
indication |
string |
No | Returns active treatment groups that contains the indication code. |
vare-nr |
string |
No | Returns active treatment groups that contains the FEST/Varenummer. |
effective-date |
string YYYY-MM-DD |
No | Returns treatment groups that is active on the given date. |
ignore-fest-validation |
bool |
No | Default false. When false, products without a resolved FEST match are dropped from each treatmentAlternatives[].products array. A group that had at least one product before filtering but is left with zero across every alternative is omitted entirely; a group with no products to begin with is unaffected. When true, returns all products unfiltered and adds isInFest to each product. |
Response
Default (no since-version, no versioned=true) — unversioned shape:
{
"entries": [
{
"treatmentGroup": { ... }
}
]
}
With versioned=true or since-version — versioned shape:
{
"currentVersion": 5,
"entries": [
{
"treatmentGroup": { ... },
"version": 5,
"lastChanged": "2025-04-10T08:30:00Z",
"isDeleted": false
}
]
}
Without since-version: Returns all non-deleted items. isDeleted is always false (only
present when the versioned shape is used).
With since-version: Always returns the versioned shape, with items with version > N,
including deleted items. isDeleted may be true either because the item was removed from the
source system or because all of its products lost their FEST match and the item is no longer
visible in a default full response. A later FEST match can return the item again with
isDeleted: false.
Example
# Get all (unversioned, default)
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/treatment-group
# Get all (versioned envelope)
curl -H "X-API-KEY: key" \
"https://api.example.com/api/v1/treatment-group?versioned=true"
# Get changes since version 3 (always versioned)
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/treatment-group?since-version=3
# Filter on indication code
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/treatment-group?indication=G35
GET /api/v1/treatment-group/
Returns a treatment group.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Returns an active treatment group with matching id. |
versioned |
bool |
No | Default false. When false, returns the plain treatmentGroup object without version/lastChanged/isDeleted. When true, wraps the response with version metadata (see below). Not listed in Swagger/OpenAPI; kept for backward compatibility with existing integrations. |
ignore-fest-validation |
bool |
No | Default false. When false, products without a resolved FEST match are dropped. 404 Not Found is returned only if the group had at least one product before filtering but is left with zero across every alternative afterward; a group with no products to begin with is returned unaffected. When true, returns all products unfiltered and adds isInFest to each product. |
Response
Default (no versioned=true) — unversioned shape (shown below without the version/
lastChanged/isDeleted fields present only in the versioned shape):
{
"treatmentGroup": {
"id": "...",
"name": "...",
"description": "...",
"diseaseGroup": "...",
"jurisdictions": [
{
"system": "...",
"code": "100022",
"display": "Helse Nord RHF"
}
],
"validityPeriod": {
"start": "...",
"end": "..."
},
"reviewDate": "...",
"indications": [
{
"coding": [
{
"system": "...",
"code": "C500",
"display": "..."
}
],
"text": null
}
],
"treatmentAlternatives": [
{
"rank": 1,
"treatmentId": "...",
"name": "Atezolizumab (Tecentriq)",
"description": "...",
"products": [
{
"identifiers": [
{
"type": "FEST/LegemiddelPakning/Varenummer",
"value": "448992",
"display": "Atezolizumab 420 mg/14 ml koncentration til injeksjonsvæske, injeksjon"
},
{
"type": "FEST/LegemiddelPakning/Id",
"value": "3F2504E0-4F89-11D3-9A0C-0305E82C3301",
"display": "Atezolizumab 420 mg/14 ml koncentration til injeksjonsvæske, injeksjon"
},
{
"type": "FEST/LegemiddelMerkevare/Id",
"value": "7C9E6679-7425-40DE-944B-E07FC1F90AE7",
"display": "Atezolizumab 420 mg/14 ml koncentration til injeksjonsvæske, injeksjon"
},
{
"type": "FEST/LegemiddelVirkestoff/Id",
"value": "E4EAAAF2-D142-11E1-B3E4-080027620CDD",
"display": "Atezolizumab"
}
]
}
]
}
]
}
}
With versioned=true — adds the version metadata around the same treatmentGroup object:
{
"treatmentGroup": { ... },
"version": 1,
"lastChanged": "2026-06-23T09:17:13.113Z",
"isDeleted": false
}
Explanation of content of indetifiers:
| System | Description |
|---|---|
FEST/LegemiddelPakning/Varenummer |
The Norwegian pharmacy article number (varenummer) for the drug package, as registered in FEST. |
FEST/LegemiddelPakning/Id |
The FEST internal identifier for the drug package (LegemiddelPakning). |
FEST/LegemiddelMerkevare/Id |
The FEST internal identifier for the drug's brand/proprietary product (Legemiddel Merkevare). |
FEST/LegemiddelVirkestoff/Id |
The FEST internal identifier for the drug's active substance (Legemiddel Virkestoff). There may be multiple FEST/LegemiddelVirkestoff/Id for each product. |
Each identifier carries an optional display with the human-readable name of the referenced
FEST entry: the package name (NavnFormStyrke) for the Varenummer and LegemiddelPakning/Id
entries, the brand name (NavnFormStyrke) for LegemiddelMerkevare/Id, and the plain substance
name (Virkestoff.Navn) for LegemiddelVirkestoff/Id. display is omitted when FEST has no name
for the entry (e.g. the Missing sentinel, or substances referenced without a strength value).
FEST supplies its own internal ids (LegemiddelPakning/Id, LegemiddelMerkevare/Id,
LegemiddelVirkestoff/Id) as a string with an ID_ prefix, e.g.
ID_3F2504E0-4F89-11D3-9A0C-0305E82C3301. This API stores those ids internally as Guid and
returns them in value as the bare uppercase Guid without the ID_ prefix (e.g.
3F2504E0-4F89-11D3-9A0C-0305E82C3301). The Varenummer identifier is unaffected — it is a plain
article number, not a FEST ID_-prefixed id.
GET /api/v1/reimbursement-group
Returns reimbursement group data (legal basis, indications, covered products). Supports filtering and version-based diff queries.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
since-version |
long |
No | If provided, returns only items changed since this version. If omitted, returns all active items. |
versioned |
bool |
No | Default false. When false (and since-version is omitted), returns the unversioned response shape (no currentVersion/version/lastChanged/isDeleted). When true, returns the versioned envelope documented below. Ignored — treated as true — whenever since-version is provided. Not listed in Swagger/OpenAPI; kept for backward compatibility with existing integrations. |
indication |
string |
No | Returns active reimbursement groups that contains the indication code. |
ignore-fest-validation |
bool |
No | Default false. When false, products without a resolved FEST match are dropped from each group's products array. A group that had at least one product before filtering but is left with zero afterward is omitted entirely; a group with no products to begin with is unaffected. When true, returns all products unfiltered and adds isInFest to each product. |
Response
Default (no since-version, no versioned=true) — unversioned shape:
{
"entries": [
{
"reimbursementGroup": {
"id": "...",
"legalBasis": {
"coding": [
{
"system": "...",
"code": "950",
"display": "H-resept"
}
]
},
"indications": [
{
"coding": [
{
"system": "...",
"code": "G35",
"display": "Multiple sclerosis"
}
]
}
],
"products": [
{
"identifiers": [
{
"type": "FEST/LegemiddelPakning/Varenummer",
"value": "166028",
"display": "Ocrevus 300 mg/1 ml koncentration til injeksjonsvæske, injeksjonssprøyte"
},
{
"type": "FEST/LegemiddelPakning/Id",
"value": "3F2504E0-4F89-11D3-9A0C-0305E82C3301",
"display": "Ocrevus 300 mg/1 ml koncentration til injeksjonsvæske, injeksjonssprøyte"
},
{
"type": "FEST/LegemiddelMerkevare/Id",
"value": "7C9E6679-7425-40DE-944B-E07FC1F90AE7",
"display": "Ocrevus 300 mg/1 ml koncentration til injeksjonsvæske, injeksjonssprøyte"
},
{
"type": "FEST/LegemiddelVirkestoff/Id",
"value": "E4EAAAF2-D142-11E1-B3E4-080027620CDD",
"display": "Ocrelizumab"
}
]
}
]
}
}
]
}
With versioned=true or since-version — versioned shape (same reimbursementGroup object,
wrapped with version metadata):
{
"currentVersion": 5,
"entries": [
{
"reimbursementGroup": { ... },
"version": 5,
"lastChanged": "2025-04-10T08:30:00Z",
"isDeleted": false
}
]
}
Behavior is identical to the treatment-group endpoint. The products array is empty if upstream provides no article number for the authorization.
Example
# Get all (unversioned, default)
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/reimbursement-group
# Get all (versioned envelope)
curl -H "X-API-KEY: key" \
"https://api.example.com/api/v1/reimbursement-group?versioned=true"
# Get changes since version 3 (always versioned)
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/reimbursement-group?since-version=3
# Filter on indication code
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/reimbursement-group?indication=G35
GET /api/v1/reimbursement-group/
Returns a reimbursement group (legal basis, indications, covered products).
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Returns an active reimbursement group with matching id. |
versioned |
bool |
No | Default false. When false, returns the plain reimbursementGroup object without version/lastChanged/isDeleted. When true, wraps the response with version metadata (see below). Not listed in Swagger/OpenAPI; kept for backward compatibility with existing integrations. |
ignore-fest-validation |
bool |
No | Default false. When false, products without a resolved FEST match are dropped. 404 Not Found is returned only if the group had at least one product before filtering but is left with zero afterward; a group with no products to begin with is returned unaffected. When true, returns all products unfiltered and adds isInFest to each product. |
Response
Default (no versioned=true) — unversioned shape:
{
"reimbursementGroup": {
"id": "...",
"legalBasis": {
"coding": [
{
"system": "...",
"code": "950",
"display": "H-resept"
}
]
},
"indications": [
{
"coding": [
{
"system": "...",
"code": "G35",
"display": "Multiple sclerosis"
}
]
}
],
"products": [
{
"identifiers": [
{
"type": "FEST/LegemiddelPakning/Varenummer",
"value": "166028",
"display": "Ocrevus 300 mg/1 ml koncentration til injeksjonsvæske, injeksjonssprøyte"
}
]
}
]
}
}
With versioned=true — adds the version metadata around the same reimbursementGroup object:
{
"reimbursementGroup": { ... },
"version": 5,
"lastChanged": "2025-04-10T08:30:00Z",
"isDeleted": false
}
GET /api/v1/last-updated
Reports when treatment groups and reimbursement groups last changed, without returning any group data. Use it as a cheap check before a full download. See Checking for changes before downloading.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
since |
string YYYY-MM-DD |
No | Inclusive start date, interpreted as 00:00:00 UTC on that date. Use the ISO format YYYY-MM-DD. A value that is not a valid date, such as a datetime or 2026-02-30, returns 400. A valid future date is accepted and reports no changes. |
Response
200 OK (application/json) with since=2026-10-06:
{
"lastUpdated": "2026-10-06T03:12:44Z",
"treatmentGroups": { "lastUpdated": "2026-10-06T03:12:44Z", "hasChanges": true },
"reimbursementGroups": { "lastUpdated": "2026-10-01T03:10:02Z", "hasChanges": false },
"hasChanges": true
}
| Field | Description |
|---|---|
lastUpdated |
UTC time of the most recent change across both resource types, or null when there is no data. |
treatmentGroups.lastUpdated, reimbursementGroups.lastUpdated |
UTC time of the most recent change for that resource type, or null when there is no data. |
treatmentGroups.hasChanges, reimbursementGroups.hasChanges |
true when that resource type changed at or after since (00:00:00 UTC). Omitted when since is not supplied. |
hasChanges |
true when either resource type has changes. Omitted when since is not supplied. |
- A change is a group being added, updated, or removed (soft-deleted), and also a FEST update that
changes whether a group's products are matched in FEST. These are all reported even when the
default responses of the group endpoints would hide them. A sync that changes nothing does not
affect
lastUpdated. - The comparison is inclusive: a change at exactly 00:00:00 UTC on the
sincedate counts. - With no data, both
lastUpdatedvalues arenulland, withsince, everyhasChangesisfalse.
| Status | Meaning |
|---|---|
200 |
The change metadata above. |
400 |
Invalid since (application/problem+json). |
401 |
Missing or invalid credentials. |
Example
# Latest change times only
curl -H "X-API-KEY: key" https://api.example.com/api/v1/last-updated
# Did anything change on or after 2026-10-06 (UTC)?
curl -H "X-API-KEY: key" "https://api.example.com/api/v1/last-updated?since=2026-10-06"
HelseID (v2) endpoints
The v2 endpoints expose the same data and behave identically to their v1 counterparts. The differences are:
- Authentication — a HelseID access token sent with a DPoP proof, instead of the
X-API-KEYheader. See HelseID (DPoP). - No rate limiting — v2 endpoints are not part of the per-API-key rate limiter described below.
Response shapes, query parameters, and versioned-diff semantics are identical to the v1 endpoints above.
For both API versions, isInFest is omitted from products by default. It is present as a boolean
only when the request includes ignore-fest-validation=true:
{
"identifiers": [
{
"type": "FEST/LegemiddelPakning/Varenummer",
"value": "166028"
}
],
"isInFest": false
}
When there is no resolved FEST match (as above), no display is present on the identifiers.
GET /api/v2/treatment-group
Returns treatment group data. Supports the same filtering and version-based diff queries as GET /api/v1/treatment-group.
Parameters
Identical to GET /api/v1/treatment-group (since-version, versioned, name, disease-group, jurisdiction, indication, vare-nr, effective-date, ignore-fest-validation).
Response
Same envelope and entry shape as GET /api/v1/treatment-group.
Example
# Get all (authenticated with a HelseID DPoP access token + proof)
curl -H "Authorization: DPoP <access-token>" \
-H "DPoP: <proof-jwt>" \
https://api.example.com/api/v2/treatment-group
# Get changes since version 3
curl -H "Authorization: DPoP <access-token>" \
-H "DPoP: <proof-jwt>" \
https://api.example.com/api/v2/treatment-group?since-version=3
GET /api/v2/treatment-group/
Returns a single treatment group. Same id parameter and response shape as GET /api/v1/treatment-group/{id}.
GET /api/v2/reimbursement-group
Returns reimbursement group data. Supports the same filtering and version-based diff queries as GET /api/v1/reimbursement-group.
Parameters
Identical to GET /api/v1/reimbursement-group (since-version, versioned, indication, ignore-fest-validation).
Response
Same envelope and entry shape as GET /api/v1/reimbursement-group.
Example
curl -H "Authorization: DPoP <access-token>" \
-H "DPoP: <proof-jwt>" \
https://api.example.com/api/v2/reimbursement-group?indication=G35
GET /api/v2/reimbursement-group/
Returns a single reimbursement group. Same id parameter and response shape as GET /api/v1/reimbursement-group/{id}.
GET /api/v2/last-updated
Same parameters, response, and status codes as GET /api/v1/last-updated, authenticated with a HelseID DPoP access token and proof.
curl -H "Authorization: DPoP <access-token>" \
-H "DPoP: <proof-jwt>" \
"https://api.example.com/api/v2/last-updated?since=2026-10-06"
Response Envelope
By default (no versioned=true, no since-version), all endpoints return the unversioned
shape — no currentVersion/version/lastChanged/isDeleted fields. Add versioned=true
(or since-version, which always implies the versioned shape) to get the versioned envelope
documented below. See Versioning and diff queries for details.
// GET /api/v1&v2/reimbursement-group — unversioned (default)
{
entries: Array<{
reimbursementGroup: T; // The reimbursement group data object
}>;
}
// GET /api/v1&v2/reimbursement-group/{id} — unversioned (default)
{
reimbursementGroup: T; // The reimbursement group data object
}
// GET /api/v1&v2/reimbursement-group and GET /api/v1&v2/reimbursement-group/{id} — versioned (versioned=true or since-version)
{
currentVersion: number; // The server's current data version (list endpoint only)
entries: Array<{
reimbursementGroup: T; // The reimbursement group data object
version: number; // Version when this entry last changed
lastChanged: string; // ISO-8601 timestamp of last change
isDeleted: boolean; // Whether this entry has been removed
}>;
}
// GET /api/v1&v2/treatment-group — unversioned (default)
{
entries: Array<{
treatmentGroup: T; // The treatment group data object
}>;
}
// GET /api/v1&v2/treatment-group/{id} — unversioned (default)
{
treatmentGroup: T; // The treatment group data object
}
// GET /api/v1&v2/treatment-group and GET /api/v1&v2/treatment-group/{id} — versioned (versioned=true or since-version)
{
currentVersion: number; // The server's current data version (list endpoint only)
entries: Array<{
treatmentGroup: T; // The treatment group data object
version: number; // Version when this entry last changed
lastChanged: string; // ISO-8601 timestamp of last change
isDeleted: boolean; // Whether this entry has been removed
}>;
}
FEST identifier changes can update the owning group's version and lastChanged. Replaying
unchanged SHI data after that refresh does not itself count as another group change.
currentVersion can still advance because each SHI run allocates a global version.
HTTP Status Codes
| Code | Meaning |
|---|---|
200 OK |
Success |
400 Bad Request |
Missing or invalid parameter |
401 Unauthorized |
Missing or invalid API key, or invalid/expired/replayed HelseID token or DPoP proof |
403 Forbidden |
Authenticated via HelseID, but the access token is missing the required nhn:legemiddelgrunndata/api scope |
404 Not found |
Data not found in DB |
429 Too Many Requests |
Rate limit exceeded (see Rate Limiting) |
500 Internal Server Error |
Server error |
Error Format
Unhandled server errors return 500 Internal Server Error with the following JSON body:
{
"correlationId": "3f2b1c9a-4d5e-6f70-8192-a3b4c5d6e7f8"
}
In the Development environment, the body also includes message and stackTrace.
Every response — successful or error — includes an X-Correlation-Id response header. On 500 responses the same value is included in the body as correlationId. Clients may pass their own correlation ID via the X-Correlation-Id request header; it will be reused in the response and in server logs.
If the client aborts the request, no error response is written.
Rate Limiting
The data endpoints (/api/v1/* and /api/v2/*) are rate limited to 120 requests per minute (sliding window). The v1 endpoints are limited per API key; the v2 (HelseID) endpoints are limited per client IP address.
When the limit is exceeded, the API responds with 429 Too Many Requests:
{ "error": "Rate limit exceeded. Try again later." }
The response includes a Retry-After header with the length of the rate-limit window in seconds. Waiting that long guarantees the request budget has fully replenished.
Notes:
- Requests to v1 without a valid API key (missing or unknown) share a single bucket, which also prevents API-key brute-forcing.
- The limit is enforced per server instance (in-memory), so the effective limit across all instances can be higher than 120 requests per minute.
- The Swagger UI is not rate limited.