Bad API design — studying confusing AppsFlyer Server-to-server API

Alexey Strelkov
The Startup
Published in
5 min readFeb 10, 2020

If you want to learn how not to design APIs, you can just look at the AppsFlyer’s server-to-server events API. The purpose of this API is to enable developers to send additional events about user behavior after they installed your app.

Analyzing the API

Okay, let’s see what we are expected to do to send en event. Documentation opens as follows:


Looks simple enough — format the URL with your application id and send some events parameters as a JSON. Let’s skip to the parameters section to start coding.

Well, that is already weird. Look at the screenshot above, can you already see what’s bad here?

The first thing you’ll probably notice is that that some parameter names are in CamelCase , while others are in snake_case . Naming variables using different naming styles is not aesthetic and may lead to human errors. The right way would be choosing just one naming convention and sticking to it throughout the API.

The second thing is that this table lacks data types. So, are all of these parameters supposed to be strings? Well, I guess they could be, but that’s not obvious at all. Always specify data types.

Oh, wait, the table says that af_events_api must always be set to true , so I guess not everything is a string, since we have at least one boolean.

That also leads to another question — why exactly af_events_api must be set to true all the time? What happens if I set it to false or omit it? Well, spoiler alert: the API call will return an Exception. So, why even include this parameter if it must always be true ? What is design decision led to this? I have no idea, this is ridiculous.

Some of the parameters are marked as mandatory (no questions about it), some as optional (same here) and some as recommended. Now, what exactly is “recommended”? I guess it’s the same as optional? So why not just mark them as optional? I mean, if something breaks if these parameters are not set, they should be marked as “Mandatory”. If they are optional but somehow the precision of the service is enhanced when they are set, then it should be clearly stated.

Anyway, since we don’t know the data types, let’s just look at the code examples.

Here goes one:

Wait, what? af_events_api is a string saying "true"? Why would they stringify a boolean value? If it’s a boolean let it be a boolean. These kind of unintuitive things lead to human errors all the time.

Okay, eventValue is supposed to be a JSON, but… not just a JSON, a stringified JSON! Why on Earth should it be stringified? There is no good reason for that. It’s not like a JSON dictionary cannot hold another dictionary as one of it’s parameters. It can and it should in situations like this. Again, very unintuitive. Encoding two JSONs instead of one (request body itself and eventValue ) will also have a negative impact on the speed of your app if you have to send lots of events.

Okay-okay-okay, so it’s a stringified JSON. But what parameters should be in it? The API Documentation does not even describe them! From the code sample I know that there are af_revenue which is, I guess, revenue value, af_content_type which I have no idea about and af_content_id which I also have no idea about. What do they stand for? Are all of them mandatory or not? Why should I guess? This all should be described in the first table with parameters.

So we scroll the documentation and find another example of code:

Seems like there is also af_quantity — AppsFlyer, do you really want me to guess which parameters can be passed as eventValue ?

Scrolling even further we can observe the most hilarious thing:

Did you get it? AppsFlyers API is so confusing, that even the creator of this documentation managed to confuse eventValue with event_value .

And, by the way, if the value is empty it should be set to either null or omitted in the request instead of setting to an empty string which just confuses things more.

Then we scroll a little more and see this:

Wait, you can specify the event time? Then why the parameters table didn’t specify eventTime alongside other parameters?


You’ve seen a real-life example of bad API design. Here are all the points you need to remember in order to design better APIs:

  1. Be consistent with naming. JSON is supposed to use camelCase naming convention. Use it. Use it with all parameters.
  2. Clearly outline all of the parameters that can be used and how can you pass them (query params or request body — also don’t forget to mention the accepted the content-types)
  3. Clearly mark all required parameters and optional parameters as such.
  4. Specify data types for all of the parameters.
  5. Use the appropriate data types for your parameters. If it’s an integer, let it be an integer. If it’s a boolean then it should be boolean.
  6. If the parameter is a dictionary, describe all of the parameters that it can specify alongside with datatypes and if they are required or not.
  7. Consider removing parameters that are not needed. If the parameter must be always set to the same value, remove it.
  8. If a parameter is optional, let the API users omit it.
  9. If a parameter can be set to “nothing”, let the API users set it to null , don’t use empty strings to represent no data.