API Batching module

Kennisplatform API's Standard
Candidate version

This version:
https://docs.geostandaarden.nl/api/cv-st-API-Strategie-mod-batching-20260311/
Latest published version:
https://docs.geostandaarden.nl/api/API-Strategie-mod-batching/
Previous published version:
https://docs.geostandaarden.nl/api/cv-st-API-Strategie-mod-batching-20251021/
Latest editor's draft:
https://geonovum.github.io/KP-APIs/API-strategie-modules/batching
Editor:
Joost Farla (Geonovum)
Authors:
Joost Farla (Geonovum)
Frank Terpstra (Geonovum)
Martin van der Plas (Logius)
Peter Haasnoot (Logius)
Pano Maria (Skemu)
Participate:
GitHub Geonovum/KP-APIs
File an issue
Commit history
Pull requests

Abstract

This module defines a standardized approach for performing batch operations in RESTful APIs. The objective of batching is to improve efficiency and scalability in situations where clients need to retrieve multiple resources in a single interaction.

The module specifies conventions for endpoint design, request and response formats, and error handling. By following these rules, API providers enable consistent client implementations, reduce network overhead, and improve overall performance and reliability in distributed environments.

Status of This Document

Noot

This is a proposed version authored by the ADR "orchestration" working group. Comments regarding this document may be shared through GitHub: https://github.com/Geonovum/KP-APIs/issues

1. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key words MAY, MUST, and SHOULD in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

2. Introduction

This section is non-normative.

Batching provides a standardized way for clients to retrieve multiple resources in a single call, reducing network overhead and simplifying access patterns in distributed systems.

The design rules in this document address common challenges such as the N+1 problem, orchestration across distributed sources, and predictable request–response mapping. They are intended to complement, not replace, existing singular and collection endpoints, ensuring consistency, scalability, and ease of use across APIs.

2.1 Scope

This module defines rules for implementing batch operations in RESTful APIs. Batching is introduced as a complementary access pattern to singular and collection endpoints, designed to improve efficiency, scalability, and client usability.

The design rules specify when and how to expose batch endpoints, how to structure requests and responses, and how to handle errors consistently. By standardizing these aspects, batching ensures predictable client–server interactions, reduces the risk of divergent implementations, and promotes interoperability across APIs in distributed architectures.

The scope of this batching module is limited to synchronous read operations. It does not include write operations, transactional guarantees across multiple requests, or global batch endpoints spanning multiple collections. Each batch item is processed independently, and partial successes or failures are explicitly allowed and expected. Asynchronous patterns like 'Claim-Check', 'Subscribe-Notification' and 'Call-back' are also out of scope.

2.2 Use cases

Batch endpoints are useful in situations where multiple related resources need to be retrieved in a single operation. Typical use cases include:

3. Summary

3.1 List of technical rules

4. Architecture

This section is non-normative.

Batching is an architectural pattern that complements the standard RESTful model of singular and collection endpoints. Its purpose is to improve efficiency in distributed systems by reducing network overhead and providing predictable request–response mappings in situations where multiple resources need to be fetched at once.

4.1 Data distribution

Modern APIs often operate in a distributed data landscape. A single logical query may require access to multiple underlying systems or databases, each with its own identifiers, query capabilities, and performance characteristics. This distribution introduces challenges:

Batching mitigates these issues by grouping multiple requests into a single network call, enabling the server to optimize access to underlying systems and respond in a coordinated manner.

4.2 The N+1 problem

A common inefficiency in distributed APIs is the N+1 problem. For example, a client queries one collection to retrieve N entities, then performs N additional singular lookups to fetch related resources. The result is N+1 total calls, each incurring network and processing overhead.

Batch endpoints provide a standardized way to replace N+1 calls with just two calls: one to retrieve the initial collection and one batch request to fetch all related resources in parallel. This pattern reduces latency and lowers the load on both clients and servers.

Batching is a performance optimization, not a replacement for singular or collection endpoints.

4.3 Transactionality

Batching is not a transaction mechanism. Each request item in a batch is processed independently, and the success or failure of one item does not affect the others. There is no guarantee of atomicity, consistency, isolation, or durability (ACID) across items in a batch.

This design ensures resilience: a batch MAY contain both successful and unsuccessful entries, but the overall response is always delivered in a predictable format.

4.4 Design principles

The following principles guide the architecture of batch endpoints:

