Skip to main content Link Search Menu Expand Document (external link)

Introduction

The purpose of our RESTful API guidelines is to define standards to successfully establish “consistent API look and feel” quality.

These guidelines will, to some extent, remain work in progress as our work evolves, but development teams can confidently follow and trust them.

In case guidelines are changing, following rules apply:

  • existing APIs don’t have to be changed, but we recommend ita

  • clients of existing APIs have to cope with these APIs based on outdated rules

  • new APIs have to respect the current guidelines

Furthermore you should keep in mind that once an API becomes public externally available, it has to be re-reviewed and changed according to current guidelines - for sake of overall consistency.

Based on https://opensource.zalando.com/restful-api-guidelines

Conventions Used in These Guidelines

The requirement level keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” used in this document (case insensitive) are to be interpreted as described in RFC 2119.

General guidelines

SHOULD Provide API User Manual In addition to the API Specification, it is good practice to provide an API user manual to improve client developer experience, especially of engineers that are less experienced in using this API. A helpful API user manual typically describes the following API aspects:

API scope, purpose, and use cases concrete examples of API usage edge cases, error situation details, and repair hints architecture context and major dependencies - including figures and sequence flows MUST Write APIs in U.S. English

Security

MUST Secure Endpoints with OAuth 2.0 or Basic Auth Every secured API endpoint needs to be secured using OAuth 2.0 or Basic Auth.

Compatibility

MUST Don’t Break Backward Compatibility Change APIs, but keep all consumers running. Consumers usually have independent release lifecycles, focus on stability, and avoid changes that do not provide additional value. APIs are contracts between service providers and service consumers that cannot be broken via unilateral decisions.

There are two techniques to change APIs without breaking them:

  • follow rules for compatible extensions
  • introduce new API versions and still support older versions

We strongly encourage using compatible API extensions and discourage versioning (see Avoid Versioning below). The following guidelines for service providers (Prefer Compatible Extensions) and consumers (Prepare Clients To Not Crash On Compatible API Extensions) enable us (having Postel’s Law in mind) to make compatible changes without versioning.

There is a difference between incompatible and breaking changes. Incompatible changes are changes that are not covered by the compatibility rules below. Breaking changes are incompatible changes deployed into operation, and thereby breaking running API consumers. Usually, incompatible changes are breaking changes when deployed into operation. However, in specific controlled situations it is possible to deploy incompatible changes in a non-breaking way, if no API consumer is using the affected API aspects (see also Deprecation guidelines).

Please note that the compatibility guarantees are for the “on the wire” format. Binary or source compatibility of code generated from an API definition is not covered by these rules. If client implementations update their generation process to a new version of the API definition, it has to be expected that code changes are necessary.

SHOULD Prefer Compatible Extensions

API designers should apply the following rules to evolve RESTful APIs for services in a backward-compatible way:

  • Add only optional, never mandatory fields.
  • Never change the semantic of fields (e.g. changing the semantic from customer-number to customer-id, as both are different unique customer keys)
  • Input fields may have (complex) constraints being validated via server-side business logic. Never change the validation logic to be more restrictive and make sure that all constraints are clearly defined in description.
  • Enum ranges can be reduced when used as input parameters, only if the server is ready to accept and handle old range values too. Enum range can be reduced when used as output parameters.
  • Enum ranges cannot be extended when used for output parameters — clients may not be prepared to handle it. However, enum ranges can be extended when used for input parameters.
  • Support redirection in case an URL has to change (Moved Permanently/301).

MUST Prepare Clients To Not Crash On Compatible API Extensions

Service clients should apply the robustness principle:

  • Be conservative with API requests and data passed as input, e.g. avoid to exploit definition deficits like passing megabytes of strings with unspecified maximum length.
  • Be tolerant in processing and reading data of API responses, more specifically…

Service clients must be prepared for compatible API extensions of service providers:

  • Be tolerant with unknown fields in the payload (see also Fowler’s TolerantReader post), i.e. ignore new fields but do not eliminate them from payload if needed for subsequent PUT requests.
  • Be prepared that {x-extensible-enum} return parameter may deliver new values; either be agnostic or provide default behavior for unknown values.
  • Be prepared to handle HTTP status codes not explicitly specified in endpoint definitions. Note also, that status codes are extensible. Default handling is how you would treat the corresponding {x00} code (see RFC 7231 Section 6).
  • Follow the redirect when the server returns HTTP status code (Moved Permanently/301).

SHOULD Design APIs Conservatively

Designers of service provider APIs should be conservative and accurate in what they accept from clients:

  • Unknown input fields in payload or URL should not be ignored; servers should provide error feedback to clients via an HTTP 400 response code.
  • Be accurate in defining input data constraints (like formats, ranges, lengths etc.) — and check constraints and return dedicated error information in case of violations.
  • Prefer being more specific and restrictive (if compliant to functional requirements), e.g. by defining length range of strings. It may simplify implementation while providing freedom for further evolution as compatible extensions.

