I have been a Netflix customer for several years now and I just love the service. As a software developer, I was quite excited when I heard that they had opened up a Web Service API to access their service. Wasting no time, I headed over to http://developer.netflix.com to grab a piece of the action. However, my enthusiasm quickly waned as I started plowing through their documentation. Initially, I came across terms that were quite familiar: REST, XML, etc. But soon, I was confronted with some fundamental problems, such as:
- The documentation is quite extensive, but is only available in an online format. It would have been really handy to download a PDF to print out and browse offline at my leisure.
- They use some peculiar formatting that looks great in a browser, but when I printed it, pretty much all of the diagrams were lost or chopped to bits.
- The details are sometimes way oversimplified, or sometimes quite obtuse.
Through diligent effort searching for better explanations for their terminology, and any code that might demystify things for me, I finally was able to assemble some code the demonstrates the Netflix APIs - and it turned to be quite simple in the end. This is the first of a series of articles that describe the fundamentals of the Netflix APIs and demonstrate how .NET applications can access the Netflix services programmatically.
One last word about the Netflix documentation before we proceed. Please don't get me wrong. I don't mean to sound harsh about the documentation they produced. It's enough work just publishing a reliable and well designed API set, much less producing quality documentation. I give my utmost kudos to the Netflix team for the work they've done, and hope that my additional clarifications will be of benefit to everyone. Furthermore, my first encounter with the documentation was right after the API was released, so it's understandable that it was a bit rough. It has indeed improved subsequently; however, there are still areas remaining that these articles can help clarify.
And finally, you might want to skip ahead as you're reading to the Glossary of Terms at the end of this article. It lists some of the terminology that I'm using and how it relates to the terminology in the Netflix documentation.
This article will introduce you to the technologies used by the Netflix APIs:
I realize that many of you CodeProject readers do not have access to the Netflix service. I hope that you will find the technical information in these articles of merit even if the practical application of the example code is of lesser benefit.
The first step in developing a Netflix client application is signing up for a developer account. There is a link on the Netflix Developer Network home page that leads to a form where you can sign up for a developer account. This form requires you to enter some basic information about yourself, including a name and password you would like to use for this account. After completing this form, click Register, and you will be presented with a confirmation page that informs you that you will be receiving an e-mail message with further instructions.
The e-mail message contains a link that takes you back to the Netflix account management site to confirm your developer account request. You will find a link on this page that will take you to another form where you register your application.
Note: You are only allowed to register one application with each developer account. If you wish to develop multiple applications, you must create a separate account for each application.
On the application registration form, you will fill out the basic information about your application. The most important field on this form is, "How did you hear about this API?". You must, of course, answer "Codeproject.com"! When completed, click Register Application and you will arrive at a page that confirms your application registration.
The Application Registration page contains several items of information, the most important of which are:
- Key - This is generally referred to as the "Consumer Key" and represents your application's identity when making Netflix API requests.
- Shared Secret - Also referred to as the "Consumer Secret", this is a secret token that is used when digitally signing your Netflix service requests. Without this token, no one can falsify a service request claiming to be you.
You may wish to print this page, or copy and paste the information into a file, for later reference. But don't worry, Netflix also sends you an e-mail message containing your consumer key and shared secret for archiving in your records.
Now that you have your application credentials, you can run a quick test right away to verify that they are valid. There are three types of requests you can make:
- Non-Authenticated Requests - These require nothing more than your consumer key.
- Signed Requests - These require a digital signature, which will be described in this article.
- Protected Requests- These are requests that access a subscriber's account, and will be described in a subsequent article.
A non-authenticated request can be constructed in a simple URL sent from a Web browser. This request requires only your Consumer Key and does not need signing. Per the Netflix API documentation, the only non-authenticated request currently supported is the auto-complete catalog search. The following example demonstrates this type of search, where YourConsumerKey is the consumer key you received for your application when you created a developer account.
You can copy and paste the preceding request example into your favorite browser, insert your own consumer key, and the results returned should be a list of movie titles associated with the term "Casper".
<title short="Casper Van Dien"></title>
<title short="Casper the Friendly Ghost: Casper's Birthday"></title>
<title short="Casper Meets Wendy"></title>
<title short="Casper: Scare School"></title>
<title short="Casper: A Spirited Beginning"></title>
<title short="Casper's Spookiest Tales"></title>
<title short="Casper: Trick or Treat"></title>
<title short="Casper & Wendy's Ghostly Adventures"></title>
<title short="Best of Casper: Vol. 1"></title>
Signed requests are the basis of this article, and are described in detail in the following sections. With signed requests, you can access any of the Catalog Resources in the Netflix API set, such as:
- Search for a catalog title (movie, television series, etc.).
- Obtain details for a given catalog title.
- Search for people (actors, directors, etc.).
Protected requests access subscriber account information, which requires an additional level of security. This will be described in a subsequent article in this series.
As I mentioned earlier, I was quite mystified by the Netflix documentation's explanation of the fundamentals of accessing the APIs, as described in their Authentication Overview topics. They tried, in my opinion, to weave too much technical information together in one big blob, which resulted in my spending more time trying to decipher their documentation conventions than actually understanding the application of the various technologies. I will not attempt to rewrite the Netflix API documentation in its entirety here, but rather to paraphrase their introductory overview in what, I hope, is a more logical and comprehensible manner.
The Netflix API is base on REST, which stands for Representational State Transfer. Having been a SOAP (a.k.a., Web Services) developer for some time, I occasionally find myself having to describe REST to other SOAP developers. My short answer is that REST is a Web Services protocol where you simply put all of the information about the request into the HTTP request URI and query string parameters. I don't want to dwell on the differences between SOAP and REST in this article, but this comparison of the two is a brief way of describing how REST works. For those of you who are familiar with SOAP, or simply as a way of describing REST, here's my version of how the two protocols compare:
|Action is contained in the SOAP header (WS-Transfer).
||Action is specified by the HTTP method (GET, POST, PUT, DELETE).
|Routing information is contained in the SOAP header (WS-Transfer).
||Routing information is appended to the base URI.
|Parameters are passed in the body.
||Parameters are passed in the URI and query string. Because of this, the parameters must be URL encoded (also referred to as percent-encoding by those who may be conceptually challenged).
|Errors are returned as SOAP faults.
||Errors are returned as HTTP error codes. If you're really lucky, you might get a bit of detail about why the request was rejected, but usually, you get just an error code.
|Security (authentication, message integrity, etc.), if implemented, is contained in the SOAP header (WS-Security).
||With the Netflix APIs, security is implemented through OAuth.
Briefly, OAuth provides a means by which services may be requested in a secure manner. There are two aspects to this security:
- Authentication - Providing information with the request that identifies you, and confirms that you are who you claim to be.
- Integrity - Providing a digital signature with the request that indicates if the request has been tampered with while it was enroute to the service provider.
Let's get right down to business and look at a REST service request using OAuth. First of all, let's look at the individual parameters for an example request:
HTTP Method: GET
Request URL: http://api.netflix.com/catalog/people
Query String: max_results=10
Note that the query string is wrapped in this example for clarity, and that YourConsumerKey is the key for your Netflix developer account. So what does all this mean?
- The first thing you need to make a service request is which HTTP method will be used (GET, POST, and so on).
- The next item is the URL, formally called the "service endpoint", to which the request will be directed.
- Lastly, a list of parameters that provides details about what information you wish to obtain, and some additional OAuth security parameters. Note that the query parameters are specified in alphabetical order, formally known as canonicalization. The important thing to observe here is that the two application-provided request parameters are
term - everything else is for the OAuth security. Because of the canonicalization, the
term parameters wind up at the beginning and end of the query string, respectively.
Now, let's take a look at the OAuth parameters individually.
|This is the consumer key that was issued to you for your application. This is what identifies your application as an authorized Netflix service consumer.
A nonce is simply a unique identifier, somewhat like a GUID, that can be considered as having multiple purposes:
- It represents a unique value for each message so that any given message cannot be resent, thus preventing "replay" attacks.
- It can also represent a message identifier, somewhat equivalent to the
<MessageID> in a SOAP WS-Addressing header. If Netflix supported asynchronous service requests, and included the nonce in the service response, this identifier could be used to correlate responses with their original requests.
|Describes the hash method used to generate the digital signature (to be described shortly). This is always HMAC-SHA1 for Netflix API requests.
|Indicates the date and time at which this request was created. This timestamp also prevents replay attacks, as previously described. Per the OAuth specification, the "timestamp is expressed in the number of seconds since January 1, 1970 00:00:00 GMT". In simple terms, it's the good old UNIX time format.
|This is always version 1.0 for Netflix API requests.
Now that we have the REST request and the OAuth security parameters prepared for the HTTP query string, all that's left is to add a digital signature and we're ready to create our Web request.
The signature is, as previously mentioned, a simple HMAC-SHA1 hash, which is readily available in the .NET
System.Security.Cryptography namespace. The signature is generated by concatenating the following elements from the request into what the Netflix documentation calls a "base string", which is then fed to an instance of an HMAC-SHA1 hash generator class.
- The HTTP method.
- The base URL.
- The query string parameter list.
- The "Consumer Key" and "Consumer Secret" that were provided to you when you created your developer account.
Using the request example from the beginning of this section, this is the equivalent base string:
Observe in this example that the base string is constructed by:
- URL encoding each of the three parameters of the request as previously described.
- Concatenating them into a single string in a specified order: HTTP method, URL, and query string (canonicalization again).
The signature (using HMAC-SHA1) is produced as follows:
- The HMAC-SHA1 key is set to the URL-encoded Consumer Secret.
- A "signature base" string is constructed by concatenating:
- The HTTP method ("GET", "POST", "PUT", "DELETE").
- The "normalized" URL, URL encoded. A normalized URL is one that is fully qualified, including the port designation (":80" for HTTP, ":443" for HTTPS).
- The "normalized" request parameters. This consists of all the query string parameters, sorted alphabetically and URL encoded.
- The signature results from feeding the signature base to the HMAC-SHA1 hashing function.
The resulting HMAC-SHA1 signature value is then Base64-encoded and appended to the previously constructed URI and query string that comprises the service request.
After all of this preparation, you are ready at long last to send off your request, which is now quite simple. The request is sent over HTTP using a GET method, to the URL representing the service endpoint of interest, and the query parameters and digital signature are appended to the request. If everything is constructed correctly, you will receive an XML document in return that contains the information you requested.
The example code accompanying this article is a simple application that demonstrates:
OAuth class that handles all of the details of generating a properly formed OAuth request with a digital signature.
- Making a synchronous HTTP request and receiving the results.
During my research on OAuth, I was fortunate to stumble across a nice bit of C# code that encapsulates all of the effort required for creating a properly formed OAuth request written by Eran Sandler (see the comments in the code for additional details). There are several public methods in this module, and I also added some tweaks of my own, and ultimately I found the following method to be the most appropriate for my purposes:
public string GenerateSignature(
Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters)
The following code snippet demonstrates how the
OAuth class is used to create a signed request:
OAuth.OAuthBase oauth = new OAuth.OAuthBase();
Uri requestUrl = new Uri("http://api.netflix.com/catalog/titles/");
string sig = oauth.GenerateSignature(requestUrl,
null, null, "GET", oauth.GenerateTimeStamp(), oauth.GenerateNonce(),
out normalizedUrl, out normalizedRequestParameters);
string reqUrl = requestUrl + "?" +
Note that the
OAuthBase class takes care of all the URL encoding required for the query string parameters when it constructs the
normalizedRequestParameters output parameter. However, the signature does require URL encoding before it is appended to the rest (ha ha, no pun intended) of the request.
Also note that I'm using the
UrlEncode function provided by the
OAuthBase class. This is very important! You may be tempted to use the more familiar URL encoder provided by the .NET
HttpHelper.UrlEncode method; however, it returns the encoded characters in lowercase. Netflix requires the URL encoded characters to be in uppercase, which is what the
OAuthBase class provides. There is at least one tale of woe in the Netflix Developer Forum where this minor detail was overlooked.
Whew! Now all we have to do is to fire off the request and (hopefully) get back a bucketful of data. For this example, I'm using a simple synchronous HTTP request using the
string results = "";
WebRequest req = WebRequest.Create(reqUrl);
WebResponse rsp = req.GetResponse();
StreamReader sr = new StreamReader(rsp.GetResponseStream());
results = sr.ReadToEnd();
catch (Exception ex)
txtRequest.Text ("Request failed: " + ex.Message);
The example code is an application that searches for Netflix titles. It requires three inputs: your consumer key, your consumer secret, and the term for which to search. Optionally, you can specify the maximum number of results to return, or choose zero to return the default of up to 25 items. The constructed REST request and the resulting response from the Netflix API call are displayed when Search is clicked.
The returned data is an XML document that contains information from the search request. It is fairly obvious what everything means; however, you can also find details in the Netflix API documentation if you need help in understanding it. In a subsequent article in this series, I will show you how to parse and use the data contained in this response.
So, there you have it. I hope this article will save you lots of time and confusion in writing your own Netflix client application. In future editions of this series, I'll demonstrate:
- How the Netflix data is structured and how to efficiently parse the requested information.
- How to obtain permission to access a subscriber's account, and how to view an account using Protected Requests.
- How to modify account information, such as making changes to a subscriber's movie queue, using Protected Requests.
I must also point out that the code example in this article, as in the subsequent articles, will be evolving over the course of this series. Please understand that each one of these examples is written to demonstrate a particular aspect of the Netflix API and therefore they are not intended to represent a complete library of the Netflix API functionality.
One last item is that I want to point out some differences in the terminology that I'll be using in these articles and how it relates to the terminology found in the Netflix documentation. I prefer to use some specific, or perhaps more conventional, terminology, and so here is a cross reference for you to keep in mind as you read these articles.
- September 2, 2009 - Original submission
- September 8, 2009 - Updated source code