URLs and URIs

Base URL

Examples of good base URLs:

https://api.platform.com/v1
https://api.platform.com/subsystem/v1.1

Not so good example (see rules below why):

http://open.prod.platform.com/api/subsystem/services/1.2.4

Base URL should be short

The base URL SHOULD be short and easy to remember. The longer the domain name and path, the harder it is to use.

Https MUST be used

Https MUST be used. This solves a great deal of otherwise hard to tackle security problems.

Base URL MUST end in a version number

The version in the URL is the escape route to introduce breaking changes to the API. This should not be taken lightly and therefore a single digit should be enough for a long time.

For clarity, the version MUST be preceded by a v.

Base URL’s version number MAY include a minor version

See Versioning for more information on when to use a minor version.

URL and URI naming

Collection names SHOULD be a plural noun

A URI identifying a collection of resources SHOULD be named with a plural noun, or noun phrase, as its path segment. For example, the URI for a collection of categories may use the plural noun form as follows:

https://api.platform.com/v1/categories

Document names SHOULD be a singular noun

A URI representing a document resource SHOULD be named with a singular noun or noun phrase path segment. For example, the URI for a single seller profile document would have the singular form:

https://api.platform.com/v1/users/1/profile

Forward slash separator (/) MUST be used to indicate a hierarchical relationship

The forward slash (/) character MUST be used in the path portion of the URI to indicate a hierarchical relationship between resources. For example:

https://api.platform.com/v1/categories/91/advertisements

URIs SHOULD NOT have a trailing forward slash (/)

As the last character within a URI’s path, a forward slash (/) adds no semantic value and may cause confusion. REST APIs should not expect a trailing slash and SHOULD NOT include them in the links that they provide to clients.

Many web components and frameworks will treat the following two URIs equally:

https://api.platform.com/v1/categories/
https://api.platform.com/v1/categories

However, every character within a URI counts toward a resource’s unique identity. Two different URIs must map to two different resources. If the URIs differ, then so do the resources, and vice versa. Therefore, a REST API must generate and communicate clean URIs and SHOULD be intolerant of any client’s attempts to identify a resource imprecisely.

Redirects (e.g. 301 Moved Permanently) from one version to the other MUST NOT be used. Some clients don’t support redirects and we need to keep this consistent over our separate APIs.

Hyphens (-) SHOULD be used to improve the readability of URIs, not underscores (_)

To make your URIs easy for people to scan and interpret, use the hyphen (-) character to improve the readability of names in long path segments. Anywhere you would use a space or hyphen in English, you should use a hyphen in a URI. For example:

https://api.platform.com/v1/this-is-an-endpoint-with-a-large-name

URI paths MUST use lowercase letters

Lowercase letters are preferred in URI paths since capital letters can sometimes cause problems. RFC 3986 defines URIs as case-sensitive except for the scheme and host components. For example:

https://api.example.restapi.org/my-folder/my-doc
HTTPS://API.EXAMPLE.RESTAPI.ORG/my-folder/my-doc
https://api.example.restapi.org/My-Folder/my-doc

The first URL is fine and RFC3986 considers the second url to be identical to URL #1. But the third URL is not the same as URIs one and two, which may give unnecessary confusion.

Parameters inside the URI path are excluded from this rule.

Actions

Though it SHOULD be avoided, in some cases it is simply not possible to mold an action into REST’s resource model. In those cases an action can be made available on a resource. To clearly indicate that a URL indicates an action instead of a sub-resource, action URLs MUST contain the path segment /actions/ in front of the action name.

For example:

https://api.example.restapi.org/v1/message/1234/actions/mark-as-read

Query parameters

The query component of a URI SHOULD be used to filter collections

A URI’s query component is a natural fit for supplying search criteria to a collection.

Example :

GET /v1/users?casUser=true HTTP/1.1
Host: api.platform.com


HTTP/1.1 200 OK
Content-Type: application/json

