- A URL identifies a resource.
- URLs should include nouns, not verbs.
- Use plural nouns only for consistency (no singular nouns).
- Use HTTP verbs (GET, POST, PUT, DELETE) to operate on the collections and elements.
- You shouldn’t need to go deeper than resource/identifier/resource.
- Put the version number at the base of your URL, for example http://example.com/v1/path/to/resource.
- URL v. header:
- If it changes the logic you write to handle the response, put it in the URL.
- If it doesn’t change the logic for each response, like OAuth info, put it in the header.
- Specify optional fields in a comma separated list.
- Formats should be in the form of api/v2/resource/{id}.json
- List of magazines:
- Filtering is a query:
- Sorting(Comma separated fields to sorts. Prefix field name with "-" for desc sort):
- A single magazine in JSON format:
- All articles in (or belonging to) this magazine:
- Add a new article to a particular magazine:
- Non-plural noun:
- Verb in URL:
- Filter outside of query string
HTTP verbs, or methods, should be used in compliance with their definitions under the HTTP/1.1 standard. The action taken on the representation will be contextual to the media type being worked on and its current state. Here's an example of how HTTP verbs map to create, read, update, delete operations in a particular context:
| HTTP METHOD | POST | GET | PUT | DELETE |
|---|---|---|---|---|
| CRUD OP | CREATE | READ | UPDATE | DELETE |
| /dogs | Create new dogs | List dogs | Bulk update | Delete all dogs |
| /dogs/1234 | Error | Show Bo | If exists, update Bo; If not, error | Delete Bo |
(Example from Web API Design, by Brian Mulloy, Apigee.)
- No values in keys
- No internal-specific names (e.g. "node" and "taxonomy term")
- Metadata should only contain direct properties of the response set, not properties of the members of the response set
No values in keys:
"tags": [
{"id": "125", "name": "Environment"},
{"id": "834", "name": "Water Quality"}
],
Values in keys:
"tags": [
{"125": "Environment"},
{"834": "Water Quality"}
],
Error responses should include a common HTTP status code, message for the end-user (when appropriate), internal error code (corresponding to some specific internally determined ID). For example:
{
"status" : 400,
"message" : "This is a message that can be passed along to end-users, if needed.",
"code" : "444444"
}
Use three simple, common response codes indicating (1) success, (2) failure due to client-side problem, (3) failure due to server-side problem:
- 200 All other successful
- 201 POST
- 204 DELETE
- 400 Bad request
- 401 Unauthorized/invalid token
- 402 Payment required
- 403 Forbidden
- 404 Not found
- 423 Locked
- 498 Token expired
- Never release an API without a version number.
- Versions should be integers, not decimal numbers, prefixed with ‘v’. For example:
- Good: v1, v2, v3
- Bad: v-1.1, v1.2, 1.3
- Maintain APIs at least one version back.
- If no limit is specified, return results with a default limit.
- To get records 51 through 75 do this:
- http://example.com/api/v1/magazines?page[limit]=25&page[offset]=50
- offset=50 means, ‘skip the first 50 records’
- limit=25 means, ‘return a maximum of 25 records’
Information about record limits and total available count should also be included in the response. Example:
{
"total": {
"count": 227,
"offset": 25,
"limit": 25
},
"results": [],
"links": {}
}
-
Where specified, a links member can be used to represent links. The value of each links member MUST be an object (a "links object").
-
Each member of a links object is a "link". A link MUST be represented as either:
- a string containing the link’s URL.
- an object ("link object") which can contain the following members:
- href: a string containing the link’s URL.
- meta: a meta object containing non-standard meta-information about the link.
Example:
{
"links": {
"first": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=0",
"prev": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=25",
"self": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=50",
"next": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=75",
"last": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=150"
}
}
Example: http://example.com/api/v1/magazines.json
Response body:
{
"total": {
"count": 227,
"offset": 50,
"limit": 25
},
"results": [
{
"id": "1234",
"type": "magazine",
"title": "Public Water Systems",
"tags": [
{"id": "125", "name": "Environment"},
{"id": "834", "name": "Water Quality"}
],
"created": "1231621302"
},
{
"id": 2351,
"type": "magazine",
"title": "Public Schools",
"tags": [
{"id": "125", "name": "Elementary"},
{"id": "834", "name": "Charter Schools"}
],
"created": "126251302"
},
{
"id": 2351,
"type": "magazine",
"title": "Public Schools",
"tags": [
{"id": "125", "name": "Pre-school"},
],
"created": "126251302"
}
],
"links": {
"first": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=0",
"prev": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=25",
"self": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=50",
"next": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=75",
"last": "http://example.com/api/v1/magazines?page[limit]=25&page[offset]=150"
}
}
Example: http://example.com/api/v1/magazines/[id].json
Response body:
{
"id": "1234",
"type": "magazine",
"title": "Public Water Systems",
"tags": [
{"id": "125", "name": "Environment"},
{"id": "834", "name": "Water Quality"}
],
"created": "1231621302"
}
Example: Create – POST http://example.com/api/v1/magazines/[id]/articles
Request body:
{
"title": "Raising Revenue",
"author_first_name": "Jane",
"author_last_name": "Smith",
"author_email": "[email protected]",
"year": "2012",
"month": "August",
"day": "18",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget ante ut augue scelerisque ornare. Aliquam tempus rhoncus quam vel luctus. Sed scelerisque fermentum fringilla. Suspendisse tincidunt nisl a metus feugiat vitae vestibulum enim vulputate. Quisque vehicula dictum elit, vitae cursus libero auctor sed. Vestibulum fermentum elementum nunc. Proin aliquam erat in turpis vehicula sit amet tristique lorem blandit. Nam augue est, bibendum et ultrices non, interdum in est. Quisque gravida orci lobortis... "
}