Serve both audiences
APIs must satisfy two distinct audiences. They need rigor and performance, to keep up with the demands of the client processes that depend on them. They also need to be documented and designed well enough that programmers can usefully develop reliable applications with them.
Nearly all successful APIs have at least adequate documentation, and often such other programmer comforts as examples, evaluation products, language-specific software development kits (SDKs), and even supported user communities.
API documentation is paramount. If in doubt, invest more in documentation. When not in doubt, documentation still needs more attention. Documentation is crucial to acceptance by working developers, who are crucial to successful API-based applications. GitHub provides one nice model of a documented API.
Good REST APIs are good software; they are simultaneously good consumer products, with the understanding that their consumers are application programmers.
Fielding defined appropriate use within HTTP of the canonical verbs GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. Don't change state with GET, of course.
The HTTP verbs are far from equally important. Some designs avoid PATCH or PUT, for instance, because their support in common Web proxies is uneven. More precisely, while they might support PUT, they also need to recognize requests like:
POST /api/v2/Customer/3514 HTTP/1.1
as effective synonyms for the corresponding PUTs. API servers often support such equivalences with OverrideHandlers and/or routing expressions.
REST is an acronym for REpresentational State Transfer; it's all about communication of state, and structured in terms of the resources which achieve this.
Good APIs know their version. Essentially all commentators agree on this. There is nearly no agreement on the mechanism, though. In effect, different successful APIs choose one of:
- embedding the version in the URL: /winning-api/v1.3/some-resource;
- specifying it in the header, as Accept or X-api-version or similar;
- providing the version as a parameter: /winning-api/some-resource?version=1.3; or
- a combination of the approaches above.
This lack of agreement about what the "best practice" is for the details of transmission of version information makes it doubly important that a public API clearly document its own choice.
Good APIs return sensible, relatively precise HTTP Status Codes. To return 200 OK for everything, or nearly everything, and then require the client code to parse content to discover error conditions, is simply wrong.
Beyond Fielding: operational refinements that help acceptance
Good APIs handle errors carefully. High-quality diagnostics pay off enormously; working developers who consume APIs are able to make far greater and faster progress when APIs clearly explain themselves.
Wise API publishers log usage, or, in different words, build in good analytics. Logging provides the crucial data to manage the lifecycle of an API wisely: logging gives an objective basis for analysis of questions such as, "how important to our users are items from more than thirty days prior?" or "how many customers will replication of our data to a European mirror help?"
JSON or XML? While nearly all commentators favor JSON as a delivery format, plenty of widely-used APIs still are expressed as XML. Even better: move beyond both, to hypermedia returns. Hypermedia As The Engine Of Application State (HATEOAS) looks like the right choice for new designs; still, as the survey referenced above documents, it accounts for only a tiny sliver of current APIs. Whatever a good API chooses in regard to format — and especially if the choice is to provide multiple formats — the Accept header ought explicitly reinforce that choice.
Good APIs paginate results over collections. Successful APIs inevitably need to account for load, and it's smarter to build in rate controls from the beginning. The default return value for a collection should be its first twenty items, rather than all members. Make sure all collection returns take limit and offset, with natural defaults such as limit=20 and offset=0.
Also consider such variant parameters as first, previous, next, and last. As much as possible, code collection metadata in HTTP Link-Headers. The general principle, already mentioned with Status Codes, is that separation of metadata from content simplifies parsing. Another example: return an entry total as HTTP header X-Total-Count.
Another smart way to improve effective performance is a query or result language that filters, sorts, and selects the attributes delivered as results. Especially with bandwidth — and CPU —limited mobile endpoints, server-side selection costs much less than sending out full datasets, and discarding the unneeded parts.
Limit rates early. Maybe releases through 0.08 or so can concentrate on functionality; before users even become aware of performance thresholds, though, good APIs start to accrue experience with rate-limiting.
Plenty of principles follow the formula: design X carefully. Caching, for instance, needs to be thoughtful, and well-designed, for it impacts performance crucially. The precise implementation of caching — reverse-proxy, Russian doll, or so on — is less clear. Other questions a good API tackles similarly:
- HTTPS or HTTP? Both?
- Should the API avoid PUT entirely?
- What different licenses are available? Is there a low-cost way for developers to enter the API?
- Are private access tokens a worthwhile complement to Basic Authentication?
- Are nouns consistently expressed as singular or plural?
Good REST APIs:
- are well-documented and reliable
- use HTTP verbs as Fielding originally defined
- support X-HTTP-METHOD-Override to accommodate picky proxies
- express URLs with nouns rather than verbs
- track version
- make expressive use of HTTP Status Codes
- handle errors carefully and explicitly
- log activity
- choose wisely between XML (increasingly rare), JSON, and HATEOAS (rapidly growing)
- paginate, filter, and sort results
- limit rates
- explicitly design in other commercially-significant API-specific features