ACTIVE Network API Developer Blog

Lessons Learned in API Development

Recently I've been working with some of our legacy APIs - either creating backwards compatible adapters or writing a new improved version of a particular API.  At several points during this time, I've asked myself "What were we thinking?" I'm sure at some point a lot of our API consumers have asked the same thing. To be fair, a lot of these legacy APIs were written to solve a specific business need for different groups. However that doesn't absolve us of any of the blame nor does it ease the pain I'm currently experiencing.

Be Explicit

Our company has a plethora of registration systems that serve a variety of markets.  These registration systems feed their events (assets as we call them) into our directory which is published via our Search API. To get more details on these assets, we provide a Asset Details API which gets further information from endpoints exposed by the different registration systems if they existed.  When we first released the Asset Details API, the underlying endpoint only exposed details from one registration platform. It was our largest platform in terms of number of events and it covered the majority of assets that came back in our search results. We assumed that the 80% coverage would be enough and that our consumers would know that this endpoint only served assets from that one registration platform.  Oops.  Our consumers assumed that any of the assets that came back in results could be fed into this API to get the additional info (of course, this is the correct assumption). We began fielding lots of questions as to why one particular asset had data in the API while others did not. Be explicit in what the endpoint is used for in both the name/url and the documentation of the endpoint.

Separation of Concerns

When an API grows out of an application, there is a temptation to mix the code for both the application and the API since they are essentially using the same things.  Resist that temptation.  You can have them in the same code base and even the same application but make some sort of delineation in your code between the two. If you decided to split the API code into its own project, this separation will make it an easier task. The V1 Search API endpoint used the same url and the same controller as the web user interface. All you had to do is change the "v" (view) parameter from "list" to "json" and you were in API mode.  We ended up with a lot of bloated controller code with conditionals everywhere checking to see what view (API vs application) the user is viewing. 

Don't Tie Your API To A Specific Technology

Our V1 Search API was backed by two Google Search Appliances (GSA).  In our API, we allowed GSA specific language in our parameter values instead of abstracting it out to a more neutral format.  For example, to search on an asset's metadata, we provided a "m" parameter whose value was a list of filters:

m=meta:assetId=96d1c440-425d-4dfe-af97-cba325ae73b7 AND meta:city=San%2520Diego

We adopted this approach since it closely mirrored the GSA parameter format and we could just pass the value on through in our query to the GSA. This format was difficult to understand and required double URL encoding of some of the filter values and not of others.  What seemed like a convenience at first turned out to be a bit of a headache.  Another potential problem arises if you happen to switch your backend technology.  Now we had to create code to parse this value, translate it to something our new backend understood, and hoped the new backend supported all of the features.  

Be Consistent

One of the first complaints that we got from our V1 Search API consumers was that several of our fields were inconsistent in the data type that they returned.  If there was one value for the field, it would return just that value.  If there were more than one, it would return an array of values.  This made our clients do a lot of data type checking in their API clients which was unnecessary.  


We provided two views in our V1 Search API - xml and json - which users could switch on based on a parameter value.  We first built the xml view (remember when xml was the future?) using a view template. Later on, we added the JSON view using Google's Gson library and spat out the resulting serialization.  Of course, this lead to inconsistent field names and value data types between the two views. In the rare occurrence when a single client needs to consume both views, these inconsistencies can lead to problems or at least lots of conditional logic in the code.


    "_results": [
              "meta": {
                   "state": "CA",
                   "channel": "Running,

Over the course of the V1 Search API's lifespan, we've had several developers work on various parts of the code. If I hadn't told you that you could mostly likely figure that out on your own.  The dead giveaway - inconsistent casing in the field names in our output.  camelCase, snake_case, ProperCase, oh my!  This is one of those minor issues that will drive your consumers crazy.  With a consistent case for your field names, it makes it easier to deserialize your output into an object in their clients.  It will also eliminate hard to find bugs in client code due to misspellings of field names.

Eat Your Own Dog Food

The most important lesson in all of this is to use the API you are designing. I don't mean write a simple client to hit a few of your endpoints  If you can, consume the API in one of your production applications.  You will not know your API pain points until you are trying to use it in a real world application.  The mistakes we made in our legacy API could easily have been avoided before we released it to the public. 

Once your API is out there to the public, they will use it in ways you never dreamed of, and that's a good thing normally. However if you've built a fragile API, they'll be finding ways to break it that you never dreamed of as well. With careful planning, you can avoid future headaches and pain points.  Moral of the story - don't make the same mistakes we did!