Every REST API must at least accept basic authentication. Resources that should be available anonymously must be marked as such.


Authorization is NOT handled by the REST APIs themselves. It is the responsibility of the service layer to make sure the authenticated client (user) has the correct permissions to access the given resource. 

Require TLS

Require TLS to access the API, without exception. It’s not worth trying to figure out or explain when it is OK to use TLS and when it’s not. Just require TLS for everything.

Return appropriate status codes

Return appropriate HTTP status codes with each response. Successful responses should be coded according to this guide:

  • 200: Request succeeded for a GET calls, and for DELETE or PATCH calls that complete synchronously
  • 201: Request succeeded for a POST call that completes synchronously
  • 202: Request accepted for a POST, DELETE, or PATCH call that will be processed asynchronously
  • 206: Request succeeded on GET, but only a partial response returned

Refer to the HTTP response code spec for guidance on status codes for user error and server error cases.

Provide full resources where available

Provide the full resource representation (i.e. the object with all attributes) whenever possible in the response. Always provide the full resource on 200 and 201 responses, including PUT/PATCH and DELETE requests, e.g.:

202 responses will not include the full resource representation.

Accept serialized JSON in request bodies

Accept serialized JSON on PUT/PATCH/POST request bodies, either instead of or in addition to form-encoded data.

Provide resource (UU)IDs

Give each resource an id attribute by default. . Don’t use IDs that won’t be globally unique across instances of the service or other resources in the service, especially auto-incrementing IDs.

Render UUIDs in downcased 8-4-4-4-12 format, e.g.:

"id": "01234567-89ab-cdef-0123-456789abcdef"

Provide standard timestamps

Provide created_at and updated_at timestamps for resources by default, e.g:

{ ... "created_at": "2012-01-01T12:00:00Z", "updated_at": "2012-01-01T13:00:00Z", ... }

These timestamps may not make sense for some resources, in which case they can be omitted.

Use UTC times formatted in ISO8601

Accept and return times in UTC only. Render times in ISO8601 format, e.g.:

"finished_at": "2012-01-01T12:00:00Z"

Use consistent path formats

Resource names

Use the plural version of a resource name unless the resource in question is a singleton within the system (for example, in most systems a given user would only ever have one account). This keeps it consistent in the way you refer to particular resources.


Prefer endpoint layouts that don’t need any special actions for individual resources. In cases where special actions are needed, place them under a standard actions prefix, to clearly delineate them:




Downcase paths and attributes

Use downcased and dash-separated path names, for alignment with hostnames, e.g:

Downcase attributes as well, but use underscore separators so that attribute names can be typed without quotes in JavaScript, e.g.:

service_class: "first"

Nest foreign key relations

Serialize foreign key references with a nested object, e.g.:

{ "name": "service-production", "owner": { "id": "5d8201b0..." }, ... }

Instead of e.g:

{ "name": "service-production", "owner_id": "5d8201b0...", ... }

This approach makes it possible to inline more information about the related resource without having to change the structure of the response or introduce more top-level response fields, e.g.:

{ "name": "service-production", "owner": { "id": "5d8201b0...", "name": "russ", "email": "" }, ... }

Support non-id de-referencing for convenience

In some cases it may be inconvenient for end-users to provide IDs to identify a resource. In these cases you may want to accept both an id or name, e.g.:

$ curl{app_id_or_name}
$ curl
$ curl

Do not accept only names to the exclusion of IDs.

Generate structured errors

Generate consistent, structured response bodies on errors. Include a machine-readable error id, a human-readable error message, and optionally a url pointing the client to further information about the error and how to resolve it, e.g.:

HTTP/1.1 429 Too Many Requests

{ "id": "rate_limit", "message": "Account reached its API rate limit.", "url": "" }

Document your error format and the possible error ids that clients may encounter.

Support caching with Etags

Include an ETag header in all responses, identifying the specific version of the returned resource. The user should be able to check for staleness in their subsequent requests by supplying the value in the If-None-Match header.

Trace requests with Request-Ids

Include a Request-Id header in each API response, populated with a UUID value. If both the server and client log these values, it will be helpful for tracing and debugging requests.

Paginate with Ranges

Paginate any responses that are liable to produce large amounts of data. Use Content-Range headers to convey pagination requests.

Show rate limit status

Rate limit requests from clients to protect the health of the service and maintain high service quality for other clients.

Return the remaining number of request tokens with each request in the RateLimit-Remaining response header.


Version the API from the start.

A version number indicates a certain level of backwards-compatibility the client can expect, and as such, extra care should be taken to maintain this trust.

Accepts header

Use the Accepts header to communicate the version, along with a custom content type, e.g.:

Accept: application/vnd.EIS_EVENTS+json; version=2

Prefer not to have a default version, instead requiring clients to explicitly peg their usage to a specific version.

Request URI

The version number of an API appears in its URI.{id}

When an API has multiple versions, we recommend:

  • Two versions should be supported at the same time.
  • If two or more versions are available, applications may provide a shortcut to the latest version using the latest keyword.

For example, if versions 1 and 2 of the API are available, the following two URIs would point to the same resources:{id}{id}

Minimize path nesting

In data models with nested parent/child resource relationships, paths may become deeply nested, e.g.:


Limit nesting depth by preferring to locate resources at the root path. Use nesting to indicate scoped collections. For example, for the case above where a dyno belongs to an app belongs to an org:


Provide machine-readable JSON schema

Provide a machine-readable schema to exactly specify your API.

Provide human-readable docs

Provide human-readable documentation that client developers can use to understand your API.

In addition to endpoint details, provide an API overview with information about:

  • Authentication, including acquiring and using authentication tokens.
  • API stability and versioning, including how to select the desired API version.
  • Common request and response headers.
  • Error serialization format.
  • Examples of using the API with clients in different languages.

Provide executable examples

Provide executable examples that users can type directly into their terminals to see working API calls. To the greatest extent possible, these examples should be usable verbatim, to minimize the amount of work a user needs to do to try the API.

Describe stability

Describe the stability of your API or its various endpoints according to its maturity and stability, e.g. with prototype/development/production flags.

Once your API is declared production-ready and stable, do not make backwards incompatible changes within that API version. If you need to make backwards-incompatible changes, create a new API with an incremented version number.

Pretty-print JSON by default

The first time a user sees your API is likely to be at the command line, using curl. It’s much easier to understand API responses at the command-line if they are pretty-printed. For the convenience of these developers, pretty-print JSON responses, e.g.:

{ "beta": false, "email": "", "id": "01234567-89ab-cdef-0123-456789abcdef", "last_login": "2012-01-01T12:00:00Z", "created_at": "2012-01-01T12:00:00Z", "updated_at": "2012-01-01T12:00:00Z" }

Be sure to include a trailing newline so that the user’s terminal prompt isn’t obstructed.

For most APIs it will be fine performance-wise to pretty-print responses all the time. You may consider for performance-sensitive APIs not pretty-printing certain endpoints.

Not Yet Covered in these Guidelines

Some items worth discussing in the guidelines are currently out of scope:

  • Batching
  • Gzip
  • Internationalization (i18n)