{
    "_links": {
        "self": { "href": "/v1/users?casUser=true" },
        "curies": [
          {
            "href": "/v1/docs/rels/{rel}.html",
            "templated": true,
            "name": "ec"
          }
        ]
    },
    "_embedded": {
        "ec:user": [{
           "_links": {
             "self": { "href": "/v1/users/2" },
           },
           "id": 2,
           "name": "Richard",
           "email": "2@platform.com",
           "casUser": true
        }]
    },
    "totalResults": 10
}

Query parameters offset and limit SHOULD be used to paginate collections results

A REST API client SHOULD use query parameters offset and limit to paginate collections.

The offset parameter specifies the 0 based index of the first element to return in the response. The limit parameter specifies the maximum number of contained elements to return in the response.

Example:

GET /v1/users?offset=0&limit=2 HTTP/1.1
Host: api.platform.com

HTTP/1.1 200 OK
Content-Type: application/json

{  /* See section `resource representation format` for an example. */ }

Note that offset and limit are only used for collection resources. Pagination of embedded (linked) collections is done with the _expand query parameter.

The _expand parameter MAY be used for zooming

Zooming is an optional technique that allows linked resources to be embedded. The embedded resources are serialized as described by HAL.

The full syntax for the _expand parameter is:

<basic_expand> = <relation name>[:<offset>:<limit>][(<include field>{,<include field>})]

<rel_expand> = <basic_expand>{/<basic_expand>}

_expand = <rel_expand>{,<rel_expand>}

Where [...] indicates an optional clause and {s...} indicates an optional repeating clause with s as separator.

Here are some examples:

_expand=ec:parent-category
_expand=ec:parent-category/ec:parent-category
_expand=ec:parent-category,ec:child-categories:0:10
_expand=ec:parent-category/ec:parent-category,ec:child-categories:0:10/ec:child-categories:0:10
_expand=ec:parent-category(name,shortName)

Relation name

The relation name can be quite long, for example https://api.platform.com/v1/docs/rels/users and can contain many separator characters. Therefor the relation name MUST be shortened with HAL’s curie syntax. Clients can obtain the correct prefix by getting the non-expanded resource first and inspect the curies section inside the _links section. Implementations are RECOMMENDED to verify the prefix but are free to ignore it.

Offset and limit

When the relation refers to a collection, the offset and limit parameters SHOULD be used for pagination. Offset specifies the 0 based index of the first element to return in the response. Limit specifies the maximum number of contained elements to return in the response.

Nesting expansion

An embedded resource can be further expanded by adding a slash (/) and another expand clause.

Multiple expansions

Multiple embedded resources can be expanded by adding a comma (,) and another expand clause. It is not possible to expand multiple embedded resources in an embedded resource.

Implementation considerations

Documentation MUST specify exactly what parts of the _expand parameter are supported. It is not expected that implementations support expansion in a generic way.

Example

Full example for a simple expansion (expand parent category in a category resource):

GET /v1/categories/92?_expand=ec:parent-category HTTP/1.1
Host: api.platform.com

HTTP/1.1 200 OK
Content-Type: application/json

{
    "_links": {
        "self": { "href": "/v1/categories/92" },
        "curies": [
          {
            "href": "/v1/docs/rels/{rel}.html",
            "templated": true,
            "name": "ec"
          }
        ],
        "ec:parent-category": { "href": "/v1/categories/91" }
    },
    "_embedded": {
        "ec:parent-category": {
           "_links": {
             "self": { "href": "/v1/categories/91" },
           },
           "id": 91,
           "name": "Auto's, motors en andere voertuigen",
           "shortName": "Auto's"
        }
    },
    "id": 92,
    "parentCategoryId": 91,
    "name": "Alpha romeo",
    "shortName": "Alpha romeo"
}

An example for a linked collection resource (expand messages in a conversation resource). See chapter 20 for the full representation.

GET /v1/conversations/1234?_expand=ec:messages:0:10
Host: api.platform.com


HTTP/1.1 200 OK
Content-Type: application/json

{
  "_links": { ... },
  "_embedded": {
    "ec:messages": {
      "_embedded": {
        "ec:message": [
          { ... message fields ... },
          { ... message fields ... }
        ],
        "offset": 0,
        "limit": 10,
        "totalCount": 25
      }
    }
  },
  "id": "1234",
  ... more conversation fields ...
}

