Twitter Migration to v1.1 API. My code breaks. I mend it.

Blueargh! All you wanted to do was grab your own twitter timeline to display on your own website using a bit of PHP to grab the data as JSON. You were even willing to cache it fairly aggressively in order to spare Twitter a load of unneccesary requests. But now they’ve migrated to v1.1 there's no way to get this without authentication! And you don't understand authentication. What’s a poor boy/girl to do?

1. Create an application

In version 1.1, we're requiring applications to authenticate all of their requests with OAuth 1.0a or Application-only authentication.

As they say you need to authenticate now. What I found was that Application-only authentication was enough for what I wanted to do. So the first thing I did was create a new application. I had to sign in to twitter and then go to my apps page. Once there I clicked the "Create a new application" button.

I had to give my app a name, description and website URL so I just filled these in with my website's name, a description of "my website" and the URL ... of my website. I didn't fill in the Callback URL and everything was fine, so I guess for my purposes it wasn't needed.

2. Secret Key

Next I had to grab my Consumer Key and my Consumer Secret, these are needed to move to the next step. They're displayed on your App page (for the app you’ve just created). Twitter helpfully explains on this page what you needed to do with them. Now, this is the point where things get weird. I found that page very hard to understand. I'm no security expert or API expert or auth expert so a lot of the language there was confusing.

I did manage to glean that I had to combine my Consumer Key and Consumer Secret in a single string (with a colon : in between) this is called the Bearer token credentials. Then I had to base64encode that string.

$bearer_token_credentials = $consumer_key.":".$consumer_secret;
$b64_bearer_token_credentials = base64_encode($bearer_token_credentials);
I then had to post this string to the Twitter API at this URL:

$url = 'https://api.twitter.com/oauth2/token';

I used CURL via PHP to do this. It took a bit of trial and error to get the CURL right but here's what I ended up with:

$headers = array(
'Authorization: Basic '.$b64_bearer_token_credentials,
'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_USERAGENT, 'Morganesque');
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'grant_type=client_credentials');
curl_setopt($ch, CURLINFO_HEADER_OUT, 0);
$output =  curl_exec($ch);
$data = json_decode($output);

I'm not entirely sure what all those options are. The $headers are clearly what Twitter states you need to include and the POSTFIELDS option is again stated as a requirement. It took me a while to get all of my ducks in a line here but I'm pretty sure this is cool. Oh... and the user-agent is just the name of the app I created, I don't think it’s necessarily vital.

3. Bearer Token

What I got back from that request was a tiny piece of JSON which had two values.

{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}

It’s the access_token there that’s important. That’s what I need to be able to send more general requests to the Twitter API from now on. Now one thing I'm not entirely sure about here is how long this access_token lasts. I think it lasts a while. Maybe not forever but quite a long time (assuming you don't piss twitter off!) so the above code probably only needs to be run once. Once you’ve got the access_token you're ready to go (I think).

4. Get My Twitter Feed

So finally I could now sent a request to the Twitter API to retrieve my timeline data. As before I needed to create a CURL request.

$url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?count=100&screen_name=morganesque';

And then ... Kablammo!

$tw = curl_init($url);
$headers = array('Authorization: Bearer '.$access_token);
curl_setopt($tw, CURLOPT_HTTPHEADER, $headers);
curl_setopt($tw, CURLOPT_USERAGENT, 'Morganesque');
curl_setopt($tw, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($tw, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($tw, CURLOPT_RETURNTRANSFER, 1);
$output =  curl_exec($tw);
$data = json_decode($output);
var_dump($data);

As before the output is JSON but as far as I could see this was as similar to the output of the V1 API as I needed to make everything else work fine. I haven't tried any other calls to the API and I don't know whether you need to authenticate differently if you're posting to timelines or reading direct messages (I'm guessing you do). But for that basic "grab a few tweets" type of thing which I'm guessing a few people want, and if you're not content to cede all control and use Twitter's suggested embed solution, maybe this will be of help.

5. Cache Yo!

Finally it might be worth sticking those tweets into a file and only going back to the API when that file is a bit old. Again I don't know exactly how many requests Twitter will accept before it starts getting sad about you, but unless you're updating your timeline every second and don't have too many visitors to your site, going back to the API for each and every request probably isn't a good idea.