Having to include an entire PHP OAuth library every time I want to make a simple API request for some of my own data from a 3rd party app like Twitter really pisses me off. Perhaps this is unreasonable, but it’s a problem I ran into for the 4th or 5th time today when trying to help John O’Nolan fetch his status count for his blog.
Making a REST API request for your own twitter info using the “user/show” request is relatively simple as it doesn’t actually require authentication. That is until you upload the code to your server and discover that someone else sharing the server’s IP is very selfishly hogging all of the API requests.
Twitter very helpfully allows you to authenticate your own app for your account and generate an OAuth Access token, meaning you can skip the first few steps of the OAuth process. However after much Googling, I discovered there really aren’t that many examples of what to do next – they all seem to use one or another library – which seems totally unnecessary for making one request.
After much searching, finally I found one awesome step-by-step tutorial which explains the first few steps of OAuth and in particular how to build the OAuth HTTP headers and OAuth signature to sign the request. So I’ve taken lead from here, borrowed the helper functions and signature code, and put it all together to make a signed GET request to the API and return some ACTUAL data.
Here’s the couple of helper functions I borrowed, the first combines the request URI and parameters into a string as required by the OAuth signature. The second one takes an array of OAuth credentials/settings and combines them into the HTTP header for use with cURL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function buildBaseString($baseURI, $method, $params) { $r = array(); ksort($params); foreach($params as $key=>$value){ $r[] = "$key=" . rawurlencode($value); } return $method."&" . rawurlencode($baseURI) . '&' . rawurlencode(implode('&', $r)); } function buildAuthorizationHeader($oauth) { $r = 'Authorization: OAuth '; $values = array(); foreach($oauth as $key=>$value) $values[] = "$key=\"" . rawurlencode($value) . "\""; $r .= implode(', ', $values); return $r; } |
Next you need to setup all of the OAuth credentials and settings. For Twitter, the tokens, keys and secrets you need will all be available on your App page. If you haven’t got any Apps, you’ll need to create one, authorise it for your account and grab an access token.
1 2 3 4 5 6 7 8 9 10 11 12 13 | $url = "http://api.twitter.com/1/account/totals.json"; $oauth_access_token = "YOUR TOKEN HERE"; $oauth_access_token_secret = "YOUR TOKEN SECRET HERE"; $consumer_key = "YOUR KEY HERE"; $consumer_secret = "YOUR SECRET HERE"; $oauth = array( 'oauth_consumer_key' => $consumer_key, 'oauth_nonce' => time(), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_token' => $oauth_access_token, 'oauth_timestamp' => time(), 'oauth_version' => '1.0'); |
The tricky bit, that doesn’t ever seem to be explained to well, is creating the OAuth signature. For HMAC-SHA1, this boils down to the following few lines:
Create your URI base string by combining the URL you will be sending the request to with the oauth details as per the helper function. The key is the consumer secret and access token secret URL encoded and combined with an ampersand. Finally, the base url and the composite key are hashed and and base 64 encoded. Sounds really fraking complicated when you read the instructions, doesn’t look so bad when it’s written out in PHP.
1 2 3 4 | $base_info = buildBaseString($url, 'GET', $oauth); $composite_key = rawurlencode($consumer_secret) . '&' . rawurlencode($oauth_access_token_secret); $oauth_signature = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true)); $oauth['oauth_signature'] = $oauth_signature; |
To make the request we have to setup a HTTP header and a few other options to pass to cURL. The helper method does the hard work of creating the correct header format with the OAuth settings and then finally, it’s time to make the request with cURL.
1 2 3 4 5 6 7 8 9 10 11 12 13 | $header = array(buildAuthorizationHeader($oauth), 'Expect:'); $options = array( CURLOPT_HTTPHEADER => $header, CURLOPT_HEADER => false, CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false); $feed = curl_init(); curl_setopt_array($feed, $options); $json = curl_exec($feed); curl_close($feed); $twitter_data = json_decode($json); |
This works really well if you just want to fetch data from your own account – last statuses, mentions, favourites or your stats like in the example above. It should also work with any OAuth based API which supports HMAC-SHA1 signatures (Google, LinkedIn, Vimeo, the list goes on). If the API you’re working with doesn’t have a neat tool for getting an access token for your account like Twitter does, you can always do the first few authentication steps by using an online client. Fill out your details and it will handle the first few exchanges, and once you have your access token you’re free to make requests using just the 50 lines of code above, instead of requiring an OAuth library.
Download the full Twitter OAuth Code Example
Big Kudos to Jason Graves (aka. GodLikeMouse) for putting his excellent PHP OAuth tutorial on the web.




