RESTful APIs are anything but — Pippy’s v1 API

We had to build an API (Application Programming Interface) for Pippy to allow other developers as well as ourselves to access our data in a more formal & secure way. There’s a lot to consider when building an API, how do you want to authenticate developers? What data should be accessible? Are there people that don’t need to request for permission from either the user or us, like internal entities? And that’s just logistics.

What about the technical considerations? (Hint: There are a lot)

Do we want to build in our preferred web language (PHP), how does that affect performance in situations like bulk upload? Should we allow standard GET requests, or handle via cURL? Do applications have to get authorization from users first, or can we ignore this for v1 — if yes, how should we structure the transaction, and should we tie this to an active session, or allow permanent access once access is granted? Should we use OAuth? Wasn’t lying — there’s a lot to consider.

Here’s a primer on some of the bigger decisions we made:

  • RESTful

Stateless, Cacheable, Layered, Uniform (for the most part). Really all this means is we opted to keep things as simple and familiar as possible for devs, and didn’t opt for using the SOAP protocol.

  • Sticking to PHP
*grabs popcorn*

We built our API using PHP. While Node.JS might have reduced overall latency especially in situations that’d require multiple successive requests — we opted to stick to PHP for now as this is what our most powerful machines actively use, and introducing a heavy instance of Node.JS would decrease overall service latency and create reliability concern. Our API isn’t a core service, so dedicating more resources to running it isn’t favorable (yet).

  • JSON only

This is why we’re only mostly uniform! We didn’t want to invest time into maintaining an XML version of our API, while yes, this is simple enough to do— we think JSON is enough of a standard, and will provide our developers with enough flexibility.

  • Mixture of cURL + Standard GET

When handling write and other heavy operations, especially, we want developers to pass both a Token and a Secret code only accepted via cURL— allowing us to verify the integrity of the request, and not have to deal with abused limits from bad practices in handling Tokens. cURL requests have to be issued on the server side, so it makes little sense to publicize the Token & Secret codes in applications, meaning we likely don’t have too much to worry about by way of angry developers emailing us when someone’s stolen their Token. In simple SELECT type operations that don’t involve writing or sensitive materials, we allow simple GET requests via Token only.

  • Developers create Apps, which are assigned an AppId, Token & Secret

This one is a little more regarding our object model than technical side, but still a pretty important decision. We built a good amount of the API assuming Developers would only create 1 app per account. This was a flawed assumption that forced us to rewrite a lot of our code. Token & Secret are our verification methods for apps, as well as allow us to keep track of API call quotas and rate limits. On a less related note: Developers can login using their Pippy accounts, but are internally differentiated in a separate table.

  • Apps get permanent access once User Authorizes (until revoked)

Session handling across multiple logged in platforms is a HUGE pain. While we could tie app’s access to active sessions for added security, this is probably going to annoy the user more than it is going to keep them safe. We instead opted for apps getting permanent access to User data (until revoked)

  • Apps request an Auth Code, which are used to identify Users and their allowed permissions — OAuth Style

The Auth Code flow isn’t necessary for all APIs, we document this further in our API documentation, but the way we do it right now is:

*takes a deep breath*

Developers must keep track of Auth Codes (a random 15 digit cased alphanumeric string) when making a request, when User A requests for something like their Task History from App B — App B must either HAVE or NOT HAVE an Auth Code for User A’s acceptance for App B to receive their Pippy user data. If App B does NOT HAVE an Auth Code for User A, they must redirect User A to Pippy to Sign In, where User A will be asked whether or not they would like App B to receive access to their Pippy user data — if they authorize App B, Pippy generates an Auth Code and stores this relationship along with the extent of access and sends the generated Auth Code back to App B in the form of a GET callback endpoint on App B (which we also store on app creation). Now App B does indeed HAVE the Auth Code for User A — App B must use the generated Auth Code for User A whenever they want access to User A’s data. The reason for this second step of annoyance is that we don’t know what User A might be referenced as on App B — maybe it’s their Pippy username, but more likely it’s not, as a result the Auth Code helps us identify which user’s data you’re requesting. The worst case if a developer does this poorly, is User A would have to keep logging in everytime App B needs data — not ideal, but good enough for now.

*gasps for air*

Here’s a visualization for those who couldn’t deal with reading that wall of text:

OAuth Flow for App B / User A Interaction with Pippy

In v2 we might look into adding a “Login with Pippy” such that developers don’t have to deal with Authentication through our Auth Code method at all, a whole other monster but may prove to be useful!

  • Wrappers!

We want people to use our APIs, and cURL isn’t super user friendly especially for inexperienced developers. We figure Python & PHP are likely the most used access methods so we’re dedicating a little time to building wrappers for both these platforms. It’s actually really cool! We do all the heavy cURLing (get it?).

7 lines! (well, without the fluff)

Take a look to the left for what a sample (getting an ETA) using the Pippy PHP API Wrapper looks like.

It’s pretty neat.

A total of 7 lines (without the fluff) to get the ETA of a particular task/order from Pippy, assuming you have the User’s Auth Code.

If not, that’s just an additional line ($PIPPY->requestAuthCode();) and a callback endpoint.

  • Free Rate & Quota Limited Plan

While we do want people to use our API, we don’t want them to abuse it. We run a very computationally intensive service with very limited resource, and by creating a way for developers to access our data, we’re taking on additional computational overhead — as a result we have a free-to-use rate & quota limited plan, it’s unlikely anyone will hit these rates/quotas if they’re building a hobby project, we calculated the limits based on what we expect 500 semi-active users using an app per month might look like, using probability, and translated this to what it would mean for us regarding resource allocation. If people start hitting caps, we give them quota errors and ask them to pay a small fee per X calls (depends on API). Apps & Pippy widgets that directly contribute to our bottom line, e.g. “Order Nana’s Cookies using Pippy!” are exempt from quotas.

This isn’t the last version of our API, we’ve got plenty more to add! Stay tuned :-)