Not ignoring unknown input fields is a specific deviation from Postel’s Law (e.g. see also The Robustness Principle Reconsidered) and a strong recommendation. Servers might want to take different approach but should be aware of the following problems and be explicit in what is supported:

  • Ignoring unknown input fields is actually not an option for PUT, since it becomes asymmetric with subsequent GET response and HTTP is clear about the PUT replace semantics and default roundtrip expectations (see RFC 7231 Section 4.3.4). Note, accepting (i.e. not ignoring) unknown input fields and returning it in subsequent GET responses is a different situation and compliant to PUT semantics.
  • Certain client errors cannot be recognized by servers, e.g. attribute name typing errors will be ignored without server error feedback. The server cannot differentiate between the client intentionally providing an additional field versus the client sending a mistakenly named field, when the client’s actual intent was to provide an optional input field.
  • Future extensions of the input data structure might be in conflict with already ignored fields and, hence, will not be compatible, i.e. break clients that already use this field but with different type.

In specific situations, where a (known) input field is not needed anymore, it either can stay in the API definition with “not used anymore” description or can be removed from the API definition as long as the server ignores this specific parameter.

MUST Always Return JSON Objects As Top-Level Data Structures To Support Extensibility

In a response body, you must always return a JSON object (and not e.g. an array) as a top level data structure to support future extensibility. JSON objects support compatible extension by additional attributes. This allows you to easily extend your response and e.g. add pagination later, without breaking backwards compatibility.

Maps, even though technically objects, are also forbidden as top level data structures, since they don’t support compatible, future extensions.

SHOULD Avoid Versioning

When changing your RESTful APIs, do so in a compatible way and avoid generating additional API versions. Multiple versions can significantly complicate understanding, testing, maintaining, evolving, operating and releasing our systems (Supplementary reading).

If changing an API can’t be done in a compatible way, then proceed in one of these three ways:

  • create a new resource (variant) in addition to the old resource variant
  • create a new service endpoint — i.e. a new application with a new API (with a new domain name)
  • create a new API version supported in parallel with the old API by the same microservice

As we discourage versioning by all means because of the manifold disadvantages, we strongly recommend to only use the first two approaches.

MUST Use URI Versioning

With URI versioning a (major) version number is included in the path, e.g. ‘/v1/customers’.

Deprecation

Sometimes it is necessary to phase out an API endpoint (or version), for instance, if a field is no longer supported in the result or a whole business functionality behind an endpoint has to be shut down. There are many other reasons as well. As long as these endpoints are still used by consumers these are breaking changes and not allowed. Deprecation rules have to be applied to make sure that necessary consumer changes are aligned and deprecated endpoints are not used before API changes are deployed.

MUST Obtain Approval of Clients

Before shutting down an API (or version of an API) the producer must make sure, that all clients have given their consent to shut down the endpoint. Producers should help consumers to migrate to a potential new endpoint (i.e. by providing a migration manual). After all clients are migrated, the producer may shut down the deprecated API.

MUST External Partners Must Agree on Deprecation Timespan

If the API is consumed by any external partner, the producer must define a reasonable timespan that the API will be maintained after the producer has announced deprecation. The external partner (client) must agree to this minimum after-deprecation-lifespan before he starts using the API.

MUST Monitor Usage of Deprecated APIs

Owners of APIs used in production must monitor usage of deprecated APIs until the API can be shut down in order to align deprecation and avoid uncontrolled breaking effects.

MUST Not Start Using Deprecated APIs

Clients must not start using deprecated parts of an API.

JSON Guidelines

These guidelines provides recommendations for defining JSON data at Port of Antwerp-Bruges. JSON here refers to RFC 7159 (which updates RFC 4627), the “application/json” media type and custom JSON media types defined for APIs. The guidelines clarifies some specific cases to allow Port of Antwerp-Bruges JSON data to have an idiomatic form across teams and services.

The first some of the following guidelines are about property names, the later ones about values.

MUST Property names must be ASCII snake_case (and never camelCase): ^[a-z_][a-z_0-9]*$

Property names are restricted to ASCII strings. The first character must be a letter, or an underscore, and subsequent characters can be a letter, an underscore, or a number.

(It is recommended to use ‘_’ at the start of property names only for keywords like ‘_links’.)

No established industry standard exists, but many popular Internet companies prefer snake_case: e.g. GitHub, Stack Exchange, Twitter. Others, like Google and Amazon, use both - but not only camelCase. It’s essential to establish a consistent look and feel such that JSON looks as if it came from the same hand.

SHOULD Array names should be pluralized

To indicate they contain multiple values prefer to pluralize array names. This implies that object names should in turn be singular.

MUST Boolean property values must not be null

Schema based JSON properties that are by design booleans must not be presented as nulls. A boolean is essentially a closed enumeration of two values, true and false. If the content has a meaningful null value, strongly prefer to replace the boolean with enumeration of named values or statuses - for example accepted_terms_and_conditions with true or false can be replaced with terms_and_conditions with values yes, no and unknown.

SHOULD Null values should have their fields removed

OpenAPI, which is in common use, doesn’t support null field values (it does allow omitting that field completely if it is not marked as required). However that doesn’t prevent clients and servers sending and receiving those fields with null values. Also, in some cases null may be a meaningful value - for example, JSON Merge Patch RFC-7396 using null to indicate property deletion.

SHOULD Empty array values should not be null

Empty array values can unambiguously be represented as the empty list, ‘[ ]’.

SHOULD Enumerations should be represented as Strings