The _include and _exclude query parameters MAY be used for selecting fields

The _include and _exclude parameters MAY be used to select fields. If both parameters are present, only the _include parameter MUST be used.

Both fields have a similar format. A list of comma separated field names are valid arguments for both _include and _exclude.

Nested fields may be specified with a / character. E.g. seller/name identifies the name field inside the seller field.

Note that _include and _exclude only work on the fields of the requested resource, and not on embedded resources.

The _body query parameter SHOULD be used to include/exclude a response body

By default responses contain a body, even if it is mostly the same as what was posted. The _body parameter SHOULD be used to change this behavior.

Parameter _body takes values true (the default) or false.

Example:

POST https://api.platform.com/v1/users?_body=false HTTP/1.1
Host: api.platform.com

{
    "username": "testUser",
    "zipcode": "1097DN",
    "email": "test@platform.com"
}


HTTP/1.1 201 Created
Location: /v1/users/95

Note: the _body parameter SHOULD be supported by each method on each resource.

Mimicking request methods

Some clients do not support all methods. Therefore we support other ways to mimic any request method:

  1. The value of _method URL parameter MUST be interpreted as the intended request method.
  2. The value of request header X-HTTP-METHOD-OVERRIDE MUST be interpreted as the intended request method.
  3. The value of request header X-HTTP-METHOD MAY be interpreted as the intended request method.
  4. The value of request header X-METHOD-OVERRIDE MAY be interpreted as the intended request method.

These ways MUST be evaluated in the given order; the first found value overrides all following. When during evaluation of a parameter or header value an unknown or illegal method is encountered, an 400 error response MUST be given, independent of the correctness of other available values.

Example:

POST /v1/advertisements/95?_method=PATCH HTTP/1.1
Host: api.platform.com
Content-Type: application/json-patch+json
If-Match: "abc123"

[
    { "op": "replace", "path": "/title", "value": "new advertisement title" },
    { "op": "replace", "path": "/price", "value": 9000 }
]

is interpreted as a PATCH request.

A failure example:

POST /v1/advertisements/95?_method=BLABLA HTTP/1.1
Host: api.platform.com
X-HTTP-METHOD-OVERRIDE: PATCH
Content-Type: application/json-patch+json
If-Match: "abc123"

[
    { "op": "replace", "path": "/title", "value": "new advertisement title" },
    { "op": "replace", "path": "/price", "value": 9000 }
]

results in an 400 error response because the _method parameter contains an illegal method, even though the X-HTTP-METHOD-OVERRIDE header does contain a correct value.

The _callback parameter MAY be used to return a JSONP response

A _callback parameter MAY be used in any GET call to have the results wrapped in a javascript function. This is typically used when browsers want to embed content in web pages by getting around cross domain issues. The response includes the same data output as the regular API, plus the relevant HTTP Header information.

The content type of the response MUST be application/javascript.

Example:

GET /v1/users/2?_callback=foo HTTP/1.1
Host: api.platform.com

HTTP/1.1 200 OK
Content-Type: application/javascript
foo(
    {
        "_links": {
            "self": { "href": "/v1/users/2" }, ...
        },
        "name": "Richard",
        "email": "r2@hotmail.com"
    }
)

The _prettyprint parameter MAY be used to pretty print the response

Implementation MAY support the ?_prettyprint parameter to make it easier for people to view, read and understand resources. Formatting SHOULD be enabled when the parameter is present and does not have the value false.

Consider enabling pretty printing by default for documentation resources only (e.g. the / resource).

Example :

GET /v1/categories/95?_prettyprint HTTP/1.1
Host: api.platform.com

HTTP/1.1 200 OK
Content-Type: application/json

{
    "_links": {
        "self": { "href": "/v1/categories/95" }, ...
    },
    "id": 95,
    "parentCategoryId": 91,
    "name": "BMW",
    "shortName": "BMW"
}

Use of underscore character

The underscore character is used to clearly identify shared URL parameters. The underscore is nicely consistent with how it is used in HAL resource representations.

Parameter names MUST NOT start with an underscore unless they are described in these guidelines.