This batching architecture extends the architecture document of het NL API Strategy as published on: docs.geostandaarden.nl/api/API-Strategie-architectuur

5. Batch endpoints

This section is non-normative.

Batch endpoints provide a standardized way to retrieve multiple resources in a single operation. They are designed to reduce the number of network round-trips and to mitigate the N+1 problem, where a client would otherwise need to issue many separate requests for related resources. Batch endpoints are exposed per collection and follow a uniform request and response format, ensuring predictable behavior across APIs.

5.1 Endpoint path

/batching/path: Expose a standardized batch endpoint for collections

Statement

Each collection resource that supports batch requests must expose a dedicated batch endpoint. The path of the batch endpoint must be constructed by appending the literal /_batch to the path of the collection resource. The batch endpoint must implement the POST method.

For the adressen collection, the batch endpoint is:

POST /adressen/_batch

Which collections require batch support depends on the context and client use cases, and is left up to the API designer.

Rationale

Using a fixed suffix (/_batch) makes batch endpoints predictable and discoverable for clients. By scoping the batch endpoint directly under the collection, the semantics of the request are unambiguous: all items in the batch relate to resources of that collection. Restricting the method to POST ensures consistent support across frameworks and avoids limitations of GET requests with bodies or overly long query strings.

Implications
  • Clients can deterministically construct the batch endpoint URL once the collection path is known, without relying on documentation or discovery mechanisms.
  • Servers must implement the batch endpoint as a distinct operation for each collection that supports batching.
  • The rule does not mandate batch support for all collections; API designers must decide based on use cases and performance needs.
How to test
  • Identify a collection resource that supports batching (e.g. /adressen).
  • Issue an HTTP POST request to the batch endpoint by appending /_batch to the collection path (e.g. POST /adressen/_batch).
  • Validate that the server accepts the request and returns a valid response (status code 200).
  • Validate that issuing the same request with a different HTTP method (e.g. GET) returns status code 405 Method Not Allowed.
  • Validate that the batch endpoint path follows the pattern {collection}/_batch consistently across all batch-enabled collections.

5.2 Request format

/batching/req-format: Use the standardized payload format for batch requests

Statement

A batch request must be sent as a JSON object containing a top-level requests property, whose value must be an ordered array of request items. Each entry in requests must be a JSON object that specifies exactly one selection criterion for that entry (see the specific rules for singular and collection requests). The order of the requests items is significant and preserved in the response.

The batch request must be submitted using the POST method and content type application/json.

An implementation-defined maximum applies to the number of entries in requests. Requests that exceed this maximum must be rejected.

The payload may include an additional top-level context property alongside requests, to provide contextual request criteria to apply on all request items (such as time travelling).

For the adressen collection, the batch endpoint is:

{
    "context": {
        "peildatum": "2025-09-12"
    },
    "requests": [
        { ... },
        { ... },
        { ... }
    ]
}
Rationale

Using a standardized JSON envelope with a requests property makes batch requests predictable and uniform across all collections. It ensures that client and server implementations can rely on a single pattern, reducing ambiguity and implementation errors. The use of POST with a request payload avoids limitations of URL length and tooling support for GET request bodies. The optional context property provides a forward-compatible mechanism for passing shared criteria without breaking the core structure.

Implications
  • Clients must always wrap batch input in a requests array and preserve the intended order of items.
  • Servers must validate the request structure and enforce the maximum number of allowed items.
  • Clients can depend on a deterministic mapping between request items and response items by array position.
  • The optional context property allows cross-cutting parameters to be added without introducing new endpoints or breaking existing clients.
How to test
  • Issue an HTTP POST request to a batch endpoint with Content-Type: application/json and a JSON body containing a requests array.
  • Validate that the server accepts the request and returns status code 200.
  • Validate that a request without a requests property is rejected with status code 400.
  • Validate that a request with an empty requests array is handled appropriately (either accepted with an empty results array, or rejected per server policy).
  • If the server supports a context property, validate that it can be included alongside requests and affects the response as documented.

5.3 Response format

/batching/res-format: Use the standardized payload format for batch responses

Statement

A batch response must be sent as a JSON object containing a top-level results property, whose value must be an ordered array of result items. The number of items in results must be exactly equal to the number of items in the input requests array. The order of the results items must be identical to the order of the corresponding request items.

Each entry in results must correspond to exactly one request item:

  • For singular requests, the entry must be the resource object if found, or null if not found or not accessible.
  • For collection requests, the entry must be a JSON object with an items array, which may be empty if no resources matched the criteria.

