Netflix, in case you didn't know, is a marvelous place to rent movies and television series on DVD, and watch an increasing percentage online. Over the last ten years, Netflix has accumulated a treasure trove of information about movies and the people who make them, and has recently opened it all up with the Netflix Catalog API. (Full disclosure: yes, I work there, and yes, it's awesome.)
Here's a handy tool to help you get your feet wet without having to worry about OAuth.
You'll need a developer account, from developer.netflix.com. Once you're signed up and logged in, the link to Consumer Key--below, to the left of the first text entry box--should take you right to the spot where you can copy your key and secret.
If you're feeling paranoid, please view source before clicking the Onwards! button. This thing just below that looks like a form really isn't a form, and is not going to submit your information anywhere except the Netflix API. The only reason why I'm using an all-client solution is so you, the developer, can actually try out your very own consumer key and shared secret, without me, the potentially-nefarious third party, ever actually knowing what they are.
http://api.netflix.com/catalog/titles/autocomplete?term=mcdohttp://api.netflix.com/catalog/people?term=Frances&max_results=1http://api.netflix.com/catalog/people/61544http://api.netflix.com/catalog/people?term=Frances&start_index=1&max_results=9http://api.netflix.com/catalog/people/61544/filmography?expand=synopsishttp://api.netflix.com/catalog/titles/movies/493387http://api.netflix.com/catalog/titles/movies/493387?expand=cast,synopsisThese are only a few examples; please see the REST API Reference for detailed information.
http://api.netflix.com/catalog can be copied and pasted right back into the query blank for further exploration.expand parameter to avoid extra calls to the API. Any of the link attributes in the main tree should be expandable; details are in the API docs, which I am currently in the process of refactoring.v=1.5 to any query. Please note that expand behaves differently in version 1.5.No, seriously. This is critical; you need to know what happens when things go wrong. Change your key or secret, or enter an URL that doesn't compute, and make note of the errors you get back.
/catalog/titles/autocomplete is for, folks; it's even been left free of OAuth requirements so people banging on it won't use up your daily quota of API calls.For the morbidly curious, here's how I got client-side OAuth to work in eighty lines of JavaScript. It may be worth looking at if you're a throwback like me who refuses to use a library he doesn't understand. The heavy lifting happens at the bottom of the script, from oAuthEscape on down, with much help from Paul Johnston's HMAC-SHA1 routine.
( function(d) {
var trueName = '';
for (var i = 0; i < 16; i++) {
trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
window[trueName] = {};
var $ = window[trueName];
$.f = function() {
return {
init : function() {
$.v = '';
var t = ['a','b','k','s','u','x','z','q'];
for (var i = 0; i < t.length; i++) {
$[t[i]] = d.getElementById(t[i]);
}
$.b.onclick = function() {
$.f.runQuery();
}
var a = $.x.getElementsByTagName('A');
for (var i = 0; i < a.length; i++) {
a[i].onclick = function() {
$.q.value = this.rel;
}
}
},
runQuery : function() {
if ($.q.value && $.q.value !== $.v) {
$.z.style.display = '';
$.u.style.display = '';
var key = $.k.value;
var secret = $.s.value;
var url = $.q.value;
var request = $.f.oAuthRequest(key, secret, url);
$.u.href = request;
$.z.src = request;
}
},
oAuthEscape : function(r) {
return encodeURIComponent(r).replace("!","%21","g").replace("*","%2A","g").replace("'","%27","g").replace("(","%28","g").replace(")","%29","g");
},
oAuthRequest : function(key, secret, url) {
var timestamp = Math.floor(new Date().getTime()/1000);
var nonce = '';
for (var i = 0; i < 15; i++) {
nonce += String.fromCharCode(Math.floor(Math.random() * 10) + 48);
}
var u = url.split('?')[0];
var q = url.split('?')[1];
if (q) {
q = q.replace(/@/g, '%40').replace(/,/g, '%2C').replace(/:/g, '%3A').replace(/\//g, '%2F');
var a = q.split('&');
} else {
var a = [];
}
a.push('oauth_consumer_key=' + key);
a.push('oauth_nonce=' + nonce);
a.push('oauth_signature_method=HMAC-SHA1');
a.push('oauth_timestamp=' + timestamp);
a.push('oauth_version=1.0');
a.sort();
var theBody = a.join('&');
var theHead = 'GET&' + $.f.oAuthEscape(u) + '&';
var sigBase = theHead + $.f.oAuthEscape(theBody);
var theSig = $.f.b64_hmac_sha1(secret + '&', sigBase);
var signedUrl = u + '?' + theBody + '&oauth_signature=' + $.f.oAuthEscape(theSig);
return (signedUrl);
},
b64_hmac_sha1 : function(k,d,_p,_z){
// heavily optimized and compressed version of http://pajhome.org.uk/crypt/md5/sha1.js
// _p = b64pad, _z = character size; not used here but I left them available just in case
if(!_p){_p='=';}if(!_z){_z=8;}function _f(t,b,c,d){if(t<20){return(b&c)|((~b)&d);}if(t<40){return b^c^d;}if(t<60){return(b&c)|(b&d)|(c&d);}return b^c^d;}function _k(t){return(t<20)?1518500249:(t<40)?1859775393:(t<60)?-1894007588:-899497514;}function _s(x,y){var l=(x&0xFFFF)+(y&0xFFFF),m=(x>>16)+(y>>16)+(l>>16);return(m<<16)|(l&0xFFFF);}function _r(n,c){return(n<<c)|(n>>>(32-c));}function _c(x,l){x[l>>5]|=0x80<<(24-l%32);x[((l+64>>9)<<4)+15]=l;var w=[80],a=1732584193,b=-271733879,c=-1732584194,d=271733878,e=-1009589776;for(var i=0;i<x.length;i+=16){var o=a,p=b,q=c,r=d,s=e;for(var j=0;j<80;j++){if(j<16){w[j]=x[i+j];}else{w[j]=_r(w[j-3]^w[j-8]^w[j-14]^w[j-16],1);}var t=_s(_s(_r(a,5),_f(j,b,c,d)),_s(_s(e,w[j]),_k(j)));e=d;d=c;c=_r(b,30);b=a;a=t;}a=_s(a,o);b=_s(b,p);c=_s(c,q);d=_s(d,r);e=_s(e,s);}return[a,b,c,d,e];}function _b(s){var b=[],m=(1<<_z)-1;for(var i=0;i<s.length*_z;i+=_z){b[i>>5]|=(s.charCodeAt(i/8)&m)<<(32-_z-i%32);}return b;}function _h(k,d){var b=_b(k);if(b.length>16){b=_c(b,k.length*_z);}var p=[16],o=[16];for(var i=0;i<16;i++){p[i]=b[i]^0x36363636;o[i]=b[i]^0x5C5C5C5C;}var h=_c(p.concat(_b(d)),512+d.length*_z);return _c(o.concat(h),512+160);}function _n(b){var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s='';for(var i=0;i<b.length*4;i+=3){var r=(((b[i>>2]>>8*(3-i%4))&0xFF)<<16)|(((b[i+1>>2]>>8*(3-(i+1)%4))&0xFF)<<8)|((b[i+2>>2]>>8*(3-(i+2)%4))&0xFF);for(var j=0;j<4;j++){if(i*8+j*6>b.length*32){s+=_p;}else{s+=t.charAt((r>>6*(3-j))&0x3F);}}}return s;}function _x(k,d){return _n(_h(k,d));}return _x(k,d);
}
};
}();
$.f.init();
})(document);
If you'd like to learn more about the various snakes that bit me while I was figuring it out, please see True OAuth Confessions, or Why My Hand-Rolled Calls All Blew Chunks.
Have fun, please let me know how it goes, and don't forget to read the documentation, at http://developer.netflix.com/. It will be better soon, I promise! :)
Under "Things to Do and Notice" you mention that the OAuth-signed URL button will let me see what OAuth did to my request string. But what I am seeing is the results of the query. Am I missing something?
Thanks,
Brad