August 15th 2012
Disclosure: I recently worked for Google for about a year. It was alright.
Recently, Vic Gundotra of Google+ fame made a bold statement. He proclaimed that the lack of write API access to Google+ is born not out of lack of foresight, planning, or even bandwidth, but out of trepidation, caution, and the desire to do right by developers.
This is raw, barely-refined bullshit and I regret not being able to respond to the thread with a pithier snarky comment. The truth is simpler: Google is full-stop terrible at APIs.
Don’t look at me, it says so right there
As a case study, let’s examine what I had to do in order to use the most powerful collection of user-generated content that Google makes available to developers: a user’s email contact list. People who work for consumer- facing web companies probably know how deeply important having a user’s email address is for marketing. Despite being invented before I was born, email has yet to be outmoded as the most effective way to push content to users on demand.
Recently here at Everlane, we thought it might be a good idea to have a button a user could hit to view a list of their Gmail contacts with portraits, select some of them, and then have us send those users an invitation email. Simple, obvious stuff to want to do, right?
What we want to implement
That’s fine – we’re pretty decent engineers and can call the Contacts API on our own. We register our application with the API Console and start reading about OAuth. Google provides a few authorization schemes. We probably want the one titled “Client-side Applications”, which saves us the complexity of an application server having to be aware of any sensitive information.
Now we can finally ask the Contacts API for a list of contacts! Easy enough, right? We’ll just do a JSONP request to get around the cross-domain restrictions.
What does this give us? To our slow and creeping horror, we get back something like this for every contact:
You take several deep breaths. You ignore the fact that you are fetching roughly 1kb of data per contact (out of potentially thousands) to get a name, an email, and the URL of an image. “Okay”, you think to yourself, “this is still salvageable. I can parse XML on the client. In fact, jQuery can probably do it for me.” You take a quick stab at grabbing names and emails.
A quick browser test shows that this only appears to work in Chrome. A bit more
digging turns up, to your chagrin, that jQuery has trouble finding namespaced
elements like “
For now, you ignore how inefficient this is, hoping merely to reach functionality. It works! Now you want to add images. It looks like one of the link elements under <entry> appears to point to an image for that contact. You fiddle around on the console:
Attempting to load this in your browser gives you a 401. Taking a look a the photo management section of the docs seems to suggest you need to additionally apply auth credentials to this url. You amend your code:
This seems to produce a real photo in your browser. Success! Let’s extrapolate:
Unfortunately, only a few images load. What’s going on? You squint at the docs more closely.
Note: If a contact does not have a photo, then the photo link element has no gd:etag attribute.
Great, so some image links have a magic attribute on them that says they’re real images. You wonder why Google even bothers returning image links for photos that don’t exist. You try something like the following:
But no, jQuery can’t deal with namespaced attribute selection, at all, so you arrive at:
This time, a few more photos load, but then they stop coming. The console shows a few successful image loads, but most of the requests for images returned with 503 (Service Unavailable) errors. You realize, after an hour or so, that each image load is being counted as an API call against you, and that there must be some rate limiting in place.
Naturally, this fact is completely undocumented. Playing around with the code, you find that Google doesn’t like it if you have more than one in-flight API request at a time. You come up with essentially the opposite of an image preloader to stagger image loading:
Whew, that was fun, right? At this point it seems like a good idea to try to sort contacts by some sort of relevance metric. Unfortunately, the Contacts API doesn’t support this at all. Oh well. You give up, having reached something approximating your original goals.
What have we learned?
- Google doesn’t get JSON.
- Google can’t design clean APIs or document them well.
- Vic Gundotra is soooooo lying.
Developers waiting for Google+ to deliver on its full, API-wonderland potential: you should probably just give up. What are the odds that this whole time, they’ve been cooking up the perfect write API, replete with features, libraries, and documentation? I’m betting that they’re doing exactly what I did when I worked for Google: absolutely nothing of importance.
Lastly, we’re always on the lookout for talented developers who are interested in the fashion space here at Everlane. You don’t need to own more than one pair of shoes. My love of fashion is born of William Gibson novels and is almost entirely academic. If you’re interested, check out our jobs page or send me an email at email@example.com.
P.S. Yes, I am aware there is an older gdata library that can potentially handle contacts. I might have used it if it wasn’t deprecated or Google had made any mention of it whatsoever on their Contacts API page.