Servers may include additional top-level properties in the response (such as metadata), provided they do not alter the semantics of results.

Example response for a batch request combining a singular and a collection criterion:

{
    "results": [
        {
            "identificatie": "12345",
            "postcode": "1234AB",
            "huisnummer": 1
        },
        {
            "items": [
                {
                    "identificatie": "34567",
                    "postcode": "3456AB",
                    "huisnummer": 3
                }
            ]
        }
    ]
}
Rationale

Wrapping batch results in a results array guarantees a predictable, deterministic mapping between requests and responses. By enforcing equal length and order, clients can align response entries with their original requests by array index, without the need for correlation keys. Using null or empty arrays distinguishes clearly between "not found" and "no matches," improving client-side error handling and reducing ambiguity.

Implications
  • Clients can reliably map each request item to a result by array position, simplifying processing logic.
  • Servers must always return a results array of the same length as requests, even if some entries are null or empty.
  • Requests containing invalid keys must result in an error response.
  • Additional metadata can be included at the top level, but must not affect the semantics of the results property.
How to test
  • Issue an HTTP POST request to a batch endpoint with a valid requests array containing multiple items.
  • Validate that the response contains a results array with exactly the same number of items as the requests array.
  • Validate that the order of items in results corresponds to the order of items in requests.
  • For singular requests, validate that found resources are returned as objects and missing or inaccessible resources are returned as null.
  • For collection requests, validate that each result entry contains an items array (which may be empty if no resources matched).
  • Validate that any additional top-level properties in the response do not interfere with the results array structure.

6. Singular resources

This section is non-normative.

Singular resource requests target one specific resource by its unique identifier. This chapter defines how such requests are represented in batch operations, using the key property to identify resources consistently with their canonical URI. The rules ensure that both simple and compound identifiers can be handled reliably in batch requests and responses.

6.1 Singular request

/batching/req-singular: Specify a list of singular requests when batching singular resources

Statement

When requesting a batch of singular resources, a list of singular requests must be specified.

Each singular request must contain at least the unique identifier of the thing that the singular resource represents, using the key property. In most cases, things are identified by a single scalar value (string or number). However, they could also be identified by a compound value, consisting of multiple scalar values (in the resource URI, they would typically be represented by multiple path segments).

An example request for requesting two building resources in a single batch operation:

// POST https://api.example.org/v1/gebouwen/_batch
{
   "requests": [
      { "key": "3b9710c4-6614-467a-ab82-36822cf48db1" },
      { "key": "609b0651-acad-4091-9144-432621df8bf8" }
   ]
}

In case of a compound identifier, the value of the key property must be an array, in the exact same order as the path segments of the corresponding resource URI. The value type of the key property must be identical to the original value of the identifier. In case of a compound identifier, this could differ per part.

When a singular resource has a compound identifier, an array value must be provided. In this example, the first identifier part is a string, and the second part is numeric.

// POST https://api.example.org/v1/gebouwen/_batch
{
   "requests": [
      { "key": ["key-1-part-1", 123] },
      { "key": ["key-2-part-1", 456] }
   ]
}
Rationale

Using a standardized key property for singular requests ensures consistency across all batch-enabled collections. It mirrors the way singular resources are addressed in URIs, making the mapping intuitive for both clients and server implementers. Supporting both scalar and compound identifiers provides flexibility for resources that require multi-part keys, while preserving strict typing and ordering prevents ambiguity in request processing.

Implications
  • Clients must always supply the key property for singular requests, with either a scalar or an ordered array depending on the resource’s identifier model.
  • Clients must use the same data types for identifier values as the canonical resource definition (e.g., string vs. integer), ensuring compatibility with singular endpoints.
  • Servers must validate the structure and types of the key property, and reject requests with malformed or incomplete identifiers.
  • By preserving the exact order of identifier parts, clients and servers avoid mismatches and maintain consistency with URI path semantics.
How to test
  • Issue an HTTP POST request to a batch endpoint (e.g. /gebouwen/_batch) with a JSON body containing a requests array, where each item has a key property with a valid identifier.
  • Validate that a response with status code 200 is returned.
  • Validate that the response contains a results array with the same number of items as the requests array.
  • For resources with compound identifiers, issue a request where the key property is an array of values in the correct order and with the correct data types.
  • Validate that compound keys are processed correctly by verifying the returned resource matches the requested identifier parts.
  • Issue a request with an invalid or malformed key and validate that the server returns a 400 Bad Request response.