27th Oct
Twitter OAuth: Simple cURL requests for your own data « Http « Php « Codes php – Share your php snippets pinged back:
[...] : http://erisds.co.uk/code/twitter-oauth-simple-curl-requests-for-your-own-data [!] Report this snippet Processing your request, Please [...]
10th Nov
Kasper says:
Great tutorial!
I’ve actually read over 20 tutorials and this one was by far the easiest to use!
Thank you very very much!
10th Nov
Kasper says:
Unfortunatly, for some reason it still doesnt work… The problem is, on my domain (with shared hosting -.- ) I can’t use the regular settings (read: no special settings) to get the user timelines from Twitter, because someone else on the same IP is maxing out the limit of requests. So I read that using OAuth would fix this. The problem is… it doesnt…
Would you know how to fix this?
10th Nov
ErisDS says:
This was exactly the problem I was trying to get around. What request are you using to grab the data?
10th Nov
Kasper says:
Thanks for the fast reply!
And yeah, it is exactly what I’m trying to get around aswell…
I am requesting this url: http://api.twitter.com/statuses/user_timeline.json?screen_name=NS_online
Using your method it works fine here: http://bit.ly/uWaf53
But when I move that to the domain which is having problems, I get Http error 400: http://bit.ly/s9Gm9u
Really weird, right?
10th Nov
Kasper says:
For the rest the code is exactly like yours…
10th Nov
ErisDS says:
Ok so the problem you have is, if you look here:
https://dev.twitter.com/docs/api/1/get/statuses/user_timeline
For that request it says “authentication: supported”. I had this problem too, I have no idea how to force twitter to use your authentication, and what it seems to do is ignore it even if you provide it.
So what you will need to do is either find out how to force that request to use authentication, or use a different api request which has “authentication:yes” (there are many that do similar things)
10th Nov
ErisDS says:
Additional note. Try not specifying the screen name at all.. that might trick it into being an authenticated request – i.e. return the authenticated user’s details rather than the one you specify.
10th Nov
Kasper says:
Hmmm, I already suspected something like that… Too bad nobody responds to the Twitter Dev forum… (except you)
10th Nov
Kasper says:
At the moment I’ve partially fixed the problem, in my case atleast: I’ve now used the Search API, searching for all tweets from a certain user.
However, this comes with a big downside: using the Search API, I can no longer see what the user replied to in his message…
11th Nov
Kasper says:
The whole problem is solved now, using the (extremely simple) tmhOAuth library (mentioned here: https://dev.twitter.com/discussions/3564 ) which you can get here: https://github.com/themattharris/tmhOAuth/
The reason why your method doesnt work, is because (as far as I understand) there is no signature specified. There is a signature method, but not signature.
21st Mar
kermit says:
Great! after a day spent on twitter oauth I’ve found this page.. and all problem with that f** oatuh signature was solved!
thanks.
21st Mar
API Twitter e Oauth | Zenworks pinged back:
[...] http://erisds.co.uk/code/twitter-oauth-simple-curl-requests-for-your-own-data [...]
23rd Apr
Boris Rarden says:
How would I modify this script to submit a status update to my own twitter account ?
23rd Apr
benoit says:
Thanks a lot for this tutorial : it works great for GET requests without any parameters but as soon as I add one, I get an error
Let’s say the URL is
I get
2
3
4
error: "Could not authenticate with OAuth.",
request: "/1/statuses/home_timeline.json?count=5"
}
whereas it works fine when I just use
How do you add GET parameters without messing up the authentication ?
10th May
Twitter API Made Easy | Blogrescue.com pinged back:
[...] on NestWork, I needed to send Twitter API requests that were authenticated for different users. This excellent post got me about 95% of the way there. The two missing pieces [...]
20th May
Saket says:
Dear ErisDS,
Thank you for an extremely informative post! I have struggled with OAuth, and your solution has helped me make GET requests to verify credentials, etc successfully.
However, I am trying to follow people on behalf of users (needs POST), but am getting the following error from Twitter:
I used the following tweaks to your code. What am I doing wrong?
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$oauth_access_token = "user's access token, stored earlier";
$oauth_access_token_secret = "user's token secret, stored earlier ";
$consumer_key = "My Application consumer key";
$consumer_secret = "My Application secret key";
$oauth = array( 'oauth_consumer_key' => $consumer_key,
'oauth_nonce' => time(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_token' => $oauth_access_token,
'oauth_timestamp' => time(),
'oauth_version' => '1.0');
$oauthbase = array( 'oauth_consumer_key' => $consumer_key,
'oauth_nonce' => time(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_token' => $oauth_access_token,
'oauth_timestamp' => time(),
'oauth_version' => '1.0');
$base_info = buildBaseString($url, 'POST', $oauth); //Changed to POST
$composite_key = rawurlencode($consumer_secret) . '&' . rawurlencode($oauth_access_token_secret);
$oauth_signature = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true));
$oauth['oauth_signature'] = $oauth_signature;
$header = array(buildAuthorizationHeader($oauth), 'Expect:');
$options = array( CURLOPT_HTTPHEADER => $header,
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_POST => true, //POST
CURLOPT_POSTFIELDS => 'screen_name=wisebuddha', //POST
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false);
$feed = curl_init();
curl_setopt_array($feed, $options);
$json = curl_exec($feed);
curl_close($feed);
Getting an oauth error when I’m trying to ‘post’ makes me want to pull my last hairs out! Any pointers would be greatly appreciated!
23rd May
G says:
Thanks! Spent ages trying to figure this out simply, everything I’ve found has involved multiple include files. You’re a star :)
21st Jun
Alain says:
I have currently use your tutorial to help me with the twitter thing, but it doesn’t work.
Result : Failed to validate oauth signature and token
And i’m not too familiar with that cURL thing.
13th Jul
Rense says:
This has made me really happy…
I hope it’s okay, I used some of this code to create an oauth class: http://code.google.com/p/simple-php-oauth/
29th Jul
Johan says:
Dude! That was just what I needed. The way you used array was brilliant. Had never found a use case of strings being manipulated as arrays. I got everything working after about 3 days but couldn’t have done it without this tutorial!
1st Aug
Rob says:
I just need a little clarification. When you register the app with twitter, you need to specify a callback page. What would I put in there? Seems like (but I’m far from an expert with php) this php script does it all, so the callback would be twitter-oauth.php, but I’m probably wrong. Help?
17th Aug
Paul Salvette says:
This is a very helpful tutorial. Thank you for putting it all together. This oAuth stuff is a bit beyond my level of intelligence and I couldn’t have done it without you.
We are a small business trying to set up a script for our two staff to send a few precomposed tweets out when we’re not at work via a cron job. If you send out POST data (in our case, the “status=some tweet”) as part of the cURL request, you need to include that in the buildBaseString function. This is because the oauth_signature takes that extra POST data into account, in addition to the oauth parameters.
We included the following paramaters in the cURL request and it seems to be working:
curl_setopt($feed, CURLOPT_POST, true);
curl_setopt($feed, CURLOPT_POSTFIELDS, $fixedpostparams);
where $fixedpostparams is a url encoded value. Thanks, again.
31st Aug
Donovant says:
This code for Vimeo requests not work….
Does someone know why?
10th Oct
Chris says:
Thanks so much for this post!
I wonder if you could help me… I’m having trouble getting this to work when the url has a query string (e.g. https://api.twitter.com/1.1/statuses/user_timeline.json?count=5). It comes up with error code 32: “Could not authenticate you”.
I’ve tried everything I can think of to fix it. It works fine for the same url without the query string. Am I missing something obvious here?
10th Oct
Chris says:
I figured it out!
For anyone else with the same question: the authentication fails if you simply add a query string to $url because the base string uses an unencoded ‘&’ before the parameters. Putting a ‘?’ in the url messes that up.
Switch the second block of code in the main article to this:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$query = array( 'count' = 5 ); # leave an empty array if you don't need a query string
$oauth_access_token = "YOUR TOKEN HERE";
$oauth_access_token_secret = "YOUR TOKEN SECRET HERE";
$consumer_key = "YOUR KEY HERE";
$consumer_secret = "YOUR SECRET HERE";
$oauth = array( 'oauth_consumer_key' => $consumer_key,
'oauth_nonce' => time(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_token' => $oauth_access_token,
'oauth_timestamp' => time(),
'oauth_version' => '1.0');
$base_params = empty($query) ? $oauth : array_merge($query,$oauth);
$url = empty($query) ? $url : $url . "?" . http_build_query($query);
And the first line of the third block to this:
That works for me, anyway!
7th Nov
Al says:
Hey Chris. I tried your solution but I still get the same error. Could you send or post your full source?
10th Jan
Lars says:
You made my day. This was exactly what I was looking for. Thanks!
16th Jan
andrew says:
I just found this via a google search and copied it and it worked with no modification (except the obvious). Thanks a lot for posting.