General rules
Introduction
An API provides an interface for presentation layers to interact with a data layer, decoupling them from specific underlying systems and technologies. To ensure consistent and predictable interactions, a data layer's API must adhere to several rules.
Documentation
The API is as good as the accompanying documentation. The documentation of the API must be easily discoverable, searchable and publicly accessible. It is often the primary resource for developers of presentation layers during implementation.
API documentation MUST be provided in the form of an OpenAPI definition document which conforms to the OpenAPI Specification, version 3 or later.
See Documentation in the API Design Rules for more information.
Versioning
The API may evolve over time. It MUST be versioned as follows:
- The URI base path MUST include a major version number prefixed with
v. Example:https://example.org/v1,https://example.org/v2. - The API MUST send the full version number of the API in the
API-Versionheader. Example:API-Version: 1.2.3,API-Version: 2.1.0.
Example
An example request from a presentation layer:
GET /v1/entities/objects/1234 HTTP/2
Host: example.org
This tells the API that the presentation layer is requesting a resource of version 1 (v1) of the API.
An example of the response headers of the API:
HTTP/2 200 OK
API-Version: 1.2.3
The response indicates that the exact version of the API is 1.2.3.
The data layer MUST facilitate the transition between versions. For example, the data layer SHOULD publish a deprecation schedule and a changelog. See Versioning in the API Design Rules for more information.
Status codes
The API MUST use common HTTP status codes in its responses: 2xx for success, 3xx for redirects, 4xx for errors caused by the presentation layer and 5xx for errors caused by the API.
The following table lists common status codes:
| Status | Description |
|---|---|
200 | The request has succeeded. |
304 | The requested resource is not modified. |
400 | The request is invalid. |
404 | The requested resource does not exist. |
406 | The request could not be accepted according to the content negotiation headers. |
415 | The media type in the request is not supported. |
429 | The server has received too many requests. |
500 | A server error has occurred. |
503 | The server is under maintenance. |
Media types
The API MUST use media types to enable open and extensible content negotiation.
- A presentation layer MAY send the
Acceptheader in its request. Its value MUST conform to the HTTP semantics. For example, the value may contain a specific media type (e.g.application/json), a media type range (e.g.application/json, application/problem+json) or any media type (*/*). - If a presentation layer sends the
Acceptheader with a media type the API does not support, the API MUST respond with a415 Unsupported Media Typestatus code. - The API MUST send the media type of its response in the
Content-Typeheader. Its value MUST conform to the HTTP semantics. - The API MUST send the
Vary: Acceptheader to indicate to a presentation layer that it supports content negotiation for media types. This tells a presentation layer that changing the value of theAcceptheader in a request will yield a different representation of a resource. - The API MUST send its responses as JSON; it is easy to parse and supported natively in most programming languages.
To do: make JSON-LD the default, not JSON.
Example
An example request from a presentation layer:
GET /v1/entities/objects/1234 HTTP/2
Host: example.org
Accept: application/json
This tells the API that the presentation layer prefers the response to be serialized as JSON.
An example of the response headers of the API:
HTTP/2 200 OK
Content-Type: application/json
Vary: Accept
The response indicates that the body is serialized as JSON and that a new request to the same resource with a different Accept header value will result in a different representation of the resource.
Languages
Heritage information is available in one or more languages, such as Dutch or English. The API MUST allow presentation layers to request information in a preferred language, even if the API only exposes information in one language.
- A presentation layer MAY send the
Accept-Languageheader in its request to indicate which language it prefers. Its value MUST conform to the HTTP semantics. - If a presentation layer sends the
Accept-Languageheader with a language tag that the API does not support, the API MUST respond with a406 Not Acceptablestatus code. - If a presentation layer does not send the
Accept-Languageheader, the API MUST send its content in its default language, defined by the data layer. - The API MUST send the language tag of the content in the
Content-Languageheader. Its value MUST conform to the HTTP semantics. - The API MUST NOT serve its content in different languages in one response. Instead, a presentation layer MUST issue separate requests for each language, with different
Accept-Languageheader values. - The API MUST send the
Vary: Accept-Languageheader to indicate to a presentation layer that it supports content negotiation for languages. This informs a presentation layer that changing the value of theAccept-Languageheader in a request will yield a different, language-aware representation of a resource.
Example
An example request from a presentation layer:
GET /v1/entities/objects/1234 HTTP/2
Host: example.org
Accept-Language: nl
This tells the API that the presentation layer prefers the content to be in Dutch.
An example of the response headers of the API:
HTTP/2 200 OK
Content-Language: nl
Vary: Accept-Language
The response indicates that the content is in Dutch and that a new request to the same resource with a different Accept-Language header value will result in a different representation of the resource.
Character encoding
Character encoding defines how characters are converted into bytes by the data layer for transmission to a presentation layer. The data layer MUST encode all API payload responses using UTF-8, except for payloads that are binary by nature, such as images.
Compression
Compression reduces the size of a response body as it is transmitted to a presentation layer, improving performance and reducing bandwidth use. The API SHOULD support compression. This section lists the primary requirements — see HTTP Semantics for more information.
- A presentation layer MAY send the
Accept-Encodingheader in its request to indicate which compression schemes it supports, such asgzip,br,deflateorzstd. Its value MUST conform to the HTTP semantics. - If a presentation layer sends the
Accept-Encodingheader, the API MAY compress the response body using one of the schemes the presentation layer supports. The API MUST then send theContent-Encodingheader to indicate which scheme it used; its value MUST conform to the HTTP semantics. - If a presentation layer does not send the
Accept-Encodingheader, or requests only schemes the API does not support, the API MUST send the response body uncompressed, without aContent-Encodingheader. - The API MUST send the
Vary: Accept-Encodingheader to indicate to a presentation layer that responses can differ based on the value of theAccept-Encodingrequest header. This informs a presentation layer that changing the value of theAccept-Encodingheader in a request will yield a differently compressed representation of a resource.
Example
An example request from a presentation layer:
GET /v1/entities/objects/1234 HTTP/2
Host: example.org
Accept-Encoding: gzip, br
This tells the API that the presentation layer can handle the compressed response body with either gzip or br.
An example of the response headers of the API:
HTTP/2 200 OK
Content-Type: application/json
Content-Encoding: br
Vary: Accept-Encoding
The response indicates that the body is a JSON representation compressed with Brotli (br). The Vary: Accept-Encoding header indicates that a new request to the same resource with a different Accept-Encoding header value will result in a differently compressed representation of the resource.
Caching
Caching is a mechanism where presentation layers store responses from the API to reuse them for subsequent requests. The API SHOULD support caching via HTTP headers; it enhances performance by reducing server load and latency. This section lists the primary requirements — see HTTP Caching for more information.
- The API SHOULD send the
Cache-Controlheader, to make clear to a presentation layer whether information can be cached and, if so, for how long (e.g. 1 hour, 1 day or 1 week). Its value MUST conform to the HTTP Caching standard. - The API SHOULD send the
ETagheader with a unique fingerprint of a resource. When the resource changes, the API MUST change its fingerprint. - A presentation layer MAY send the
If-None-Matchheader in its request with one or more ETag values returned by the API. If the resource still has one of the provided ETags, the API MUST respond with the304 Not Modifiedstatus code. Otherwise, the API MUST return the modified resource with a newETagheader value. - The API SHOULD send the
Last-Modifiedheader with a timestamp of when the resource was last modified. Its value MUST conform to the HTTP semantics. - A presentation layer MAY send the
If-Modified-Sinceheader in its request. If the resource has not been modified since that time, the API MUST respond with the304 Not Modifiedstatus code. Otherwise, the API MUST return the modified resource with a newLast-Modifiedheader value. - If a presentation layer sends both the
If-None-MatchandIf-Modified-Sinceheaders in the same request, the API MUST acceptIf-None-Matchand ignoreIf-Modified-Since.
Example
An example of the response headers of the API:
HTTP/2 200 OK
Cache-Control: max-age=3600
ETag: "xyz"
Last-Modified: Fri, 28 Aug 2026 04:58:08 GMT
The response indicates that this resource can be cached for 1 hour (max-age=3600) without checking the API, has ETag xyz, and was last modified on Friday 28 August 2026.
Rate limiting
Rate limiting is a traffic control mechanism that caps the number of requests a presentation layer can make to the API within a specific time window. It protects the data layer's infrastructure from overload and abuse. This section lists the primary requirements — see Retry-After and RateLimit header fields for HTTP for more information.
- The API SHOULD support rate limiting. The data layer chooses a policy that best fits its situation. For example, the data layer may choose a rate limiting algorithm such as Token Bucket, Fixed Window Counter or Sliding Window. The data layer may also choose a method for identifying presentation layers, for example based on IP address or the
User-Agentheader. - The API SHOULD send the
RateLimit-PolicyandRateLimitheaders to communicate its rate limiting policy and the current limits for a particular presentation layer. - The API SHOULD send a
429 Too Many Requestsstatus code if a presentation layer has sent too many requests within the current time window. - The API SHOULD send the
Retry-Afterheader if a presentation layer has sent too many requests within the current time window, to indicate how long the presentation layer ought to wait before making a new request.
Example
An example of the response headers of the API:
HTTP/2 200 OK
RateLimit-Policy: "default";q=100;w=60
RateLimit: "default";r=50;t=30
The RateLimit-Policy indicates that a presentation layer may send up to 100 requests (q=100) per 60 seconds (w=60). The RateLimit shows 50 of those remain (r=50) and that they may be used within the next 30 seconds (t=30).
An example of the response headers of the API when a rate limit has been reached:
HTTP/2 429 Too Many Requests
Retry-After: 120
The response indicates that a presentation layer has made too many requests and that it can try again after 120 seconds.
Cross-Origin Resource Sharing (CORS)
Cross-Origin Resource Sharing (CORS) is a mechanism that allows browser-based presentation layers to interact with the API. The API MUST support CORS by following the relevant requirements of the CORS specification. This section lists the primary requirements.
- A browser-based presentation layer MUST send the
Originheader to indicate the origin (scheme, hostname, and optionally port) that caused the request to the API. - The API MUST send the
Access-Control-Allow-Originheader. The value SHOULD be*to allow access to the API from any origin. If the API limits access (e.g. via theAuthorizationheader), a specific origin MUST be provided instead of*, and theAccess-Control-Allow-Credentials: trueheader MUST be send. - If the API sends a response with an
Access-Control-Allow-Originvalue with an explicit origin (rather than the*wildcard), the API MUST also send theVary: Originheader to indicate to a presentation layer that responses can differ based on the value of theOriginrequest header. - The API MUST send the
Access-Control-Allow-Methodsheader to specify which HTTP methods are permitted for cross-origin requests. - The API MUST send the
Access-Control-Allow-Headersheader to specify which HTTP headers are permitted for cross-origin requests. - The API MUST support the HTTP
OPTIONSmethod and send the headers above in response to anOPTIONSrequest (the preflight request pattern).
Example
An example of a preflight request from a presentation layer:
OPTIONS /v1/entities/objects/1234 HTTP/2
Host: example.org
Origin: https://mywebsite.nl
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Accept, Accept-Language, If-None-Match, If-Modified-Since, Origin
An example of the preflight response headers of the API:
HTTP/2 200 OK
Content-Length: 0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Accept, Accept-Language, If-None-Match, If-Modified-Since, Origin
Access-Control-Max-Age: 7200
An example of a regular, non-preflight request from a presentation layer:
GET /v1/entities/objects/1234 HTTP/2
Host: example.org
Origin: https://mywebsite.nl
An example of the regular, non-preflight response headers of the API:
HTTP/2 200 OK
Access-Control-Allow-Origin: *
Error handling
When an error occurs, the API MUST handle errors as follows:
- The API MUST return an appropriate HTTP status code, such as
404or500. - The API MUST return error information according to Problem Details for HTTP APIs. The error information MUST contain at least the following fields:
| Name | Data type | Cardinality | Description |
|---|---|---|---|
status | number | 1 | The HTTP status code. Example: 400, 404, 500. |
title | string | 1 | A short, human-readable summary of the problem type. For example: Resource not found for every resource that could not be retrieved by the API. |
detail | string | 1 | A human-readable explanation specific to this occurrence of the problem. |
To be discussed: add support for the type field, for machine-readable processing?
Example
An example of the response headers of the API:
HTTP/2 404 Not Found
Content-Type: application/problem+json
Content-Language: en
Note that the API has responded with the application/problem+json media type, even though the presentation layer may not have listed it in its Accept header. This is allowed by the HTTP Semantics. Also note the Content-Language header, to make clear that the content is in a particular language, per the Accept-Language header of the presentation layer or, if that header was not provided, the default language of the API.
An example of the response body of the API:
{
"status": 404,
"title": "Resource not found",
"detail": "No heritage object found with ID 1234"
}
Note that the title is generic (it applies to every resource) and that the detail is specific (it applies to a particular resource with a particular ID).
Open access
The API SHOULD be open to any presentation layer without technical constraints, such as authentication (e.g. via the Authorization header) or IP address filtering.
Access should only be restricted to designated presentation layers under specific circumstances, such as legal requirements. This specification does not dictate which technical constraints a data layer should implement; that decision rests with the data layer based on its specific needs.
To do: rephrase or remove this section - it's not yet clear what the requirements are.
Client identification
The data layer should be able to monitor the usage of its API and advise presentation layers in optimizing their implementations. The data layer should therefore be able to identify individual presentation layers.
- A presentation layer SHOULD send the
User-Agentheader in its requests. The header value SHOULD consist of the name of the system of the presentation layer, the version of its system and the URL of the owner of the presentation layer. The value SHOULD look like this:system/version (url), e.g.MyApp/1.7.6 (https://mymuseum.nl). See the HTTP semantics for more information. - The API MAY respond with a
400 Bad Requeststatus code if theUser-Agentheader in the request is missing or invalid.
Example
An example request from a presentation layer:
GET /v1/entities/objects/1234 HTTP/2
Host: example.org
User-Agent: MyApp/1.7.6 (https://mymuseum.nl)
This tells the API that the request comes from system MyApp, version 1.7.6, operated by a presentation layer with URL https://mymuseum.nl.