This document is licensed under
Creative Commons Attribution 4.0 International Public License
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.
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
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.
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.
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.
Batch endpoints are useful in situations where multiple related resources need to be retrieved in a single operation. Typical use cases include:
Orchestration across distributed data sources
Resources are often spread across multiple data sources, residing at different physical locations. A batch endpoint enables an application (or orchestration component) to perform distributed lookups efficiently, by reducing the complexity and client–server chatter.
For example: a client retrieves a list of persons from one source, which only contains address identifiers. Instead of performing a separate request per person to fetch the corresponding address, the client can submit a single batch request to the batch endpoint to retrieve all addresses at once.
Fetching multiple known resources
A client needs details for a set of known resource identifiers. Instead of issuing one request per identifier, the client can submit them in a single batch request, simplifying logic and reducing latency.
Parallel evaluation of collection filters
A reporting tool needs to query the same resource collection multiple times with different filter criteria (e.g. addresses in different postal code areas). A batch request allows all queries to be executed in parallel, returning the results in a single response.
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.
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.
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.
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.
null.items array.This design ensures resilience: a batch MAY contain both successful and unsuccessful entries, but the overall response is always delivered in a predictable format.
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
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.
/batching/path: Expose a standardized batch endpoint for collections
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.
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.
/batching/req-format: Use the standardized payload format for batch requests
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": [
{ ... },
{ ... },
{ ... }
]
}
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.
requests array and preserve the intended order of items.context property allows cross-cutting parameters to be added without introducing new endpoints or breaking existing clients./batching/res-format: Use the standardized payload format for batch responses
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:
null if not found or not accessible.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
}
]
}
]
}
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.
results array of the same length as requests, even if some entries are null or empty.results property.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.
/batching/req-singular: Specify a list of singular requests when batching singular resources
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] }
]
}
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.
key property for singular requests, with either a scalar or an ordered array depending on the resource’s identifier model.key property, and reject requests with malformed or incomplete identifiers./batching/res-singular: Return a list of singular results when batching singular resources
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:
null.Example response for two building resources, one found and one missing:
{
"results": [
{
"identificatie": "3b9710c4-6614-467a-ab82-36822cf48db1",
"naam": "Stadhuis",
"bouwjaar": 1978
},
null
]
}
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.
results array of the same length as the request, even when some entries are null.null entries gracefully, treating them as "resource not found" or "user not authorized".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.
/batching/req-collection: Specify a list of collection requests when batching collection resources
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 }}
]
}
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.
filter object for each collection request, with criteria that are valid for the targeted collection resource.results array, containing an items array of zero or more matching resources.items array rather than omitting the result./batching/res-collection: Return a list of collection results when batching collection resources
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": []
}
]
}
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.
items is empty.items arrays; servers may enforce limits per batch to protect performance.items without using pagination extensions.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.
/batching/err-req-invalid: Reject invalid batch requests using problem+json
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'."
}
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.
/batching/err-req-limit: Reject batch requests that exceed the maximum number of items
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."
}
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.
/batching/err-res-limit: Reject batch requests that would produce oversized responses
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."
}
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.
/batching/err-invalid-keys: Reject singular batch requests with invalid keys
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"
]
}
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.
invalidKeys array or equivalent field to indicate which keys failed, enabling corrective actions.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).
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"
}
]
}
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"
}
]
}
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.