API Design Guide
Introduction
This is a general design guide for the Avis, Budget, Payless (ABG) Platform APIs. It represents the best-practices that our product development teams follow when designing APIs. It does not represent the current state of all ABG APIs.
The primary goal of these guidelines is to establish a predictable and consistent look-and-feel for all API products, particularly for product development teams who intend to expose APIs on our Developer Portal.
Design Flow
A resource-oriented API is generally modeled as a resource hierarchy, where each node is either a simple resource or a collection.
By beginning the process of identifying the design flow of an API with your product manager or design lead, you will be better-equipped to define the resource, collection, client URL, and product name. To begin this process, be sure to:
- Identify the types of resources an API provides and the relationships between those resources;
- Identify the resource name(s) based on these relationships;
- Identify the existing naming conventions for these design schemes that may be reused;
- Identify and attach the minimum set of methods to resources.
Collections
A collection contains a list of resources of the same type. For example, ABG has a collection of Rental Cars. In general, collection names should be plural and lower case.
Collections must be differentiated to distinguish the business use case, end-user application and target audience. The goal of this section is to establish the name of the collection in which an API should reside.
/cars
The Cars collection indicates that the endpoints can be used as part of a traditional rental car application. The end-user is likely to be a corporate or special-occasion leisure customer that intends to rent a car. This collection should contain rental car location, reservation, and other rental resources. Endpoints within the Cars collection would be used within an airline application, rental car application, or for an online travel agency consumer.
Resources
A resource can be a singleton or a collection. For example, rental car “locations” is a collection resource and an individual “location” is a singleton resource. In general, resources should be lower case.
The resources that a given collection provides should be encapsulated in one word. If it cannot be encapsulated in one word, the name of the resource should be separated by an underscore.
The guidance herein is not meant to represent an exhaustive list of all possible resource naming conventions. Use the below examples to derive logic for collection and resource naming, or contact your design lead or product manager for guidance.
/reservation
An endpoint that reserves a car, updates the reservation of car, or cancels the reservation of a car, should use this resource. Since it is ultimately creating a new 'reservation' resource in the Cars collection, the collection will be /cars and the name of the resource will be /reservation, since only one reservation resource will be created, updated, or canceled.
/cars/reservation/
In general, the name of the resources an endpoint provides is generally reflected in the resource name. Determine the design flow of your API prior to development, including the core functionality and the resources it obtains. This will help you to determine potential naming of the collection and resource.
Product Naming
In general, the name of the collection and the resource should be reflected in the product name. This allows the API consumers to quickly identify the functionality of the endpoint within the developer portal.
Product names refer to the product marketing name of an API. A product name is generally determined by the collection, and the types of resources the API provides.
Product names should be consistently used by APIs, in documentation, terms of service, billing statements, and commercial contracts. To provide consistent developer experience across many APIs and over a long period of time, all names used by an API should be:
- simple
- intuitive
- consistent
In the interest of naming consistency, when the API requests or creates data for a single data set (ex: a receipt for a single user), the product name is singular. When the API requests multiple data sets, the product name is plural.
There will be some situations where a product name may not reflect all resources or collection names. For example, the Car Availability endpoint is /cars/catalog/vehicles. This endpoint gets availability and returns the vehicle inventory/catalog available at a given location. We’ve opted to use Car Availability as the product name.
Template
{Collection}+{Resource}
Examples
- A collection of Cars, which creates a single reservation resource on behalf of a single user (singleton-resource), would have the following product name.
Car Reservation
- It is bound to occur that a product name does not fit the template. In this case, work with your design lead or product manager to derive a name to fit the functionality. For example, a collection of Locations, which conducts a search based on a keyword, would have the following product name.
Location Keyword Search
HTTP Method & Endpoint Guidelines
The following method and endpoint guidelines are derived from ReST API best practices, as described in a variety of sources. In the interest of reducing ambiguity and promoting consistency among ABG products, this section uses somewhat more prescriptive language than is typically used in a best practices discussion.
Client URL
The client URL should only contain nouns (not actions or verbs) that both use existing conventions and make sense from the perspective of the developer customers. This is determined by the relationships between the collection and its resources. The URL must also use lower case conventions and underscores (if more than one word must be used to intuit its resources).
- /cars: to describe the rental car collection should be plural (ex: /cars), and the resource singular (ex: reservation), as it is creating, updating or canceling a single resource. That is, a single car reservation.
- /cars/locations: to describe the location collection should be plural.
Template
https://{domain}/{collection}/{resource}/{sub_collection}/
NOTE URLs have practical length limits that are quite low—generally 2,000 characters.
Examples
- Car Reservation, a collection of Cars, which creates a reservation resource on behalf of the user, would have the following endpoint.
/cars/reservation/
- Location Keyword Search, a collection of Cars, which searches for rental car Locations based on a keyword, would have the following endpoint. This is an atypical example where we've included the required query parameter to demonstrate naming.
/cars/locations?keyword=Boston
APIs may need to include an additional level of identifying sub-resources or sub-collections to represent a subset of the endpoint's overall functionality. Collection and resource names should be chosen carefully such that these need not change when products are versioned or rebranded.
HTTP Method
GET
According to w3c.org, the GET method retrieves whatever information (in the form of an entity) is identified by the Request-URI. If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in the response and not the source text of the process, unless that text happens to be the output of the process.
The response to a GET request is cacheable if and only if it meets the requirements for HTTP caching. See the w3c.org GET design principles for details.
When to Use
The GET method should be used to read/view a resource that is not likely to change, such as to view details of a desired rental car.
URLs have practical length limits that are quite low—generally 2,000 characters. Therefore, the GET method should not be used for complex search requests (using complex filtering with if/then conditions, or extensive request query parameters) or when sending sensitive information.
Example
Location Keyword Search retrieves the top three search results based on a given user keyword:
GET /cars/locations?keyword={userkeyword}
POST
According to w3c.org, the POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. POST is designed to allow a uniform method to cover the following functions:
- Annotation of existing resources;
- Posting a message to a bulletin board, newsgroup, mailing list or similar group of articles;
- Providing a block of data, such as the result of submitting a form, to a data-handling process;
- Extending a database through an append operation.
The POST method is generally non-idempotent and non-cacheable. See the w3c.org POST design principles for details.
When to Use
The POST method should be used if the submission response may change, such as for complex search APIs; for the “create” function, such as to create a user profile; to retrieve pricing information, or for new booking operations.
Example
Reserves a rental car, i.e., a single rental car resource:
POST /cars/reservation/
PATCH
According to w3c.org, applications extending the Hypertext Transfer Protocol (HTTP) may require a feature to do partial resource modification. The PUT method only allows a complete replacement of a document. This proposal adds a new HTTP method, PATCH, to modify an existing HTTP resource.
The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request-URI. The set of changes is represented in a format called a "patch document" identified by a media type. See the w3c.org PATCH design principles for details.
When to Use
The PATCH method should be used if the previous submission has changed, such as to update an existing ride request (e.g. to update the lat/long for a booking). To cancel an existing ride request, use the DELETE method.
Example
Updates an existing rental car reservation:
PATCH /cars/reservation/
DELETE
According to w3c.org, the DELETE method requests that the origin server remove the association between the target resource and its current functionality.
When to Use
The DELETE method should be used to undo the actions of a previous submission, such as to cancel an existing ride request, or to delete a user profile.
Example
Cancels an existing rental car reservation:
DELETE /cars/reservation/
PUT
According to w3c.org, the PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity should be considered as a modified version of the one residing on the origin server.
If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. See the w3c.org PUT design principles for details.
When to Use
The PUT method could be used if the previous submission has changed, such as to extensively modify an existing rental car reservation. To update an existing Reservation, use the PATCH method. To cancel an existing Reservation, use the DELETE method. Note: you should discuss the use of PUT with your product manager or design lead, as the use of PUT is still in discussion.
Example
Updates a rental car reservation:
PUT /cars/reservation/
URL Query Parameters
With regard to URL query parameters, services should:
- choose intuitive, unambiguous and succinct names;
- avoid naming conflicts by reusing names for dissimilar purposes;
- use plural nouns for arrays;
- use singular nouns for non-arrays;
- use lowercase letters;
- use under_scores;
- be case sensitive;
- must not accept or transmit sensitive data or user privacy data (PII) such as credentials, keys, passwords, SSN, or credit card numbers in URLs.
Query Parameters with POST
In cases where POST provides paged results, query parameters may be used in order to provide hypermedia links to the next page of results.
Cache-friendly APIs
If a resource must be highly cacheable (such as data with minimal change), query parameters should be utilized as opposed to POST + request body. As POST (generally) would make the response uncacheable, GET is preferred in these situations. A GET method is the only scenario in which query parameters may be required.
Designing for Backward Compatibility
APIs should be designed in a forward and extensible way to maintain compatibility and avoid duplication of resources, functionality and excessive versioning. APIs must adhere to the following principles to be considered backward-compatible:
- All changes must be additive;
- All changes must be optional;
- Semantics must not change;
- Query parameters and request body parameters must be unordered;
-
Additional functionality for an existing resource must be implemented either:
- As an optional extension, or
- As an operation on a new child resource, or
- By changing the request body while still accepting all the previous, existing request variations, if an existing operation (e.g., resource creation) cannot be reasonably extended.
Version scheme
API Product Lifecycle
API product lifecycle management is a term illustrating the need to manage all steps in a life of an API, from concept to retirement. These states of a product are referenced for understanding with regards to the backward-compatibility and versioning guidance below.
In total, the five states of an API’s product lifecycle are:
Name | Description | Availability |
---|---|---|
Designed |
|
No; however, "mock service" should have been enabled. |
Beta |
|
Only to select customers. Note: talk with your product manager or design lead to determine whether your endpoint should be publicly-available on the developer portal. |
Live |
|
Yes; to new and existing customers. |
Deprecated |
|
Yes; existing customers only. |
Retired |
|
No. |
Backward Compatibility
The following is a quick-reference guide to determining the version scheme for backward-, and backward-incompatible changes. This guidance should not be treated as a comprehensive list of every possible change. It is expected that API producers are aware of their own deployment requirements, including changes in implementation details.
Minor Version
The following backward-compatible changes are considered non-breaking, and would constitute a minor version:
- Adding a method
- Adding an optional query parameter to the request
- Adding an attribute to the response
- Adding a value to an enum (of a parameter's valid values)
- Adding an output-only resource field
NOTE minor API versions must maintain backward-compatibility with all previous minor versions within the same major version.
Major Version
The following backward-incompatible changes are considered breaking, and would constitute a major version:
- Adding a required field to the request
- Removing or renaming a service, interface, field, method or enum value
- Changing the type of a field (ex: from a number to a string)
- Changing a resource name format
- Changing visible behavior of existing requests
- Changing the URL format in the HTTP definition
- Adding a read/write field to a resource message
*NOTE a new major API version results in the deprecation of a pre-existing major API version, and is considered a significant business investment decision. API owners must justify the new major version before beginning significant design and development work. See the Major API considerations section for information on the business justifications you must consider.
Versioning Up
Whether backward-compatible or backward-incompatible, each time there is an incremental change to an API, the API specification must be versioned. This allows the change to be labeled, documented, reviewed, discussed, published and communicated.
vMajor.Minor.Patch
- We always introduce the first major "Live" version of an API as v1.
- We always introduce a minor release of any major version as v1.1. For example, if the version of an endpoint is v1.2, that means two minor releases have occurred for that endpoint.
- We always introduce the first patch release of any major release as v1.x.1. For example, v1.0.1. In this example, a major release has occurred, with no minor release, and a patch release has occurred.
Example
- The first “Live” version was released as v1. One month later, a new optional query parameter was added. This would result in the version scheme v1.1.
- A patch is released to implement a minor bug fix; this would result in the version scheme v1.1.1.
- Six months later, our customers request a new feature, so you add a few new optional query parameters and release a new minor version. This would result in the version scheme v1.2. (The version scheme scaled back down to vMajor.Minor; as there were no patches released with this minor version, so the patch ordinal is dropped. If a patch was released, it would roll-up into the minor version v1.1).
- Over time, you realize you need to make a backward-incompatible change to an existing method and release another major version. This would result in the version scheme v2. (The version scheme scaled back down to vMajor. Note: if any minor or patch-like changes were released, this would be rolled up into the major version release.)
- Unfortunately, a bug was released in v2, so a patch is released. This would result in the version scheme v2.0.1.
- Your customers request the ability to filter the response, so you add an optional query parameter and release a minor version. This would result in the version scheme v2.1.
- A new PATCH method was added. This is considered a minor release, and would result in the version scheme v2.2.
Major API Versioning Considerations
A new major version means that all of your developer customers need to migrate to new endpoints for any of the new design and development work to deliver value. Many customers will not move to a new version without added business value of a release. There is also a lot of overhead in managing the customer migration, and you need to support multiple APIs for some time.
API owners should explore all possible alternatives to introducing a new major API version to minimize the impact on customers, before introducing a new major version, including:
- The value to the customer delivered by the new version exceeds that of the existing version.
- The cost-benefit-analysis of the new major version exceeds that of (what will become) the deprecated version.
- The number of existing customers impacted (including internal, external customers and partners).
- Identify a communication and support plan with your product manager to inform existing customers of the new version, the business value of the new version, and migration path.
HTTP Status Code to Error Mapping
When an application raises a request to the server, it should receive (success or fail) feedback so that it can programmatically resolve a request.
The following guidance represents common HTTP method to status code mappings and should not be considered exhaustive. In addition to the standard errors below, API providers must plan to return errors specific to the functionality of the resource. Particularly, for 400 Bad Request and 403 Forbidden status codes.
You must follow the below guidelines regardless of the status code returned by a supplier. For example, if a supplier returns a 609, you must work with your design lead or product manager to identify which status code and message should be returned. This allows our API developer customers a simple, consistent, predictable means of resolving a request.
Status Code Ranges
When responding to API requests, the following status code ranges must be used.
Range | Meaning |
---|---|
2xx |
Successful execution. It is possible for a request to succeed in multiple ways. This status code specifies how it succeeded. |
4xx |
Generally these are problems with data in the request; data not found, invalid authentication/authorization. In most cases, this range is used to indicate to the application that it must modify the request and resubmit. |
5xx |
The server was not able to execute the method due system outage or maintenance. This may also occur when our suppliers are unable to fulfill a request. |
2xx Status Codes
Return the following responses for 2xx successful requests.
Status Code | 200 Success | 201 Created | 202 Accepted | 204 No Content | 206 Partial Content |
---|---|---|---|---|---|
GET |
X |
X |
|||
POST |
X |
X |
X |
|
|
PUT |
X |
X |
X |
|
|
PATCH |
X |
X |
|
||
DELETE |
X |
X |
|
Success Response Format
The following describes the elements that can appear in the response body of a successful response. Note: returning a success payload, with a corresponding code, message, and details, in the API response is optional.
status
A container for the success/error details
request_time
The time stamp of the request (Ex: 2017-11-06T15:35:38Z)
success.code
The HTTP status code value (Ex: 200)
success.message
The HTTP status code (Ex: Created)
success.details
Description of the success details (Ex: The resource was successfully created.")
Example
{
"status": {
"request_time": "2017-11-06T15:35:38Z",
"success": [
{
"code": "201",
"message": "Created",
"details": "The resource was successfully created."
}
]
}
}
200 Success
200 Success is returned when the service call is a GET (or a POST that contains exposed functionally to fetch data; it is not to be used to create a new resource).
This status code may occur when the response contains 1 or more response payloads retrieved from multiple resources and no update was made to any of the resources. For example, to retrieve a car location, profile details, fleet details or to retrieve availability.
It may also be returned for aggregator APIs to consolidate responses from multiple underlying resources.
Code | Message | Details |
---|---|---|
200 |
Success |
The resource was successfully retrieved. |
201 Created
201 Created is returned for POST method execution to indicate successful creation of a resource.
This status code may occur when a booking (i.e., reservation) or a profile was created.
Code | Message | Details |
---|---|---|
201 |
Created |
The resource was successfully created. |
204 No Content
204 No Content is returned when the server has fulfilled a PATCH or DELETE request for the resource but there is no entity body to return.
This status code occurs when a profile was deleted, a ride request was updated or canceled. This status code may be used if a 204 “update” is to update a street address or phone number as part of a ride request.
Code | Message | Details |
---|---|---|
204 |
No Content |
The resource was successfully patched based on partial changes. |
204 |
No Content |
The resource was successfully updated. |
204 |
No Content |
The resource was successfully deleted. |
206 Partial Content
206 Partial Content is returned when the server has fulfilled a partial GET request for the resource.
This status code occurs when calls to multiple systems are concurrent using the GET method, where the resource was retrieved and transmitted in the message body.
206 + 500: 206 may be combined with 500 if multiple underlying resources are being called (concurrently), and one or more of our suppliers have failed to fulfill a request. If one of our suppliers failed to return a response, then 206 should contain the number of supplier failures, a 500 error response, and the specific supplier that has failed in the “details” field. This allows the information to be passed to the end user.
206 + 404: 206 may also be combined with 404 if multiple underlying services are being called (concurrently), and one or more of our suppliers have no content to fulfill a request.
206
Code | Message | Details |
---|---|---|
Partial Content |
The resource has returned a partial response. If combined with 500 Internal Server Error, then some of our suppliers failed to fulfill the request. |
|
206 |
Partial Content |
The resource has returned a partial response. If combined with 404 Not Found, then the request was successful, but no results were found from some of our suppliers for the given input. |
Example
{
"status": {
"request_time": "2017-11-06T15:35:38Z",
"request_errors": 2,
"errors": [
{
"code": "500",
"message": "Internal Server Error",
"reason": "supplier_failure",
"details": "Zipcar failed to fulfill the request."
},
{
"code": "500",
"message": "Internal Server Error",
"reason": "supplier_failure",
"details": "Budget failed to fulfill the request."
}
"success": [
{
"code": "206",
"message": "Partial Content",
"details": "The resource has returned a partial response. If combined with 500 Internal Server Error, then some of our suppliers failed to fulfill the request."
}
]
}
}
4xx Status Codes
Return the following responses for 4xx failed requests.
Status Code | 400 Bad Request | 401 Unauthorized | 403 Forbidden | 404 Not Found | 408 Request Timeout | 429 Too Many Requests |
---|---|---|---|---|---|---|
GET |
X |
X |
X |
X |
X |
X |
POST |
X |
X |
X |
X |
X |
X |
PUT |
X |
X |
X |
X |
X |
X |
PATCH |
X |
X |
X |
X |
X |
X |
DELETE |
X |
X |
X |
X |
X |
X |
Error Response Format
The following describes the elements that can appear in the response body of an error. The example values provided below are meant for illustration purposes only and is not to be considered an exhaustive list of all possible values.
Element | Description |
---|---|
status |
A container for the error details |
request_time |
The time stamp of the request (Ex: 2017-11-06T15:35:38Z) |
errors.code |
The HTTP status code value (Ex: 401) |
errors.message |
The HTTP status code (Ex: Unauthorized) |
errors.reason |
The reason the request has failed (Ex: authentication_failure) |
errors.details |
Description of the error (Ex: Invalid, missing or expired credentials were provided in the request.") |
400 Bad Request
400 Bad Request is returned when the request could not be understood by the server.
400 errors are API specific (only). API specific examples are provided for context, below; there are no standard errors. Therefore, developers should work with the design authority team to provide API-specific error messaging (including “reason” and “details”).
400 includes missing required values, valid data types, minimum/maximum values, ranges, and format.
NOTE you must have coded specific provider/supplier errors into your application. For example, if Zipcar accepts a radius value of 50 miles or less, or if Avis accepts a radius value of 40 miles or less. These should not be returned to the client/customer/application to resolve, as they cannot be resolved by the customer.
Code | Message | Reason | Details |
---|---|---|---|
400 |
Bad Request |
invalid_request |
"pickup_later" is required |
400 |
Bad Request |
invalid_format |
"credit_card_number" must be type "number |
400 |
Bad Request |
invalid_range |
"pickup_later" must be today or a future date |
Example
{
"status": {
"request_time": "2017-11-06T15:35:38Z",
"request_errors": 1,
"errors": [
{
"code": "400",
"message": "Bad Request",
"reason": "invalid_range",
"details": "pickup_later must be today or a future date."
}
]
}
}
401 Unauthorized
401 Unauthorized is returned when the request requires authentication and none was provided, or what was provided is invalid.
401 is used when an application has passed invalid user credentials or is not authorized to invoke the underlying service. This applies when the underlying service mandates that the access token or authorization details must be passed from the end-user application rather than to the API.
Code | Message | Reason | Details |
---|---|---|---|
401 |
Unauthorized |
authentication_failure |
Invalid, missing or expired credentials were provided in the request. |
401 |
Unauthorized |
required |
Request message requires the WWW-Authenticate header. |
Example
{
"status": {
"request_time": "2017-11-06T15:35:38Z",
"errors": [
{
"code": "401",
"message": "Unauthorized",
"reason": "authentication_failure",
"details": "Invalid, missing or expired credentials were provided in the request."
}
]
}
}
403 Forbidden
403 Forbidden is returned when the application is not authorized to access the resource (although it may have valid credentials). The server may also respond with this status code if accessing a request during a specific window is prohibited by the server.
If possible, API providers should provide specific error messaging for 403 Forbidden in lieu the standard error below. For example, “Application not authorized to access this geofence.”
Code | Message | Reason | Details |
---|---|---|---|
403 |
Forbidden |
permissions_failure |
Application not authorized to call the resource. This may be due to the credentials used for given request input. |
404 Not Found
404 Not Found is returned when the server has not found anything matching the request URI (either due to no results, or because a non-existent endpoint or resource was requested).
The server may also respond with this status code if there was a 401 or 403 that the service wants to mask for security reasons.
Code | Message | Reason | Details |
---|---|---|---|
404 |
Not Found |
no_results |
Request was successful, but no results were found for the given input. |
404 |
Not Found |
resource_failure |
Server cannot find the requested resource. This may be due to incorrect URI or a resource that is not available. |
404 |
Not Found |
no_supplier_results |
The resource has returned a partial response. If combined with 404 Bad Request, then the request was successful, but no results were found from some of our suppliers for the given input. |
408 Request Timeout
408 Request Timeout is returned when the client has established a connection but did not send a request within the time that the server was prepared to wait.
You should send a custom "close" Connection (“Connection: close”) header field in this scenario.
Code | Message | Reason | Details |
---|---|---|---|
408 |
Request Timeout |
timeout |
Server is unable to respond within the timeout window and has terminated the connection. |
429 Too Many Requests
429 Too Many Requests is returned when the rate limit for the user, the application, or the token has exceeded a predefined value.
Until the server is ready to accept more requests, the application would respond to an end-user with this status code by dropping the requests.
Code | Message | Reason | Details |
---|---|---|---|
429 |
Not Found |
throttled |
Rate limit for the application exceeds predefined value. |
5xx Status Codes
Return the following responses for 5xx failed requests.
Status Code | 500 Internal Server Error | 503 Service Unavailable |
---|---|---|
GET |
X |
X |
POST |
X |
X |
PUT |
X |
X |
PATCH |
X |
X |
DELETE |
X |
X |
500 Internal Server Error
500 Internal Server Error is returned when a system or application error has occurred. It generally indicates that something unexpected has gone wrong on the server. 500 should not be utilized for client validation or logic error handling. This is used for a standard server side exception for a persistent error on the server side.
If underlying services are being called and the method is GET, then 500 should be combined with 206 Partial Content. For example, this response would apply when multiple underlying services are called, and only some of the suppliers return content. If all suppliers have failed, you must respond with a 500.
Code | Message | Reason | Details |
---|---|---|---|
500 |
Internal Server Error |
unexpected_condition |
An unexpected condition occurred that prevented the service or application from fulfilling the request. |
500 |
Internal Server Error |
supplier_failure |
Zipcar failed to fulfill the request. |
500 |
Internal Server Error |
supplier_failure |
Budget failed to fulfill the request. |
500 |
Internal Server Error |
supplier_failure |
Avis failed to fulfill the request. |
503 Service Unavailable
503 Service Unavailable is returned for temporary conditions, such as when the server is unable to handle the request for a service due to temporary maintenance.
You should return the Retry-After HTTP header with the estimated time for recovery of the service if possible.
Code | Message | Reason | Details |
---|---|---|---|
503 |
Service Unavailable |
maintenance |
Server is down for maintenance or is overloaded. |