Strings are a reasonable target for values that are by design enumerations.

SHOULD Date property values should conform to RFC 3339

Use the date and time formats defined by RFC-3339 section 5.6

  • for “date” use strings matching ‘date-fullyear “-“ date-month “-“ date-mday’, for example: ‘2015-05-28’
  • for “date-time” use strings matching ‘full-date “T” full-time’, for example ‘2015-05-28T14:07:17Z’

Note that the OpenAPI format “date-time” corresponds to “date-time” in the RFC) and ‘2015-05-28’ for a date (note that the OpenAPI format “date” corresponds to “full-date” in the RFC). Both are specific profiles, a subset of the international standard ISO-8601.

A zone offset may be used (both, in request and responses) – this is simply defined by the standards. However, we encourage restricting dates to UTC and without offsets. For example ‘2015-05-28T14:07:17Z’ rather than ‘2015-05-28T14:07:17+00:00’. From experience we have learned that zone offsets are not easy to understand and often not correctly handled. Note also that zone offsets are different from local times that might be including daylight saving time. Localization of dates should be done by the services that provide user interfaces, if required. When it comes to storage, all dates should be consistently stored in UTC without a zone offset. Localization should be done locally by the services that provide user interfaces, if required. Sometimes it can seem data is naturally represented using numerical timestamps, but this can introduce interpretation issues with precision - for example whether to represent a timestamp as 1460062925, 1460062925000 or 1460062925.000. Date strings, though more verbose and requiring more effort to parse, avoid this ambiguity.

MAY Time durations and intervals could conform to ISO 8601

Schema based JSON properties that are by design durations and intervals could be strings formatted as recommended by ISO-8601. (RFC 3339 Appendix A contains a grammar for durations).