6.2 Singular response

/batching/res-singular: Return a list of singular results when batching singular resources

Statement

When returning the results of a batch of singular requests, the response must contain a top-level results property. The value of results must be an array with exactly the same number of items, and in the same order, as the requests array in the corresponding request.

Each entry in results must correspond to exactly one request item:

  • If the resource exists and the user is authorized to access the resource, the entry must be the resource object.
  • If the resource does not exist or the user is not authorized to access the resource, the entry must be null.

Example response for two building resources, one found and one missing:

{
   "results": [
      {
         "identificatie": "3b9710c4-6614-467a-ab82-36822cf48db1",
         "naam": "Stadhuis",
         "bouwjaar": 1978
      },
      null
   ]
}
Rationale

Returning a results array of equal length and order ensures deterministic mapping between request and response items. Using null to represent missing or inaccessible resources simplifies client-side error handling and reduces ambiguity. Additionally, returning null for both non-existent and unauthorized resources prevents clients from inferring whether a resource exists but is protected, or simply does not exist. This consistent response pattern mitigates potential information disclosure risks.

Implications
  • Clients can map each response entry directly to its request by array index, without correlation keys or additional matching logic.
  • Servers must always produce a results array of the same length as the request, even when some entries are null.
  • Clients must handle null entries gracefully, treating them as "resource not found" or "user not authorized".
  • This structure enables batch responses to remain predictable, consistent, and easy to process in client libraries and tooling.
How to test
  • Issue an HTTP POST request to a batch endpoint with multiple singular requests (each containing a key property).
  • Validate that the response contains a results array with exactly the same number of items as the requests array.
  • Validate that the order of results matches the order of the original requests.
  • Request a mix of existing and non-existing resources and validate that existing resources return the resource object while non-existing resources return null.
  • Validate that null is returned for resources the user is not authorized to access (indistinguishable from non-existing resources).

7. Collection resources

This section is non-normative.

Collection resource requests allow clients to retrieve multiple resources that match a set of filter criteria. In batch operations, these requests are expressed with a standardized filter object. This chapter describes how such requests are structured, how results are returned, and how empty matches are represented. The rules align with existing collection query semantics while ensuring deterministic mapping between requests and responses.

7.1 Collection request

/batching/req-collection: Specify a list of collection requests when batching collection resources

Statement

When requesting a batch of collection resources, a list of collection requests must be specified.

Each collection request must contain a filter property, whose value is an object containing one or more filter criteria. These criteria must correspond to valid query parameters or attributes defined for the collection resource. A combination of multiple filter criteria in a single request is allowed, if the collection request supports it.

The structure of the filter object must follow the same typing rules as the underlying resource attributes, ensuring that values match the expected data types. An empty result set must be returned when no resources match the given filter criteria.

Example request when requesting two address collection resources in a single batch operation:

// POST https://api.example.org/v1/adressen/_batch
{
   "requests": [
      { "filter": { "postcode": "1234AB", "huisnummer": 1 }},
      { "filter": { "postcode": "3456XY", "huisnummer": 32 }}
   ]
}
Rationale

Using a standardized filter object for collection requests ensures consistency with existing collection endpoints, where filtering is already a core concept. This approach allows multiple sets of filter criteria to be evaluated in a single request, reducing the number of round-trips and eliminating the need for clients to reassemble paginated or mixed results manually. It also enables flexible querying while keeping batch requests predictable and self-contained.

Implications
  • Clients must provide a filter object for each collection request, with criteria that are valid for the targeted collection resource.
  • Servers must validate the filter object against the allowed fields and reject requests with unsupported or malformed filter criteria.
  • Each collection request in the batch produces exactly one result entry in the results array, containing an items array of zero or more matching resources.
  • Clients can rely on the position of results in the response to map back to the corresponding filter criteria without additional correlation logic.
  • If no resources match a filter, servers must return an empty items array rather than omitting the result.
How to test
  • Issue an HTTP POST request to a batch endpoint (e.g. /adressen/_batch) with a JSON body containing a requests array, where each item has a filter object with valid filter criteria.
  • Validate that a response with status code 200 is returned.
  • Validate that the response contains a results array with the same number of items as the requests array.
  • Validate that each result entry contains an items array (which may be empty if no resources match).
  • Issue a request with an invalid or unsupported filter criterion and validate that the server returns a 400 Bad Request response.

