├── .about.yml ├── CONTRIBUTING.md ├── LICENSE.md └── README.md /.about.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # .about.yml project metadata 3 | # 4 | # Short name that acts as the project identifier (required) 5 | name: api-standards 6 | 7 | # Full proper name of the project (required) 8 | full_name: API Standards 9 | 10 | # The type of content in the repo 11 | # values: app, docs, policy 12 | type: policy 13 | 14 | # Describes whether a project team, working group/guild, etc. owns the repo (required) 15 | # values: guild, working-group, project 16 | owner_type: individual 17 | 18 | # Maturity stage of the project (required) 19 | # values: discovery, alpha, beta, live, sunset, transfer, end 20 | stage: live 21 | 22 | # Description of the project 23 | description: 18F's view of API best practices and standards 24 | 25 | # Tags that describe the project or aspects of the project 26 | tags: [standards, best-practices, apis, guides] 27 | 28 | # Should be 'true' if the project has a continuous build (required) 29 | # values: true, false 30 | testable: false 31 | 32 | # Team members contributing to the project (required) 33 | # Items: 34 | # - github: GitHub user name 35 | # id: Internal team identifier/user name 36 | # role: Team member's role; leads should be designated as 'lead' 37 | 38 | # NOTE from arowla: In choosing team members, I went with contributors 39 | # who are still at 18F who have made at least 2 commits. Many other people 40 | # have made valuable contributions to this project. 41 | # 42 | team: 43 | - github: konklone 44 | id: eric 45 | role: lead 46 | - github: adelevie 47 | id: alan 48 | role: lead 49 | - github: arowla 50 | id: alison 51 | role: contributor 52 | - github: gbinal 53 | id: gray 54 | role: contributor 55 | 56 | # Licenses that apply to the project and/or its components (required) 57 | licenses: 58 | api-standards: 59 | name: CC0 60 | url: https://github.com/18F/api-standards/blob/master/LICENSE.md 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Welcome! 2 | 3 | We're so glad you're thinking about contributing to an 18F open source project! If you're unsure about anything, just ask -- or submit the issue or pull request anyway. The worst that can happen is you'll be politely asked to change something. We love all friendly contributions. 4 | 5 | We want to ensure a welcoming environment for all of our projects. Our staff follow the [18F Code of Conduct](https://github.com/18F/code-of-conduct/blob/master/code-of-conduct.md) and all contributors should do the same. 6 | 7 | We encourage you to read this project's CONTRIBUTING policy (you are here), its [LICENSE](LICENSE.md), and its [README](README.md). 8 | 9 | If you have any questions or want to read more, check out the [18F Open Source Policy GitHub repository]( https://github.com/18f/open-source-policy), or just [shoot us an email](mailto:18f@gsa.gov). 10 | 11 | ## Public domain 12 | 13 | This project is in the public domain within the United States, and 14 | copyright and related rights in the work worldwide are waived through 15 | the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/). 16 | 17 | All contributions to this project will be released under the CC0 18 | dedication. By submitting a pull request, you are agreeing to comply 19 | with this waiver of copyright interest. 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | As a work of the United States Government, this project is in the 2 | public domain within the United States. 3 | 4 | Additionally, we waive copyright and related rights in the work 5 | worldwide through the CC0 1.0 Universal public domain dedication. 6 | 7 | ## CC0 1.0 Universal Summary 8 | 9 | This is a human-readable summary of the [Legal Code (read the full text)](https://creativecommons.org/publicdomain/zero/1.0/legalcode). 10 | 11 | ### No Copyright 12 | 13 | The person who associated a work with this deed has dedicated the work to 14 | the public domain by waiving all of his or her rights to the work worldwide 15 | under copyright law, including all related and neighboring rights, to the 16 | extent allowed by law. 17 | 18 | You can copy, modify, distribute and perform the work, even for commercial 19 | purposes, all without asking permission. 20 | 21 | ### Other Information 22 | 23 | In no way are the patent or trademark rights of any person affected by CC0, 24 | nor are the rights that other persons may have in the work or in how the 25 | work is used, such as publicity or privacy rights. 26 | 27 | Unless expressly stated otherwise, the person who associated a work with 28 | this deed makes no warranties about the work, and disclaims liability for 29 | all uses of the work, to the fullest extent permitted by applicable law. 30 | When using or citing the work, you should not imply endorsement by the 31 | author or the affirmer. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 18F API Standards 2 | 3 | **[18F](https://18f.gsa.gov/)** is a technology team inside the US federal government. 18F is very API-focused: our first project was an [API for business opportunities](https://fbopen.gsa.gov/). 4 | 5 | This document captures **18F's view of API best practices and standards**. We aim to incorporate as many of them as possible into our work. 6 | 7 | APIs, like other web applications, vary greatly in implementation and design, depending on the situation and the problem the application is solving. 8 | 9 | This document provides a mix of: 10 | 11 | * **High level design guidance** that individual APIs interpret to meet their needs. 12 | * **Low level web practices** that most modern HTTP APIs use. 13 | 14 | ### Design for common use cases 15 | 16 | For APIs that syndicate data, consider several common client use cases: 17 | 18 | * **Bulk data.** Clients often wish to establish their own copy of the API's dataset in its entirety. For example, someone might like to build their own search engine on top of the dataset, using different parameters and technology than the "official" API allows. If the API can't easily act as a bulk data provider, provide a separate mechanism for acquiring the backing dataset in bulk. 19 | * **Staying up to date.** Especially for large datasets, clients may want to keep their dataset up to date without downloading the data set after every change. If this is a use case for the API, prioritize it in the design. 20 | * **Driving expensive actions.** What would happen if a client wanted to automatically send text messages to thousands of people or light up the side of a skyscraper every time a new record appears? Consider whether the API's records will always be in a reliable unchanging order, and whether they tend to appear in clumps or in a steady stream. Generally speaking, consider the "entropy" an API client would experience. 21 | 22 | ### Using one's own API 23 | 24 | The #1 best way to understand and address the weaknesses in an API's design and implementation is to use it in a production system. 25 | 26 | Whenever feasible, design an API in parallel with an accompanying integration of that API. 27 | 28 | ### Point of contact 29 | 30 | Have an obvious mechanism for clients to report issues and ask questions about the API. 31 | 32 | When using GitHub for an API's code, use the associated issue tracker. In addition, publish an email address for direct, non-public inquiries. 33 | 34 | ### Notifications of updates 35 | 36 | Have a simple mechanism for clients to follow changes to the API. 37 | 38 | Common ways to do this include a mailing list, or a [dedicated developer blog](https://developer.github.com/changes/) with an RSS feed. 39 | 40 | ### API Endpoints 41 | 42 | An "endpoint" is a combination of two things: 43 | 44 | * The verb (e.g. `GET` or `POST`) 45 | * The URL path (e.g. `/articles`) 46 | 47 | Information can be passed to an endpoint in either of two ways: 48 | 49 | * The URL query string (e.g. `?year=2014`) 50 | * HTTP headers (e.g. `X-Api-Key: my-key`) 51 | 52 | When people say "RESTful" nowadays, they really mean designing simple, intuitive endpoints that represent unique functions in the API. 53 | 54 | Generally speaking: 55 | 56 | * **Avoid single-endpoint APIs.** Don't jam multiple operations into the same endpoint with the same HTTP verb. 57 | * **Prioritize simplicity.** It should be easy to guess what an endpoint does by looking at the URL and HTTP verb, without needing to see a query string. 58 | * Endpoint URLs should advertise resources, and **avoid verbs**. 59 | 60 | Some examples of these principles in action: 61 | 62 | * [FBOpen API documentation](https://18f.github.io/fbopen/) 63 | * [OpenFDA example query](https://open.fda.gov/api/reference/#example-query) 64 | * [Sunlight Congress API methods](https://sunlightlabs.github.io/congress/#using-the-api) 65 | 66 | ### Just use JSON 67 | 68 | [JSON](https://en.wikipedia.org/wiki/JSON) is an excellent, widely supported transport format, suitable for many web APIs. 69 | 70 | Supporting JSON and only JSON is a practical default for APIs, and generally reduces complexity for both the API provider and consumer. 71 | 72 | General JSON guidelines: 73 | 74 | * Responses should be **a JSON object** (not an array). Using an array to return results limits the ability to include metadata about results, and limits the API's ability to add additional top-level keys in the future. 75 | * **Don't use unpredictable keys**. Parsing a JSON response where keys are unpredictable (e.g. derived from data) is difficult, and adds friction for clients. 76 | * **Use consistent case for keys**. Whether you use `under_score` or `CamelCase` for your API keys, make sure you are consistent. 77 | 78 | ### Use a consistent date format 79 | 80 | And specifically, [use ISO 8601](https://xkcd.com/1179/), in UTC. 81 | 82 | For just dates, that looks like `2013-02-27`. For full times, that's of the form `2013-02-27T10:00:00Z`. 83 | 84 | This date format is used all over the web, and puts each field in consistent order -- from least granular to most granular. 85 | 86 | 87 | ### API Keys 88 | 89 | These standards do not take a position on whether or not to use API keys. 90 | 91 | But _if_ keys are used to manage and authenticate API access, the API should allow some sort of unauthenticated access, without keys. 92 | 93 | This allows newcomers to use and experiment with the API in demo environments and with simple `curl`/`wget`/etc. requests. 94 | 95 | Consider whether one of your product goals is to allow a certain level of normal production use of the API without enforcing advanced registration by clients. 96 | 97 | 98 | ### Error handling 99 | 100 | Handle all errors (including otherwise uncaught exceptions) and return a data structure in the same format as the rest of the API. 101 | 102 | For example, a JSON API might provide the following when an uncaught exception occurs: 103 | 104 | ```json 105 | { 106 | "message": "Description of the error.", 107 | "exception": "[detailed stacktrace]" 108 | } 109 | ``` 110 | 111 | HTTP responses with error details should use a `4XX` status code to indicate a client-side failure (such as invalid authorization, or an invalid parameter), and a `5XX` status code to indicate server-side failure (such as an uncaught exception). 112 | 113 | 114 | ### Pagination 115 | 116 | If pagination is required to navigate datasets, use the method that makes the most sense for the API's data. 117 | 118 | #### Parameters 119 | 120 | Common patterns: 121 | 122 | * `page` and `per_page`. Intuitive for many use cases. Links to "page 2" may not always contain the same data. 123 | * `offset` and `limit`. This standard comes from the SQL database world, and is a good option when you need stable permalinks to result sets. 124 | * `since` and `limit`. Get everything "since" some ID or timestamp. Useful when it's a priority to let clients efficiently stay "in sync" with data. Generally requires result set order to be very stable. 125 | 126 | #### Metadata 127 | 128 | Include enough metadata so that clients can calculate how much data there is, and how and whether to fetch the next set of results. 129 | 130 | Example of how that might be implemented: 131 | 132 | ```json 133 | { 134 | "results": [ ... actual results ... ], 135 | "pagination": { 136 | "count": 2340, 137 | "page": 4, 138 | "per_page": 20 139 | } 140 | } 141 | ``` 142 | 143 | ### Always use HTTPS 144 | 145 | Any new API should use and require [HTTPS encryption](https://en.wikipedia.org/wiki/HTTP_Secure) (using TLS/SSL). HTTPS provides: 146 | 147 | * **Security**. The contents of the request are encrypted across the Internet. 148 | * **Authenticity**. A stronger guarantee that a client is communicating with the real API. 149 | * **Privacy**. Enhanced privacy for apps and users using the API. HTTP headers and query string parameters (among other things) will be encrypted. 150 | * **Compatibility**. Broader client-side compatibility. For CORS requests to the API to work on HTTPS websites -- to not be blocked as mixed content -- those requests must be over HTTPS. 151 | 152 | HTTPS should be configured using modern best practices, including ciphers that support [forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy), and [HTTP Strict Transport Security](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security). **This is not exhaustive**: use tools like [SSL Labs](https://www.ssllabs.com/ssltest/analyze.html) to evaluate an API's HTTPS configuration. 153 | 154 | For an existing API that runs over plain HTTP, the first step is to add HTTPS support, and update the documentation to declare it the default, use it in examples, etc. 155 | 156 | Then, evaluate the viability of disabling or redirecting plain HTTP requests. See [GSA/api.data.gov#34](https://github.com/18F/api.data.gov/issues/34) for a discussion of some of the issues involved with transitioning from HTTP->HTTPS. 157 | 158 | #### Server Name Indication 159 | 160 | If you can, use [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication) (SNI) to serve HTTPS requests. 161 | 162 | SNI is an extension to TLS, [first proposed in 2003](http://tools.ietf.org/html/rfc3546), that allows SSL certificates for multiple domains to be served from a single IP address. 163 | 164 | Using one IP address to host multiple HTTPS-enabled domains can significantly lower costs and complexity in server hosting and administration. This is especially true as IPv4 addresses become more rare and costly. SNI is a Good Idea, and it is widely supported. 165 | 166 | However, some clients and networks still do not properly support SNI. As of this writing, that includes: 167 | 168 | * Internet Explorer 8 and below on Windows XP 169 | * Android 2.3 (Gingerbread) and below. 170 | * All versions of Python 2.x (a version of Python 2.x with SNI [is planned](http://legacy.python.org/dev/peps/pep-0466/)). 171 | * Some enterprise network environments have been configured in some custom way that disables or interferes with SNI support. One identified network where this is the case: the White House. 172 | 173 | When implementing SSL support for an API, evaluate whether SNI support makes sense for the audience it serves. 174 | 175 | ### Use UTF-8 176 | 177 | Just [use UTF-8](http://utf8everywhere.org). 178 | 179 | Expect accented characters or "smart quotes" in API output, even if they're not expected. 180 | 181 | An API should tell clients to expect UTF-8 by including a charset notation in the `Content-Type` header for responses. 182 | 183 | An API that returns JSON should use: 184 | 185 | ``` 186 | Content-Type: application/json; charset=utf-8 187 | ``` 188 | 189 | ### CORS 190 | 191 | For clients to be able to use an API from inside web browsers, the API must [enable CORS](http://enable-cors.org). 192 | 193 | For the simplest and most common use case, where the entire API should be accessible from inside the browser, enabling CORS is as simple as including this HTTP header in all responses: 194 | 195 | ``` 196 | Access-Control-Allow-Origin: * 197 | ``` 198 | 199 | It's supported by [every modern browser](http://enable-cors.org/client.html), and will Just Work in many JavaScript clients, like [jQuery](https://jquery.com). 200 | 201 | For more advanced configuration, see the [W3C spec](http://www.w3.org/TR/cors/) or [Mozilla's guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). 202 | 203 | **What about JSONP?** 204 | 205 | JSONP is [not secure or performant](https://gist.github.com/tmcw/6244497). If IE8 or IE9 must be supported, use Microsoft's [XDomainRequest](http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx?Redirected=true) object instead of JSONP. There are [libraries](https://github.com/mapbox/corslite) to help with this. 206 | 207 | 208 | ## Public domain 209 | 210 | This project is in the public domain within the United States, and 211 | copyright and related rights in the work worldwide are waived through 212 | the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/). 213 | 214 | All contributions to this project will be released under the CC0 215 | dedication. By submitting a pull request, you are agreeing to comply 216 | with this waiver of copyright interest. 217 | 218 | --------------------------------------------------------------------------------