8 reasons your API sucks

Anthony Shaw
6 min readMay 1, 2016

--

I spend a lot of time programming against REST APIs in C# and Python, each with it’s own separate challenges.

Designing APIs is hard. Keeping the entire development team(s) to the design principals of your API design is even harder.

If you’re thinking about putting together a new API, please think about your API as your interface for developers. The time at which a developer has to implement an API client class is typically after the product has been purchased or the vendor has been decided upon, so they have no choice, they have to make it work.

So this is me, at the end of the process shouting at the computer for the 20th vendor whose API sucks.

These are the things you did wrong.

1. You wrote your API documentation assuming nobody would read it

A good example: The Slack API https://api.slack.com/ is very well documented, they tick all of my boxes:

  • The website is easy to navigate
  • It combines examples, human written descriptions to the methods, the parameters and the response models
  • The possible error codes are well documented so you can program and test for them in advance

A bad example: The worst culprits here are projects that have fallen into these traits.

  • You rely on auto-generated documentation “Connect POST requests to attach of Pod”? Is Yoda your technical writer? (This is the Kubernetes API, and it has been resolved)
  • No API documentation and yet brag about their automation capabilities. For example ScienceLogic’s EM7 monitoring suite.
  • API documentation is only available to registered customers. This is painful for open-source authors. For example most of Cisco’s APIs.
  • You put the entire API documentation on one massive PDF. Come on, are you just trying to make life hard for me?

2. Your API authentication mechanism is too complex for mere mortals

API authentication typically falls into 2 scenarios:

  1. You are developing an app designed to be used actively by a user either using a browser or a native mobile or desktop application.
  2. You are developing some automation system to carry out tasks on the system.

That’s it. There are 2 scenarios.

So, why do API developers insist on only supporting 1? or 6?! Here are my tips:

  • Nobody likes using certificates. Stop using them, please. It makes it really hard for calling the API from a remote system when you don’t maybe have access to the file-system, it forces you to store the certificates somewhere central which defeats the point of having such a high-level of authentication. You rarely support Windows, Linux and Mac OS users equally.
  • Keep it simple, unless your API secures government data or trade secrets, the harder you make the authentication process, the more likely it is that users’ will get it wrong (or bypass it altogether).
  • Try and support both scenarios, OAuth 2.0 works well for active authentication, whether a web application or mobile app.

3. You started with resource-focused URIs and then gave up

I find the Docker APIs very frustrating, the support JSON and JSON-only, which is common these days. To list image and containers in Docker you would expect the URIs to be:

GET /images

GET /containers

Right? No. They are

GET /images/json

GET /containers/json

Ok, so to get the history of a container then I guess it’s GET /events/json then? No.

4. You don’t provide any versioning mechanism and then break compatibility

I had this with the GoDaddy APIs recently. They kind-of implemented versioning, that is their API is:-

/v1/domains/

But then a number of users started raising issues with the API implementation I’d developed and after looking into it, the models had changed (and so had the behaviour) but the API was still /v1/domains. I’d taken all of my responses to the API and put them into concrete fixtures in my unit tests but now these tests are useless. This happens, ALL THE TIME. If you’re going to break compatibility, change the URL and keep the old version, or raise an error explaining this.

5. You force me to make too many API requests for really simple use cases

In a perfect world, APIs would support the HEAD verb to list resources of a particular type, their friendly name, their ID and any other critical information. Then using the GET verb I could page information out with the details of those resources.

What I see all too-often is this:

GET /resources/boxes/

Gives a list of boxes (our fictional resource name) but doesn’t tell me the dimensions of the box. I want to display that on my website. But their API allows me to say

GET /resources/boxes/{id}/details

Which has that information, but now I need to do that in a loop for every box I return completely destroying my UX.

Instead, they should use HEAD /resources/boxes for the basic information and then GET /resources/boxes for the details. Just because you haven’t indexed your joins correctly shouldn’t mean I need to make extra API calls for every resource.

6. You realised what HTTP Verbs were for too late

This is pretty common, since HTTP verbs are “cool” these days. I see APIs where 90% of the functionality was developed with GET/POST and then all of a sudden features and actions start appearing using PUT/PATCH/DELETE.

POST /v1/resources/cheese/delete

DELETE /v1/resources/breadsticks/{id}

Am I just being pedantic now? Well, no because I want to create a set of utility methods for common things like delete that handle errors like resource does not exist, resource busy. But I don’t want to have to develop 2x the number of utility methods just because one of your engineers went off and implemented things differently.

7. You didn’t implement paging

Now I have to do it client side. Thanks, so usability is now my issue?

8. Your data models are not consistent.

I’m going to pick on Docker again, let’s list the containers.

GET /containers/json

[ { “Id”: “8dfafdbc3a40”, “Names”:[“/boring_feynman”], “Image”: “ubuntu:latest”, “Command”: “echo 1”, “Created”: 1367854155, “Status”: “Exit 0”, “Ports”: [{“PrivatePort”: 2222, “PublicPort”: 3333, “Type”: “tcp”}], “Labels”: { “com.example.vendor”: “Acme”, “com.example.license”: “GPL”, “com.example.version”: “1.0” }, “SizeRw”: 12288, “SizeRootFs”: 0 }, …

OK, so there is a field called Names, which is a list, the Image field is a string representing which Docker image was used. Seems to make sense.

Now I program in whichever language I’m using a data model to reflect that. I store the ID’s and later on I want to refresh the data for a particular container.

Now when I say:

GET /containers/4fa6e0f0c678/json

  1. The Names field no longer exists. there is however a Name field. (So what happens if there are more than 1?)
  2. The Image field is now nested inside another data structure named “Config”
  3. There is now another Image field at the top level which is the ID of the image, not the name.

When you list certain resources and get a single resource, the data model should be the same.

Does all this really matter or are you just bitter?

Well, I am a little bit bitter because of all the time I’ve wasted on badly designed APIs. But that isn’t the main point.

If you are selling a consumer API as a feature of your product, you need to consider the consumers. If you fall into all of these issues I’ve lined up, the chances are the developer that was tasked with implementing this now hates your product, that’s a difficult position to start on. Also, the chances are whatever code they developed is fragile, buggy and slow, so the users of that implementation aren’t going to be happy either.

And who will get the blame?

Remember an API is for life, not just for Christmas.

--

--

Anthony Shaw

Group Director of Talent at Dimension Data, father, Christian, Python Software Foundation Fellow, Apache Foundation Member.