7.2 Collection response

/batching/res-collection: Return a list of collection results when batching collection resources

Statement

When returning the results of a batch of collection requests, the response must contain a top-level results property. The value of results must be an array with exactly the same number of items, and in the same order, as the requests array in the corresponding request.

Each entry in results must correspond to exactly one collection request and must be a JSON object containing an items property. The value of items must be an array of zero or more resource objects that match the specified filter criteria.

If no resources match a filter, the corresponding items array must be empty. The result entry itself must never be omitted.

Pagination of items within a result entry is currently not standardized. Implementations that support pagination may include additional fields (e.g. nextToken or nextLink) in the result entry. Such extensions must not alter the semantics of items and must be optional for clients to consume.

Example response for two address collection requests, where the first returns matches and the second none:

{
   "results": [
      {
         "items": [
            {
               "identificatie": "12345",
               "postcode": "1234AB",
               "huisnummer": 1
            },
            {
               "identificatie": "67890",
               "postcode": "1234AB",
               "huisnummer": 2
            }
         ]
         // optional, implementation-specific
         "nextToken": "eyJwYWdlIjoxfQ=="
      },
      {
         "items": []
      }
   ]
}
Rationale

Standardizing collection results as objects with an items array ensures predictable and uniform handling across all batch-enabled collections. It distinguishes clearly between “no results” (items is empty) and “request missing” (not allowed). Preserving array length and order maintains deterministic mapping between request filters and result sets. Optional pagination fields allow flexibility for large collections without constraining implementations to a single standard prematurely.

Implications
  • Clients can reliably map each collection request to its result by array position, simplifying client-side processing.
  • Servers must always return a result entry for each request, even if items is empty.
  • Clients must be prepared to handle large items arrays; servers may enforce limits per batch to protect performance.
  • Implementations that support pagination must document their chosen approach and ensure that clients can still consume items without using pagination extensions.
  • The lack of a standardized pagination mechanism means interoperability may vary until a common approach is agreed upon.
How to test
  • Issue an HTTP POST request to a batch endpoint with multiple collection requests (each containing a filter object).
  • Validate that the response contains a results array with exactly the same number of items as the requests array.
  • Validate that each entry in results is a JSON object containing an items property.
  • Validate that the order of results matches the order of the original requests.
  • Issue a request with a filter that matches no resources and validate that the corresponding result entry contains an empty items array (not null or omitted).

8. Error handling

This section is non-normative.

Batch endpoints must use the standardized Problem Details (problem+json) [rfc9457] format to represent errors. Problem details provide a machine-readable structure while still being human-readable, ensuring interoperability and consistency across APIs.

Errors are reported using an HTTP error status and a problem+json body. Item-level errors within a batch response must not trigger a batch-level error but must be represented in the results array.

8.1 Invalid request

/batching/err-req-invalid: Reject invalid batch requests using problem+json

Statement

If a batch request is malformed, contains invalid JSON, or fails schema validation, the server must reject it with status code 400 Bad Request and return a problem+json body.

// HTTP/1.1 400 Bad Request
// Content-Type: application/problem+json
{
    "type": "https://api.example.org/problems/invalid-request",
    "title": "Invalid request",
    "status": 400,
    "detail": "The request payload is missing the required property 'requests'."
}
Rationale

Explicitly rejecting invalid requests ensures clients receive clear feedback on how to correct errors, instead of undefined or partial behavior. Using the standardized problem+json format promotes interoperability across APIs.

Implications
  • Clients must validate requests before submission to avoid unnecessary rejections.
  • Servers must consistently return problem+json for all 400-level errors in batch requests.
  • Error responses must include human-readable details and machine-readable fields for automated handling.
How to test
  • Issue an HTTP POST request to a batch endpoint with malformed JSON (e.g. missing closing braces) and validate that the server returns status code 400 with a problem+json response.
  • Issue a request with a body that is missing the required requests property and validate that the server returns status code 400.
  • Validate that the error response contains the required problem+json fields: type, title, status, and detail.
  • Validate that the Content-Type header of the error response is application/problem+json.

8.2 Request limit exceeding

/batching/err-req-limit: Reject batch requests that exceed the maximum number of items

Statement

If the number of entries in requests exceeds the server-defined maximum, the server must reject the request with status code 400 Bad Request and return a problem+json body.

