Google Reader API series
- Part 1 – Programming to the API
- Part 2 – Listing API
- Part 3 – Editing API
Google have never officially released API documentation for Google Reader, so this information is unofficial and subject to change.
When I first started looking around for API docs to interface with Google Reader, this site seemed to be pretty much the only resource available. Unfortunately a lot of the information in that document is outdated, so I set about trying to figure out the API myself, with some success.
To interface with Google reader, you’ll need to collect the following things from their authentication system:
- SID – A session ID, which remains valid until you log out
- Token – Similar to a session ID, but expires quickly. Used to access direct API calls
- Cookie – An ordinary cookie that uses your SID to authenticate your session on API calls
The code samples I’m using will be in C#, but should be easily translatable to any language.
To get an SID, we send a GET request to https://www.google.com/accounts/ClientLogin with arguments service=reader&Email=[your Google username]&Passwd=[your Google password]
This will return some text with key=value pairs for LSID, SID and User. SID is the only part of this we need, so use your string library to grab this information.
Now that we have an SID we need a token. We send a GET request to http://www.google.com/reader/api/0/token with a cookie we generate from the SID. The cookie information is
- Name: SID
- Value: [your SID]
- Path: /
- Domain: .google.com
This request returns just the token text.
So here is the code I wrote for getting SID and token:
using System.IO;
namespace GoogleReader.NET
{
public class GoogleReader
{
private string _sid = null;
private string _token = null;
private Cookie _cookie = null;
private string _username;
private string _password;
public GoogleReader(string username, string password)
{
_username = username;
_password = password;
connect();
}
private bool connect()
{
getToken();
return _token != null;
}
private void getToken()
{
getSid();
_cookie = new Cookie("SID", _sid, "/", ".google.com");
string url = "http://www.google.com/reader/api/0/token";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";
req.CookieContainer = new CookieContainer();
req.CookieContainer.Add(_cookie);
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
using (var stream = response.GetResponseStream())
{
StreamReader r = new StreamReader(stream);
_token = r.ReadToEnd();
}
}
private void getSid()
{
string requestUrl = string.Format
("https://www.google.com/accounts/ClientLogin?service=reader&Email={0}&Passwd={1}",
_username, _password);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl);
req.Method = "GET";
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
using (var stream = response.GetResponseStream())
{
StreamReader r = new StreamReader(stream);
string resp = r.ReadToEnd();
int indexSid = resp.IndexOf("SID=") + 4;
int indexLsid = resp.IndexOf("LSID=");
_sid = resp.Substring(indexSid, indexLsid - 5);
}
}
}
}
From here were can make interface calls (I plan to document the complete set of API calls in the very near future, but for now I’ll give a couple of examples). To add a subscription, make an HTTP POST to http://www.google.com/reader/api/0/subscription/quickadd?client=scroll with POST arguments quickadd=[url of feed]&T=[your token] and don’t forget to include your cookie. I wrote a couple of quick helper methods for making POST and GET calls first:
{
string url = string.Format("{0}?{1}", requestUrl, getArgs);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(_cookie);
try
{
return (HttpWebResponse)request.GetResponse();
}
catch
{
// handle error
return null;
}
}
private HttpWebResponse httpPost(string requestUrl, string postArgs)
{
byte[] buffer = Encoding.GetEncoding(1252).GetBytes(postArgs);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
request.Method = "POST";
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(_cookie);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = buffer.Length;
Stream PostData = request.GetRequestStream();
PostData.Write(buffer, 0, buffer.Length);
PostData.Close();
try
{
return (HttpWebResponse)request.GetResponse();
}
catch
{
//handle error
return null;
}
}
Now for the add subscription code:
{
string data = String.Format("quickadd={0}&T={1}", feedUrl, _token);
string url = "http://www.google.com/reader/api/0/subscription/quickadd?client=scroll";
HttpWebResponse response = httpPost(url, data);
if (response == null) return false;
return true;
}
We can add a label to a feed by POSTING to http://www.google.com/reader/api/0/subscription/edit?client=scroll with arguments a=user/-/label/[new label]&s=feed/[feed url]&ac=edit&T=[token] like so:
{
string data = String.Format
("a=user/-/label/{0}&s=feed/{1}&ac=edit&T={2}", label, feedUrl, _token);
string url = "http://www.google.com/reader/api/0/subscription/edit?client=scroll";
HttpWebResponse response = httpPost(url, data);
if (response == null) return false;
return true;
}
There are of course listing methods for getting feeds. So far I’ve only been able to figure out how to get feed information in JSON format, but if anyone knows how to grab XML data please leave a comment. To list items we send a GET request to http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/reading-list?ck=[current UNIX time] . Of course there are many more otpional arguments for all of these methods, which I will be documenting. Here’s the list code:
{
string url = string.Format
("http://www.google.com/reader/api/0/stream/contents/user/-/" +
"state/com.google/reading-list");
string args = string.Format
("ck={0}", getUnixTimeNow());
HttpWebResponse response = httpGet(url, args);
Stream str = response.GetResponseStream();
StreamReader sr = new StreamReader(str);
string s = sr.ReadToEnd();
// Handle JSON data in s
sr.Close();
}
getUnixTimeNow() is a small helper method I wrote for getting the current Unix time:
{
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
long unixTime = (long)ts.TotalSeconds;
return unixTime;
}
This has been a small taste of what’s to come as I work my way through this API. Stay tuned for more. If you want to get hacking on it yourself, the easiest way is to download Fiddler, a great web debugging tool, and watch the HTTP requests as you use Google Reader.
Part 2 now available.
{ 6 } Comments
Hey, thanks for all of this great information. Are you still able to get your tag editing functions to work? I’ve been working on a reader client and have had a pretty smooth time other than with marking items with tags such as “read” and “starred.” Even though I seem to be replicating the necessary POST data exactly, I always get an “invalid stream name” message. Since there’s so little information out there on the unofficial API, I thought I’d ask for your thoughts. Thanks again!
Great article! Thanks much.
I’ve been trying to rewrite your code in JavaScript, so JSON format is perfect for me.
But I can’t get the getToken to work. BTW, I don’t have to worry about same domain policy.
So I parse the strings from the first request, get the SID, save it to a cookie and then request the token. But this one doesn’t go through. Any thoughts why this may be?
Never mind, I was sending the wrong cookie, should have been
document.cookie = “Name=SID;SID=#{SID};Domain=.google.com;Path=/;Expires=160000000000″;
Nope. I was mistaken. That didn’t help. I sent headers in my previous request, and all subsequent worked for a while, so I assumed cookie worked, while it actually didn’t.
So the question stays.
Hi Vitaly, sorry for my late reply but I’ve been insanely busy at university
If the request is working intermittently, have you tried renewing the token and creating a new cookie? Those tokens will expire in time. If you find a solution I’d be interested to hear it.
Hey, thanks for the more recent write up compared with that Google Code wiki. It definitely helped! I managed to get my own version working in Python.
http://github.com/askedrelic/Google-Reader-API
{ 4 } Trackbacks
[...] Martin Doms : Using the Google Reader API – Part 1 [...]
[...] this is your first foray into using the Google Reader API then you’ll want to read my previous post which describes how to acquire a valid token and cookie, and make calls to the API. Unlike part 1 [...]
[...] Part I I went over how to program for the HTTP POST requests so for now I’ll just go over the API. [...]
[...] no official API for Google Reader yet, but many developers have begun using an unofficial API – which has been documented by developers using reverse-engineering. It was thought that we [...]
Post a Comment