MAY Standards could be used for Language, Country and Currency

  • ISO 3166-1-alpha2 country
  • ISO 639-1 language code
  • BCP-47 (based on ISO-639-1 for language variants
  • ISO 4217 currency codes

MAY Use lowercase separate words with hyphens for Path Segments Example:

/shipment-orders/{shipment-order-id}

This applies to concrete path segments and not the names of path parameters. For example ‘{shipment_order_id}’ would be ok as a path parameter.

MUST Use snake_case (never camelCase) for Query Parameters Examples:

customer_number, order_id, billing_address

SHOULD Prefer Hyphenated-Pascal-Case for HTTP header Fields This is for consistency in your documentation (most other headers follow this convention). Avoid camelCase (without hyphens). Exceptions are common abbreviations like “ID.”

Examples:

Accept-Encoding
Apply-To-Redirect-Ref
Disposition-Notification-Options
Original-Message-ID

See also: HTTP Headers are case-insensitive (RFC 7230).

See Common Headers section for more guidance on HTTP headers.

MAY Pluralize Resource Names

Usually, a collection of resource instances is provided (at least API should be ready here). The special case of a resource singleton is a collection with cardinality 1.

SHOULD Not Use /api as Base Path

In most cases, all resources provided by a service are part of the public API, and therefore should be made available under the root “/” base path.

If the service should also support non-public, internal APIs — for specific operational support functions, for example — we encourage you to maintain two different API specifications. For both APIs, you should not use /api as base path.

MUST Avoid Trailing Slashes

The trailing slash must not have specific semantics. Resource paths must deliver the same results whether they have the trailing slash or not.

Resources

MUST Avoid Actions — Think About Resources REST is all about your resources, so consider the domain entities that take part in web service interaction, and aim to model your API around these using the standard HTTP methods as operation indicators. For instance, if an application has to lock articles explicitly so that only one user may edit them, create an article lock with PUT or POST instead of using a lock action.

Request:

PUT /article-locks/{article-id} The added benefit is that you already have a service for browsing and filtering article locks.

MUST Keep URLs Verb-Free

The API describes resources, so the only place where actions should appear is in the HTTP methods. In URLs, use only nouns. Instead of thinking of actions (verbs), it’s often helpful to think about putting a message in a letter box: e.g., instead of having the verb cancel in the url, think of sending a message to cancel an order to the cancellations letter box on the server side.

MUST Use Domain-Specific Resource Names

API resources represent elements of the application’s domain model. Using domain-specific nomenclature for resource names helps developers to understand the functionality and basic semantics of your resources. It also reduces the need for further documentation outside the API definition. For example, “sales-order-items” is superior to “order-items” in that it clearly indicates which business object it represents. Along these lines, “items” is too general.

MUST Use URL-friendly Resource Identifiers: [a-zA-Z0-9:._-]*

To simplify encoding of resource IDs in URLs, their representation must only consist of ASCII strings of letters, numbers, underscore, minus, colon, and period.

MUST Identify resources and Sub-Resources via Path Segments

Some API resources may contain or reference sub-resources. Embedded sub-resources, which are not top-level resources, are parts of a higher-level resource and cannot be used outside of its scope. Sub-resources should be referenced by their name and identifier in the path segments.

Composite identifiers must not contain ‘/’ as a separator. In order to improve the consumer experience, you should aim for intuitively understandable URLs, where each sub-path is a valid reference to a resource or a set of resources. For example, if ‘/customers/12ev123bv12v/addresses/BE_100100101’ is a valid path of your API, then ‘/customers/12ev123bv12v/addresses’, ‘/customers/12ev123bv12v’ and ‘/customers’ must be valid as well in principle.

Basic URL structure:

/{resources}/[resource-id]/{sub-resources}/[sub-resource-id]
/{resources}/[partial-id-1][separator][partial-id-2]

Examples:

/carts/1681e6b88ec1/items
/carts/1681e6b88ec1/items/1
/customers/12ev123bv12v/addresses/BE_100100101
/content/images/9cacb4d8

SHOULD Only Use UUIDs If Necessary

Generating IDs can be a scaling problem in high frequency and near real time use cases. UUIDs solve this problem, as they can be generated without collisions in a distributed, non-coordinated way and without additional server round trips.

However, they also come with some disadvantages:

  • pure technical key without meaning; not ready for naming or name scope conventions that might be helpful for pragmatic reasons, e.g. we learned to use names for product attributes, instead of UUIDs
  • less usable, because…
  • cannot be memorized and easily communicated by humans
  • harder to use in debugging and logging analysis
  • less convenient for consumer facing usage
  • quite long: readable representation requires 36 characters and comes with higher memory and bandwidth consumption
  • not ordered along their creation history and no indication of used id volume
  • may be in conflict with additional backward compatibility support of legacy ids

UUIDs should be avoided when not needed for large scale id generation. Instead, for instance, server side support with id generation can be preferred (POST on id resource, followed by idempotent PUT on entity resource). Usage of UUIDs is especially discouraged as primary keys of master and configuration data, like brand-ids or attribute-ids which have low id volume but widespread steering functionality.

Please be aware that sequential, strictly monotonically increasing numeric identifiers may reveal critical, confidential business information, like order volume, to non-privileged clients.

In any case, we should always use string rather than number type for identifiers. This gives us more flexibility to evolve the identifier naming scheme. Accordingly, if used as identifiers, UUIDs should not be qualified using a format property.

Usually, random UUID is used - see UUID version 4 in RFC 4122. Though UUID version 1 also contains leading timestamps it is not reflected by its lexicographic sorting. This deficit is addressed by ULID (Universally Unique Lexicographically Sortable Identifier). You may favour ULID instead of UUID, for instance, for pagination use cases ordered along creation time.

MAY Consider Using (Non-) Nested URLs

If a sub-resource is only accessible via its parent resource and may not exists without parent resource, consider using a nested URL structure, for instance:

` /carts/1681e6b88ec1/cart-items/1 `

However, if the resource can be accessed directly via its unique id, then the API should expose it as a top-level resource. For example, customer has a collection for sales orders; however, sales orders have globally unique id and some services may choose to access the orders directly, for instance:

/customers/1681e6b88ec1
/sales-orders/5273gh3k525a

SHOULD Limit number of Resource types

To keep maintenance and service evolution manageable, we should follow “functional segmentation” and “separation of concern” design principles and do not mix different business functionalities in same API definition. In practice this means that the number of resource types exposed via an API should be limited. In this context a resource type is defined as a set of highly related resources such as a collection, its members and any direct sub-resources.

For example, the resources below would be counted as three resource types, one for customers, one for the addresses, and one for the customers’ related addresses:

/customers
/customers/{id}
/customers/{id}/preferences
/customers/{id}/addresses
/customers/{id}/addresses/{addr}
/addresses
/addresses/{addr}

Note that:

We consider ‘customers/{id}/preferences’ part of the ‘/customers’ resource type because it has a one-to-one relation to the customer without an additional identifier. We consider ‘/customers’ and ‘/customers/{id}/addresses’ as separate resource types because ‘/customers/{id}/addresses/{addr}’ also exists with an additional identifier for the address. We consider ‘/addresses’ and ‘/customers/{id}/addresses’ as separate resource types because there’s no reliable way to be sure they are the same. Given this definition, our experience is that well defined APIs involve no more than 4 to 8 resource types. There may be exceptions with more complex business domains that require more resources, but you should first check if you can split them into separate subdomains with distinct APIs.

Nevertheless one API should hold all necessary resources to model complete business processes helping clients to understand these flows.

SHOULD Limit number of Sub-Resource Levels

There are main resources (with root url paths) and sub-resources (or nested resources with non-root urls paths). Use sub-resources if their life cycle is (loosely) coupled to the main resource, i.e. the main resource works as collection resource of the subresource entities. You should use <= 3 sub-resource (nesting) levels – more levels increase API complexity and url path length. (Remember, some popular web browsers do not support URLs of more than 2000 characters.)

HTTP Requests

MUST Use HTTP Methods Correctly

Be compliant with the standardized HTTP method semantics summarized as follows:

GET

GET requests are used to read either a single or a collection resource.

  • GET requests for individual resources will usually generate a 404 if the resource does not exist
  • GET requests for collection resources may return either 200 (if the collection is empty) or 404 (if the collection is missing)
  • GET requests must NOT have a request body payload (see GET-with-body) GET requests on collection resources should provide sufficient filter pagination mechanisms.

GET with Body

APIs sometimes face the problem, that they have to provide extensive structured request information with GET, that may conflict with the size limits of clients, load-balancers, and servers. As we require APIs to be standard conform (body in GET must be ignored on server side), API designers have to check the following two options:

  • GET with URL encoded query parameters: when it is possible to encode the request information in query parameters, respecting the usual size limits of clients, gateways, and servers, this should be the first choice. The request information can either be provided distributed to multiple query parameters or a single structured URL encoded string.
  • POST with body content: when a GET with URL encoded query parameters is not possible, a POST with body content must be used. In this case the endpoint must be documented with the hint GET-with-body to transport the GET semantic of this call.

It is no option to encode the lengthy structured request information in header parameters. From a conceptual point of view, the semantic of an operation should always be expressed by resource name and query parameters, i.e. what goes into the URL. Request headers are reserved for general context information, e.g. FlowIDs. In addition, size limits on query parameters and headers are not reliable and depend on clients, gateways, server, and actual settings. Thus, switching to headers does not solve the original problem.

PUT

PUT requests are used to update (in rare cases to create) entire resources – single or collection resources. The semantic is best described as “please put the enclosed representation at the resource mentioned by the URL, replacing any existing resource.”.

  • PUT requests are usually applied to single resources, and not to collection resources, as this would imply replacing the entire collection
  • PUT requests are usually robust against non-existence of resources by implicitly creating before updating
  • on successful PUT requests, the server will replace the entire resource addressed by the URL with the representation passed in the payload (subsequent reads will deliver the same payload)
  • successful PUT requests will usually generate 200 or 204 (if the resource was updated – with or without actual content returned), and 201 (if the resource was created)

It is best practice to prefer POST over PUT for creation of (at least top-level) resources. This leaves the resource ID under control of the service and allows to concentrate on the update semantic using PUT as follows.

In the rare cases where PUT is although used for resource creation, the resource IDs are maintained by the client and passed as a URL path segment. Putting the same resource twice is required to be idempotent and to result in the same single resource instance (see Fulfill Common Method Properties).

POST

POST requests are idiomatically used to create single resources on a collection resource endpoint, but other semantics on single resources endpoint are equally possible. The semantic for collection endpoints is best described as “please add the enclosed representation to the collection resource identified by the URL”.

  • on a successful POST request, the server will create one or multiple new resources and provide their URI/URLs in the response
  • successful POST requests will usually generate 200 (if resources have been updated), 201 (if resources have been created), 202 (if the request was accepted but has not been finished yet), and exceptionally 204 with Location header (if the actual resource is not returned). The semantic for single resource endpoints is best described as “please execute the given well specified request on the resource identified by the URL”.

POST should be used for scenarios that cannot be covered by the other methods sufficiently. In such cases, make sure to document the fact that POST is used as a workaround (see GET-with-body).

Resource IDs with respect to POST requests are created and maintained by server and returned with response payload.

Posting the same resource twice is *not* required to be idempotent (check Fulfill Common Method Properties) and may result in multiple resources.

DELETE

DELETE requests are used to delete resources. The semantic is best described as “please delete the resource identified by the URL”.

  • DELETE requests are usually applied to single resources, not on collection resources, as this would imply deleting the entire collection
  • successful DELETE requests will usually generate 200 (if the deleted resource is returned) or 204 (if no content is returned)
  • failed DELETE requests will usually generate 404 (if the resource cannot be found) or 410 (if the resource was already deleted before)

After deleting a resource with DELETE, a GET request on the resource is expected to either return 404 (not found) or 410 (gone) depending on how the resource is represented after deletion. Under no circumstances the resource must be accessible after this operation on its endpoint.

MUST Fulfill Common Method Properties

Request methods in RESTful services can be…

  • safe - the operation semantic is defined to be read-only, meaning it must not have intended side effects, i.e. changes, to the server state.
  • idempotent - the operation has the same intended effect on the server state, independently whether it is executed once or multiple times. Note: this does not require that the operation is returning the same response or status code.
  • cacheable - to indicate that responses are allowed to be stored for future reuse. In general, requests to safe methods are cachable, if it does not require a current or authoritative response from the server. Note: The above definitions, of intended (side) effect allows the server to provide additional state changing behavior as logging, accounting, pre-fetching, etc. However, these actual effects and state changes, must not be intended by the operation so that it can be held accountable.

Method implementations must fulfill the following basic properties according to RFC-7231

HTTP Status Codes And Errors

MUST Specify Success and Error Responses

APIs should define the functional, business view and abstract from implementation aspects. Success and error responses are a vital part to define how an API is used correctly.

Therefore, you must define all success and service specific error responses in your API specification. Both are part of the interface definition and provide important information for service clients to handle standard as well as exceptional situations.

In most cases it is not useful to document all technical errors, especially if they are not under control of the service provider. Thus unless a response code conveys application-specific functional semantics or is used in a none standard way that requires additional explanation, multiple error response specifications can be combined.

API designers should also think about a troubleshooting board as part of the associated online API documentation. It provides information and handling guidance on application-specific errors and is referenced via links from the API specification. This can reduce service support tasks and contribute to service client and provider performance.

MUST Use Standard HTTP Status Codes You must only use standardized HTTP status codes consistently with their intended semantics. You must not invent new HTTP status codes. RFC standards define ~60 different HTTP status codes with specific semantics (mainly RFC7231 and RFC6585 — and there are upcoming new ones, e.g. draft legally-restricted-status. See overview on all error codes on Wikipedia or via https://httpstatuses.com) also inculding ‘unofficial codes’, e.g. used by popular web servers like Nginx.

Below we list the most commonly used and best understood HTTP status codes, consistent with their semantic in the RFCs. APIs should only use these to prevent misconceptions that arise from less commonly used HTTP status codes.

As long as your HTTP status code usage is well covered by the semantic defined here, you should not describe it to avoid an overload with common sense information and the risk of inconsistent definitions. Only if the HTTP status code is not in the list below or its usage requires additional information aside the well defined semantic, the API specification must provide a clear description of the HTTP status code in the response.

Success Codes

CodeMeaningMethod
200OK - this is the standard success response< all >
201Created - Returned on successful entity creation. You are free to return either an empty response or the created resource in conjunction with the Location header. (More details found in the common-headers.) Always set the Location headerPOST, PUT
202Accepted - The request was successful and will be processed asynchronously.POST, PUT, DELETE
204No content - There is no response body.PUT, DELETE

Redirection Codes

CodeMeaningMethod
301Moved Permanently - This and all future requests should be directed to the given URI.< all >
303See Other - The response to the request can be found under another URI using a GET method.POST, PUT, DELETE
304Not Modified - resource has not been modified since the date or version passed via request headers If-Modified-Since or If-None-Match.GET

Client Side Error Codes

CodeMeaningMethod
400Bad request - generic / unknown error. Should also be delivered in case of input payload fails business logic validation.< all >
401Unauthorized - the users must log in (this often means “Unauthenticated”).< all >
403Forbidden - the user is not authorized to use this resource.< all >
404Not found - the resource is not found.< all >
406Not Acceptable - resource can only generate content not acceptable according to the Accept headers sent in the request.< all >
408Request timeout - the server times out waiting for the resource.< all >
410Gone - resource does not exist any longer, e.g. when accessing a resource that has intentionally been deleted.< all >
422Unprocessable Entity - The server understands the content type of the request entity (hence a 415 Unsupported Media Type status code is inappropriate), and the syntax of the request entity is correct (thus a 400 Bad Request status code is inappropriate) but was unable to process the contained instructions.POST, PUT
429Too many requests - the client does not consider rate limiting and sent too many requests< all >

Server Side Error Codes

CodeMeaningMethod
500Internal Server Error - a generic error indication for an unexpected server execution problem (here, client retry may be sensible)
501Not Implemented - server cannot fulfill the request (usually implies future availability, e.g. new feature).< all >
503Service Unavailable - service is (temporarily) not available (e.g. if a required component or downstream service is not available) — client retry may be sensible.< all >

MUST Use Most Specific HTTP Status Codes

You must use the most specific HTTP status code when returning information about your request processing status or error situations.

MUST Use Code 429 with Headers for Rate Limits

APIs that wish to manage the request rate of clients must use the 429 (Too Many Requests) response code, if the client exceeded the request rate (see RFC-6585). Such responses must also contain header information providing further details to the client. There are two approaches a service can take for header information:

  • Return a {Retry-After} header indicating how long the client ought to wait before making a follow-up request. The Retry-After header can contain a HTTP date value to retry after or the number of seconds to delay. Either is acceptable but APIs should prefer to use a delay in seconds.
  • Return a trio of ‘X-RateLimit’ headers. These headers (described below) allow a server to express a service level in the form of a number of allowing requests within a given window of time and when the window is reset.

The ‘X-RateLimit’ headers are:

  • ‘X-RateLimit-Limit’: The maximum number of requests that the client is allowed to make in this window.
  • ‘X-RateLimit-Remaining’: The number of requests allowed in the current window.
  • ‘X-RateLimit-Reset’: The relative time in seconds when the rate limit window will be reset. Beware that this is different to Github and Twitter’s usage of a header with the same name which is using UTC epoch seconds instead.

The reason to allow both approaches is that APIs can have different needs. Retry-After is often sufficient for general load handling and request throttling scenarios and notably, does not strictly require the concept of a calling entity such as a tenant or named account. In turn this allows resource owners to minimise the amount of state they have to carry with respect to client requests. The ‘X-RateLimit’ headers are suitable for scenarios where clients are associated with pre-existing account or tenancy structures. ‘X-RateLimit’ headers are generally returned on every request and not just on 429, which implies the service implementing the API is carrying sufficient state to track the number of requests made within a given window for each named entity.

MUST Use Problem JSON

RFC-7807 defines a Problem JSON object and the media type ‘application/problem+json’. Operations should return it (together with a suitable status code) when any problem occurred during processing and you can give more details than the status code itself can supply, whether it be caused by the client or the server (i.e. both for 4xx or 5xx error codes).

The Open API schema definition of the Problem JSON object can be found on github. You can reference it by using:

responses: 503: description: Service Unavailable content: “application/problem+json”: schema: $ref: ‘https://zalando.github.io/problem/schema.yaml#/Problem’ You may define custom problem types as extension of the Problem JSON object if your API need to return specific additional error detail information.

for backward compatibility: A previous version of this guideline (before the publication of RFC-7807 and the registration of the media type) told to return custom variant of the media type ‘application/x.problem+json’. Servers for APIs defined before this change should pay attention to the Accept header sent by the client and set the ‘Content-Type’ header of the problem response correspondingly. Clients of such APIs should accept both media types.

MUST Do not expose Stack Traces Stack traces contain implementation details that are not part of an API, and on which clients should never rely. Moreover, stack traces can leak sensitive information that partners and third parties are not allowed to receive and may disclose insights about vulnerabilities to attackers.

Performance

MUST Use gzip Compression

Compress the payload of your API’s responses with gzip, unless there’s a good reason not to — for example, you are serving so many requests that the time to compress becomes a bottleneck. This helps to transport data faster over the network (fewer bytes) and makes frontends respond faster.

Though gzip compression might be the default choice for server payload, the server should also support payload without compression and its client control via {Accept-Encoding} request header – see also RFC 7231 Section 5.3.4. The server should indicate used gzip compression via the Content-Encoding header.

MAY Support Partial Responses via Filtering

Depending on your use case and payload size, you can significantly reduce network bandwidth need by supporting filtering of returned entity fields. Here, the client can explicitly determine the subset of fields he wants to receive via the ‘fields’ query parameter. (It is analogue to ‘fields’ and simple queries, and also applied, for instance, for Google Cloud API’s partial responses.)

Unfiltered

GET http://api.example.org/users/123 HTTP/1.1
 
HTTP/1.1 200 OK
Content-Type: application/json
 
{
  "id": "cddd5e44-dae0-11e5-8c01-63ed66ab2da5",
  "name": "John Doe",
  "address": "1600 Pennsylvania Avenue Northwest, Washington, DC, United States",
  "birthday": "1984-09-13",
  "friends": [ {
    "id": "1fb43648-dae1-11e5-aa01-1fbc3abb1cd0",
    "name": "Jane Doe",
    "address": "1600 Pennsylvania Avenue Northwest, Washington, DC, United States",
    "birthday": "1988-04-07"
  } ]
}

Filtered

GET http://api.example.org/users/123?fields=(name,friends(name)) HTTP/1.1
 
HTTP/1.1 200 OK
Content-Type: application/json
 
{
  "name": "John Doe",
  "friends": [ {
    "name": "Jane Doe"
  } ]
}

The query field value determines the fields returned with the response payload object. For instance, ‘(name)’ returns ‘users’ root object with only the ‘name’ field, and ‘(name,friends(name))’ returns the ‘name’ and the nested ‘friends’ object with only its ‘name’ field.

OpenAPI doesn’t support you in formally specifying different return object schemes depending on a parameter. When you define the field parameter, we recommend to provide the following description: `Endpoint supports filtering of return object fields as described in Support Partial Responses via Filtering

The syntax of the query field value is defined by the following BNF grammar.

 <fields>            ::= [ <negation> ] <fields_struct>
 <fields_struct>     ::= "(" <field_items> ")"
 <field_items>       ::= <field> [ "," <field_items> ]
 <field>             ::= <field_name> | <fields_substruct>
 <fields_substruct>  ::= <field_name> <fields_struct>
 <field_name>        ::= <dash_letter_digit> [ <field_name> ]
 <dash_letter_digit> ::= <dash> | <letter> | <digit>
 <dash>              ::= "-" | "_"
 <letter>            ::= "A" | ... | "Z" | "a" | ... | "z"
 <digit>             ::= "0" | ... | "9"
 <negation>          ::= "!"

MAY Allow Optional Embedding of Sub-Resources

Embedding related resources (also know as Resource expansion) is a great way to reduce the number of requests. In cases where clients know upfront that they need some related resources they can instruct the server to prefetch that data eagerly. Whether this is optimized on the server, e.g. a database join, or done in a generic way, e.g. an HTTP proxy that transparently embeds resources, is up to the implementation.

Please use the BNF grammar, as already defined above for filtering, when it comes to an embedding query syntax.

Embedding a sub-resource can possibly look like this where an order resource has its order items as sub-resource (/order/{orderId}/items):

GET /order/123?embed=(items) HTTP/1.1
 
{
  "id": "123",
  "_embedded": {
    "items": [
      {
        "position": 1,
        "sku": "1234-ABCD-7890",
        "price": {
          "amount": 71.99,
          "currency": "EUR"
        }
      }
    ]
  }
}

Pagination

MUST Support Pagination

Access to lists of data items must support pagination to protect the service against overload as well as for best client side iteration and batch processing experience. This holds true for all lists that are (potentially) larger than just a few hundred entries.

We use Offset/Limit-based pagination, a numeric offset identifies the first page entry.

‘page’ : numeric offset of the first element on a page. 0-based. ‘size’ : client suggested limit to restrict the number of entries on a page. Default 50 MAY Use Pagination Links Where Applicable

  • API implementing HATEOS may use simplified hypertext controls for pagination within collections.

Those collections should then have an items attribute holding the items of the current page. The collection may contain additional metadata about the collection or the current page (e.g. ‘index’) when necessary.

You should avoid providing a total count in your API unless there’s a clear need to do so. Very often, there are systems and performance implications to supporting full counts, especially as datasets grow and requests become complex queries or filters that drive full scans (e.g., your database might need to look at all candidate items to count them). While this is an implementation detail relative to the API, it’s important to consider your ability to support serving counts over the life of a service.

If the collection consists of links to other resources, the collection name should use IANA registered link relations as names whenever appropriate, but use plural form.

E.g. a service for articles could represent the collection of hyperlinks to an article’s ‘authors’ like that:

{
  "self": "https://.../articles/xyz/authors/",
  "index": 0,
  "page_size": 5,
  "items": [
    {
      "href": "https://...",
      "id": "123e4567-e89b-12d3-a456-426655440000",
      "name": "Kent Beck"
    },
    {
      "href": "https://...",
      "id": "987e2343-e89b-12d3-a456-426655440000",
      "name": "Mike Beedle"
    },
    ...
  ],
  "first": "https://...",
  "next": "https://...",
  "prev": "https://...",
  "last": "https://..."
}

Hypermedia

MUST Use REST Maturity Level 2

We strive for a good implementation of REST Maturity Level 2 as it enables us to build resource-oriented APIs that make full use of HTTP verbs and status codes. You can see this expressed by many rules throughout these guidelines, e.g.:

Avoid Actions — Think About Resources Keep URLs Verb-Free Use HTTP Methods Correctly Use Standard HTTP Status Codes Although this is not HATEOAS, it should not prevent you from designing proper link relationships in your APIs as stated in rules below.

MAY Use REST Maturity Level 3 - HATEOAS

We do not generally recommend to implement REST Maturity Level 3. HATEOAS comes with additional API complexity without real value in our SOA context where client and server interact via REST APIs and provide complex business functions as part of our e-commerce SaaS platform.

Our major concerns regarding the promised advantages of HATEOAS (see also RESTistential Crisis over Hypermedia APIs, Why I Hate HATEOAS and others for a detailed discussion):

  • Generic HATEOAS clients which need no prior knowledge about APIs and explore API capabilities based on hypermedia information provided, is a theoretical concept that we haven’t seen working in practice and does not fit to our SOA set-up. The OpenAPI description format (and tooling based on OpenAPI) doesn’t provide sufficient support for HATEOAS either.
  • In practice relevant HATEOAS approximations (e.g. following specifications like HAL or JSON API) support API navigation by abstracting from URL endpoint and HTTP method aspects via link types. So, Hypermedia does not prevent clients from required manual changes when domain model changes over time.
  • Hypermedia make sense for humans, less for SOA machine clients. We would expect use cases where it may provide value more likely in the frontend and human facing service domain.
  • Hypermedia does not prevent API clients to implement shortcuts and directly target resources without ‘discovering’ them.
  • However, we do not forbid HATEOAS; you could use it, if you checked its limitations and still see clear value for your usage scenario that justifies its additional complexity.

SHOULD Use Simple Hypertext Controls for Pagination and Self-References Hypertext controls for pagination inside collections and self-references should use a simple URI value in combination with their corresponding link relations (‘next’, ‘prev’, ‘first’, ‘last’, ‘self’) instead of the extensible common hypertext control

See pagination for information how to best represent paginateable collections.

MUST Not Use Link Headers with JSON entities

We don’t allow the use of the ‘Link’ Header defined by RFC 5988 in conjunction with JSON media types. We prefer links directly embedded in JSON payloads to the uncommon link header syntax.

Data Formats

MUST Use JSON to Encode Structured Data

Use JSON-encoded body payload for transferring structured data. The JSON payload must follow RFC 7159 by having (if possible) a serialized object as the top-level structure, since it would allow for future extension. This also applies for collection resources where one naturally would assume an array. See Use Pagination Links Where Applicable for an example.

MAY Use non JSON Media Types for Binary Data or Alternative Content Representations

Other media types may be used in following cases:

Transferring binary data or data whose structure is not relevant. This is the case if payload structure is not interpreted and consumed by clients as is. Example of such use case is downloading images in formats JPG, PNG, GIF. In addition to JSON version alternative data representations (e.g. in formats PDF, DOC, XML) may be made available through content negotiation.

SHOULD Prefer standard Media type name ‘application/json’

MUST Use Standard Date and Time Formats

JSON Payload

Read more about date and time format in Date property values should conform to RFC 3339.

HTTP headers

Http headers including the proprietary headers use the HTTP date format defined in RFC 7231.

MAY Use Standards for Country, Language and Currency Codes

Use the following standard formats for country, language and currency codes:

  • ISO 3166-1-alpha2 country codes
  • ISO 639-1 language code
  • BCP-47 (based on ISO-639-1 for language variants
  • ISO 4217 currency codes

Common Headers

MUST Use ‘Content-*’ Headers Correctly

Content or entity headers are headers with a ‘Content-‘ prefix. They describe the content of the body of the message and they can be used in both, HTTP requests and responses. Commonly used content headers include but are not limited to:

  • {Content-Disposition} can indicate that the representation is supposed to be saved as a file, and the proposed file name.
  • {Content-Encoding} indicates compression or encryption algorithms applied to the content.
  • {Content-Length} indicates the length of the content (in bytes).
  • {Content-Language} indicates that the body is meant for people literate in some human language(s).
  • {Content-Range} is used in responses to range requests to indicate which part of the requested resource representation is delivered with the body.
  • {Content-Type} indicates the media type of the body content.

MAY Use Standardized Headers

Use this list and mention its support in your OpenAPI definition.