// HTTP/1.1 400 Bad Request
// Content-Type: application/problem+json
{
    "type": "https://api.example.org/problems/request-limit-exceeded",
    "title": "Request limit exceeded",
    "status": 400,
    "detail": "Batch requests are limited to 100 items, but 114 items were submitted."
}
Rationale

Exceeding the maximum number of request items is a client-side input error, not a payload overflow. Returning 400 Bad Request clarifies that the client must correct the request, while still using the standardized problem+json format for machine-readable feedback.

Implications
  • Servers must document their maximum batch size and enforce it consistently.
  • Clients must be prepared to split large batches into multiple smaller ones.
  • Validation of request size becomes part of standard request validation logic, not just transport-level checks.
How to test
  • Determine the server's documented maximum batch size (e.g. 100 items).
  • Issue an HTTP POST request to a batch endpoint with a requests array containing more items than the maximum allowed.
  • Validate that the server returns status code 400 with a problem+json response.
  • Validate that the error response indicates that the request limit was exceeded.
  • Issue a request with exactly the maximum number of items and validate that it is accepted (status code 200).

8.3 Response limit exceeding

/batching/err-res-limit: Reject batch requests that would produce oversized responses

Statement

If fulfilling a batch request would result in a response exceeding server-defined limits (for example, too many results or an oversized payload), the server must reject the request with status code 400 Bad Request and return a problem+json body.

// HTTP/1.1 400 Bad Request
// Content-Type: application/problem+json
{
    "type": "https://api.example.org/problems/response-limit-exceeded",
    "title": "Response limit exceeded",
    "status": 400,
    "detail": "The response would exceed the maximum allowed size of 10 MB."
}
Rationale

Requests that would result in an oversized response represent invalid client input rather than an infrastructure failure. Using 400 Bad Request makes it clear that the client must adjust filters or batch size. The standardized problem+json body provides actionable details.

Implications
  • Servers must define reasonable response size limits and enforce them consistently across batch endpoints.
  • Clients must anticipate possible rejections and adapt (e.g., by narrowing filter criteria or splitting requests).
  • Clear feedback helps prevent repeated oversized queries and supports monitoring of misuse or misconfiguration.
How to test
  • Issue an HTTP POST request to a batch endpoint with collection requests that would produce a very large combined response (e.g. filters that match many resources).
  • Validate that if the server enforces response size limits, it returns status code 400 with a problem+json response when the limit would be exceeded.
  • Validate that the error response indicates that the response limit was exceeded and provides guidance on how to adjust the request.
  • Issue a request with narrower filter criteria that produces a smaller response and validate that it is accepted.

8.4 Invalid keys

/batching/err-invalid-keys: Reject singular batch requests with invalid keys

Statement

If a singular batch request contains invalid keys (for example, an invalid UUID), the server must reject the entire batch with status code 400 Bad Request and return a problem+json body. The response must indicate which keys caused the failure.

// HTTP/1.1 400 Bad Request
// Content-Type: application/problem+json
{
    "type": "https://api.example.org/problems/invalid-keys",
    "title": "Invalid keys",
    "status": 400,
    "detail": "One or more provided keys are invalid.",
    "invalidKeys": [
        "not-a-uuid"
    ]
}
Rationale

Invalid keys represent client input errors. Rejecting the entire batch avoids returning mixed results that could confuse clients and ensures that request validation is strict and predictable. Including the list of failing keys helps clients identify and correct errors efficiently.

Implications
  • Servers must validate all keys before processing and reject the batch if any are invalid.
  • Clients must ensure that all identifiers conform to the expected format before submitting the request.
  • The error response may include an invalidKeys array or equivalent field to indicate which keys failed, enabling corrective actions.
How to test
  • Issue an HTTP POST request to a batch endpoint for singular resources with one or more invalid keys (e.g. a malformed UUID like "not-a-uuid").
  • Validate that the server returns status code 400 with a problem+json response.
  • Validate that the error response indicates which keys are invalid (e.g. via an invalidKeys array).
  • Validate that the entire batch is rejected, not just the invalid items.
  • Issue a request with all valid keys (even for non-existing resources) and validate that the server returns status code 200 (with null for non-existing resources in the results).

9. Existing standards

This section is non-normative.

