How Developers Led SendGrid to Its JSON Driven API Request Payloads

API development at SendGrid is based on a set of principles that focus on improving how developers experience our product. All design decisions are influenced by that experience. We want things to be intuitive and welcoming. Common use cases should be straightforward to achieve, and common problems should be countered with helpful errors. We recently released a new version of our most critical API endpoint, the one that sends email. In order to provide a better developer experience, we changed the format of the API request payload.

JSON vs Form Data For Request Payloads

One format is not inherently better than the other. Like with most tools, the right one depends on the specifics of the job at hand. For non-hierarchical data, such as what you might collect from a simple “contact us” Web form, form data payloads will be straightforward and easy to use. There are no parent/child relationships or arrays of values to deal with.

JSON is better suited for more complex data structures. Sending complex structures can be done with form data, but it is hard to debug and the data is less human readable. From a development perspective, it all comes down to what is going to be easier for the users, and there are tradeoffs to be considered. For example, a multipart form data payload is better at handling binary data and sending file attachments efficiently. However, for our API, we made the decision to move our attachments into JSON based on feedback from our customers. One of the implications of that is the attachment in the payload takes up to 33 percent more bytes than what is stored on disk. Some of our customers were struggling to implement the multipart form data solution, while they were comfortable with the more straightforward JSON and base64 encoding approach. SendGrid’s customers, and our infrastructure team, were willing to trade increased payload size for ease of use.

We are lucky because we have billions of API calls every month and a very rich data set to look at to determine how developers are experiencing things and actually using our API. Based on this data and analysis of things like the number and type of support questions we received and NPS survey feedback, we found that moving to JSON would allow us to address the most common pitfalls and improve the developer experience of our API.

SendGrid’s Endpoint

In the previous version of our API, there was some common functionality that was often used but not easily to accomplish. We had one parameter in the Web form payload that accepted a JSON-encoded string for specifying complex behavior such as a mailmerge. To personalize a message with the recipient's first name, developers had to maintain one array of email addresses and one separate array of first names to be substituted within those emails. If a user had 1000 recipients for a message, they had to make sure there were exactly 1000 first names for that same message. Each of the first names had to correlate with each email addresses based on its index in the array. There was no way to easily confirm that the arrays were equal in length, and it was difficult to see the relationship between the values. Some things, like CC recipients, were further buried in the POST data and couldn’t be inferred from this JSON structure at all.

{
    "to": [
        "anna@example.com",
        "elwood@example.com"
    ],
    "sub": {
        "%fname%": [
            "Anna",
            "Elwood"
        ]
    },
    "unique_args": {
        "customer_id": [
            54,
            55
        ]
    }
}

With only two recipients it’s not too hard to see the relationships between the arrays, but what happens when you have 500?

In the new version we’ve made things more intuitive via what we call personalizations. Personalizations are within the scope of a single message that a user wants to personalize. Within the personalization block, a user can define all the metadata for a particular message: the recipients, the substitution values that will be replaced in that email, custom arguments for tracking, custom headers, and a personalized subject line. It’s easy to see all of the data for a given message, there’s no need to maintain matching indices, and more use cases can be achieved in a single API call.

{
    "personalizations": [{
        "to": [{
            "email": "anna@example.com"
        }],
        "cc": [{
            "email": "bill@example.com"
        }, {
            "email": "connie@example.com"
        }, {
            "email": "doug@example.com"
        }],
        "substitutions": {
            "%fname%": "Anna"
        },
        "custom_args": {
            "customer_id": 54
        }
    }, {
        "to": [{
            "email": "elwood@example.com"
        }],
        "cc": [{
            "email": "frances@example.com"
        }],
        "substitutions": {
            "%fname%": "Elwood"
        },
        "custom_args": {
            "customer_id": 55
        }
    }],
    "from": {
        "email": "brandon@sendgrid.com"
    },
    "subject": "Special Offer!",
    "content": [{
        "type": "text/plain",
        "value": "Hello %fname%!"
    }]
}

SendGrid was one of the first cloud-based email providers and we pioneered enabling SMTP interactions across an API. Moving to a JSON payload has allowed us to fix some of the consistent problems we were hearing from customers and that additionally provided us tremendous value as we reduced our support and operations overhead.

Experience, Experience, Experience

Any design decision has to go back to what the developer is trying to accomplish with their API and what value they are getting out of using it. Be it a JSON or a form data payload, everything has to be rooted in what will actually create a good experience for the developers. You have to test your API and sit down with the developers, watch them use it and listen to the feedback to determine how people are going to interact with the API. Don’t assume you know how developers will experience your product; or if you do, make sure to test and validate your assumptions.
 

Brandon West is Director of Developer Relations at SendGrid.

Comments