A number of widely used APIs and standards support batching. Approaches vary along several axes: endpoint scope (per-collection vs global), payload shape (JSON envelope vs multipart/mixed), operation types (read-only vs mixed read/write), result mapping (position vs correlation IDs), and error semantics (uniform result vs per-subrequest HTTP response).

9.1 Elasticsearch Multi-Get

Elasticsearch exposes a per-index and global _mget endpoint that retrieves multiple documents by ID in a single call. Clients submit an array of document identifiers in the request payload and receive a result per requested identifier. The model is read-only, index-scoped by default, and result order corresponds to the request order.

Example request payload:

{
  "docs": [
    {
      "_id": "1"
    },
    {
      "_id": "2"
    }
  ]
}

9.2 OData $batch

OData defines a global $batch endpoint that can combine heterogeneous operations (reads and writes) into a single HTTP request. Historically, batching used media type multipart/mixed; OData v4 additionally defines a JSON batch request/response format as part of the [OData JSON Format]. Responses contain per-subrequest status, headers, and bodies, effectively embedding multiple HTTP exchanges in one envelope. Ordering and dependencies can be expressed.

Example request payload (JSON batch format):

{
  "requests": [
    {
      "id": "1",
      "method": "get",
      "url": "https://example.org/api/buildings/1"
    },
    {
      "id": "2",
      "method": "get",
      "url": "https://example.org/api/buildings/2"
    }
  ]
}

9.3 Google APIs

Several Google APIs accept batch requests as a single HTTP call with media type multipart/mixed, where each part is a nested HTTP request. This enables combining multiple heterogeneous operations; responses mirror the embedded HTTP results. The format prioritizes transport flexibility but is more complex to implement efficiently than a pure-JSON envelope.

9.4 Relation to this module

The working group analyzed the standards described above and made deliberate trade-offs to optimize for simplicity, predictability, and the specific needs of Dutch government APIs.

9.4.1 What was adopted

Aspect Adopted from How it is applied
Per-collection endpoint Elasticsearch Batch endpoints are scoped per collection (/_batch suffix), not global. This keeps semantics clear and avoids mixing unrelated resources.
JSON envelope with requests array OData The request payload uses a top-level requests property containing an ordered array, similar to OData's JSON batch format.
Positional result mapping Elasticsearch Results are returned in the same order as the requests, allowing clients to map responses by array index without correlation IDs.

9.4.2 What was not adopted

Aspect Present in Reason for exclusion
Global batch endpoint OData, Google APIs A single global endpoint that can address any resource increases complexity and reduces predictability. Per-collection endpoints are simpler to implement, document, and secure.
Multipart/mixed format OData, Google APIs The multipart/mixed format embeds full HTTP requests and responses, adding parsing complexity. A pure JSON format is easier to implement, debug, and consume in modern client libraries.
Per-subrequest HTTP semantics OData, Google APIs Embedding HTTP status codes and headers per subrequest adds overhead and complexity. This module uses null for missing resources and a separate error response for batch-level failures, reducing ambiguity.
Correlation IDs OData Requiring clients to generate and match correlation IDs adds implementation burden. Positional mapping (by array index) achieves the same goal more simply.
Mixed read/write operations OData, Google APIs Supporting heterogeneous operations (GET, POST, PUT, DELETE) in a single batch increases transactional complexity and error handling. This module focuses on read-only batching.
Dependency expressions OData OData allows expressing dependencies between subrequests (e.g., use the result of request 1 in request 2). This adds significant complexity and is not needed for the read-only use case.

9.4.3 Alternative considered: collection filtering

The working group also evaluated collection filtering via query parameters as an alternative to a dedicated batch endpoint. Batch support could be implemented by passing a series of criteria as filters to an existing collection endpoint. However, this approach has significant drawbacks compared to the standards above:

  • URL length limitations may cause problems
  • Expressing a list of complex criteria in a URL is difficult
  • Clients would need to handle pagination
  • Clients would need to detect which objects yield results and map results back to criteria
  • Clients might need to re-group and/or re-sort results

A dedicated batch endpoint with a JSON request body avoids these issues while providing the same benefits as Elasticsearch and OData.

A. References

A.1 Normative references

[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174

A.2 Informative references

[OData JSON Format]
OData JSON Format Version 4.01. OASIS. 11 May 2020. URL: https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html
[rfc9457]
Problem Details for HTTP APIs. M. Nottingham; E. Wilde; S. Dalal. IETF. July 2023. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc9457
Kennisplatform API's Standard - Candidate version