Easiest Way to Retrieve Twitter Timeline and Hashtags (Twitter OAuth API 1.1)

Written by Kevin Liew on 19 Mar 2013
218,287 Views • Tutorials

Introduction

Twitter terminated its old API, and all of my Twitter tutorials have stopped working! So, here is a follow up to show you how easy is it to retrieve user timeline and hashtag with Twitter REST API 1.1. Of course, I don't just stop there. I integrated my previously written script and modified it to work with Grid-A-Licious plugin to create something that's similar with Pinterest.

Before we start, we need to have all the ingredients ready.

  1. You need to create an application. https://dev.twitter.com/apps.
  2. Once you've done that, you need to get your consumer key, consumer secret, access token and access token secret.
  3. For security reason, Twitter OAuth is done through server side, in this tutorial, we're using PHP. To keep things simple, we are using Twitter Library called CodeBird-PHP.
  4. To make all the tweets we retrieve pretty, we will be using Grid-A-Licious

Once you have all that, we are good to good. This tutorial is a modified version of my previous old Twitter API - Create a Twitter Feed With Hash Tag And Cache Support. However, we won't implement cache control here just to make it easier to understand.

HTML

Alright, first stop. Since all HTML Markup will be generated by jQuery, we only need a div with an id called #jstwitter

<div id="jstwitter"></div>
<div class="item">
{IMG}
    <div class="tweet-wrapper">
        <span class="text">{TEXT}</span>
        <span class="time">
            <a href="{URL}" target="_blank">{AGO}</a>
        </span>
        by 
        <span class="user">{USER}</span>
    </div>
</div>

CSS

I keep CSS really simple, because in the end, I don't think you will be using mine, you probably will style it up according to your website design. So, I spice thing up a little bit. I'm going to create Pinterest style with Grid-A-Licious.

#jstwitter {
    position: relative;
}
#jstwitter .item {
    -webkit-border-radius:5px;
    -moz-border-radius:5px;
    border-radius:5px;
    -webkit-box-shadow:0 0 3px 1px rgba(100,100,100,0.2);
    -moz-box-shadow:0 0 3px 1px rgba(100,100,100,0.2);
    box-shadow:0 0 3px 1px rgba(100,100,100,0.2);
    overflow:hidden;
    background: #fff;
}
#jstwitter .tweet-wrapper {
    padding:10px;
    -webkit-box-sizing:border-box;
    -moz-box-sizing:border-box;
    box-sizing:border-box;
    line-height:16px;
}
#jstwitter .item a {
    text-decoration: none;
    color: #03a8e5;
}
#jstwitter .item img {
    width:100%;
}
#jstwitter .item a:hover {
    text-decoration: underline;
}
#jstwitter .item .text {
    display:block;
}
#jstwitter .item .time, #jstwitter .tweet .user {
    font-style: italic;
    color: #666666;    
}

PHP

I keep it really simple. We're not going to write our own PHP OAuth authentication, we will be using a Twitter Library called CodeBird-PHP. CodeBird makes it ridiculously easy to get authenticated. You may want to read more about it - CodeBird Documentation. As long as you get all the keys, tokens and secrets right, you should have any problems at all.

I have write inline comment in the following PHP script. If you want to add cache control to beat the rate limits in Twitter API. Here is the place you need to modify it.

<?
//We use already made Twitter OAuth library
//https://github.com/mynetx/codebird-php
require_once ('codebird.php');

//Twitter OAuth Settings
$CONSUMER_KEY = '...';
$CONSUMER_SECRET = '...';
$ACCESS_TOKEN = '...';
$ACCESS_TOKEN_SECRET = '...';

//Get authenticated
Codebird::setConsumerKey($CONSUMER_KEY, $CONSUMER_SECRET);
$cb = Codebird::getInstance();
$cb->setToken($ACCESS_TOKEN, $ACCESS_TOKEN_SECRET);

//retrieve posts
$q = $_POST['q'];
$count = $_POST['count'];
$api = $_POST['api'];

//https://dev.twitter.com/docs/api/1.1/get/statuses/user_timeline
//https://dev.twitter.com/docs/api/1.1/get/search/tweets
$params = array(
'screen_name' => $q,
'q' => $q,
'count' => $count
);

//Make the REST call
$data = (array) $cb->$api($params);

//Output result in JSON, getting it ready for jQuery to process
echo json_encode($data);
?>

Javascript / jQuery

Lastly, the Javascript. We called the PHP we just created. Depend on the parameters, I have made it so you can either search for hashtag, or load a user timeline. It send the parameters to the PHP file above, and PHP script get us authenticated, and PHP returns Twitter data in JSON format. The following scripts read the data and parse them in to HTML markup.

For more information about the Twitter Object, you can read it here.

$(function() {		
			
JQTWEET = {
     
    // Set twitter hash/user, number of tweets & id/class to append tweets
    // You need to clear tweet-date.txt before toggle between hash and user
    // for multiple hashtags, you can separate the hashtag with OR, eg:
    // hash: '%23jquery OR %23css'			    
    search: '%23heroes2013', //leave this blank if you want to show user's tweet
    user: 'quenesstestacc', //username
    numTweets: 21, //number of tweets
    appendTo: '#jstwitter',
    useGridalicious: true,
    template: '<div class="item">{IMG}<div class="tweet-wrapper"><span class="text">{TEXT}</span>\
               <span class="time"><a href="{URL}" target="_blank">{AGO}</a></span>\
               by <span class="user">{USER}</span></div></div>',
     
    // core function of jqtweet
    // https://dev.twitter.com/docs/using-search
    loadTweets: function() {

        var request;
         
        // different JSON request {hash|user}
        if (JQTWEET.search) {
          request = {
              q: JQTWEET.search,
              count: JQTWEET.numTweets,
              api: 'search_tweets'
          }
        } else {
          request = {
              q: JQTWEET.user,
              count: JQTWEET.numTweets,
              api: 'statuses_userTimeline'
          }
        }

        $.ajax({
            url: 'grabtweets.php',
            type: 'POST',
            dataType: 'json',
            data: request,
            success: function(data, textStatus, xhr) {
	            
	            if (data.httpstatus == 200) {
	            	if (JQTWEET.search) data = data.statuses;

                var text, name, img;	         
                	                
                try {
                  // append tweets into page
                  for (var i = 0; i < JQTWEET.numTweets; i++) {		
                  
                    img = '';
                    url = 'http://twitter.com/' + data[i].user.screen_name + '/status/' + data[i].id_str;
                    try {
                      if (data[i].entities['media']) {
                        img = '<a href="' + url + '" target="_blank"><img data-src="' + data[i].entities['media'][0].media_url + '" /></a>';
                      }
                    } catch (e) {  
                      //no media
                    }
                  
                    $(JQTWEET.appendTo).append( JQTWEET.template.replace('{TEXT}', JQTWEET.ify.clean(data[i].text) )
                        .replace('{USER}', data[i].user.screen_name)
                        .replace('{IMG}', img)                                
                        .replace('{AGO}', JQTWEET.timeAgo(data[i].created_at) )
                        .replace('{URL}', url )			                            
                        );
                  }
                
                } catch (e) {
                  //item is less than item count
                }
                
	             if (JQTWEET.useGridalicious) {                
	                //run grid-a-licious
			$(JQTWEET.appendTo).gridalicious({
				gutter: 13, 
				width: 200, 
				animate: true
			});	                   
		     }                  
                    
               } else alert('no data returned');
             
            }   
 
        });
 
    }, 
     
         
    /**
      * relative time calculator FROM TWITTER
      * @param {string} twitter date string returned from Twitter API
      * @return {string} relative time like "2 minutes ago"
      */
    timeAgo: function(dateString) {
        var rightNow = new Date();
        var then = new Date(dateString);
         
        if ($.browser.msie) {
            // IE can't parse these crazy Ruby dates
            then = Date.parse(dateString.replace(/( \+)/, ' UTC$1'));
        }
 
        var diff = rightNow - then;
 
        var second = 1000,
        minute = second * 60,
        hour = minute * 60,
        day = hour * 24,
        week = day * 7;
 
        if (isNaN(diff) || diff < 0) {
            return ""; // return blank string if unknown
        }
 
        if (diff < second * 2) {
            // within 2 seconds
            return "right now";
        }
 
        if (diff < minute) {
            return Math.floor(diff / second) + " seconds ago";
        }
 
        if (diff < minute * 2) {
            return "about 1 minute ago";
        }
 
        if (diff < hour) {
            return Math.floor(diff / minute) + " minutes ago";
        }
 
        if (diff < hour * 2) {
            return "about 1 hour ago";
        }
 
        if (diff < day) {
            return  Math.floor(diff / hour) + " hours ago";
        }
 
        if (diff > day && diff < day * 2) {
            return "yesterday";
        }
 
        if (diff < day * 365) {
            return Math.floor(diff / day) + " days ago";
        }
 
        else {
            return "over a year ago";
        }
    }, // timeAgo()
     
     
    /**
      * The Twitalinkahashifyer!
      * http://www.dustindiaz.com/basement/ify.html
      * Eg:
      * ify.clean('your tweet text');
      */
    ify:  {
      link: function(tweet) {
        return tweet.replace(/\b(((https*\:\/\/)|www\.)[^\"\']+?)(([!?,.\)]+)?(\s|$))/g, function(link, m1, m2, m3, m4) {
          var http = m2.match(/w/) ? 'http://' : '';
          return '<a class="twtr-hyperlink" target="_blank" href="' + http + m1 + '">' + ((m1.length > 25) ? m1.substr(0, 24) + '...' : m1) + '</a>' + m4;
        });
      },
 
      at: function(tweet) {
        return tweet.replace(/\B[@ï¼ ]([a-zA-Z0-9_]{1,20})/g, function(m, username) {
          return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/intent/user?screen_name=' + username + '">@' + username + '</a>';
        });
      },
 
      list: function(tweet) {
        return tweet.replace(/\B[@ï¼ ]([a-zA-Z0-9_]{1,20}\/\w+)/g, function(m, userlist) {
          return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/' + userlist + '">@' + userlist + '</a>';
        });
      },
 
      hash: function(tweet) {
        return tweet.replace(/(^|\s+)#(\w+)/gi, function(m, before, hash) {
          return before + '<a target="_blank" class="twtr-hashtag" href="http://twitter.com/search?q=%23' + hash + '">#' + hash + '</a>';
        });
      },
 
      clean: function(tweet) {
        return this.hash(this.at(this.list(this.link(tweet))));
      }
    } // ify
 
     
};		

});

And finally, you run the script like this:

$(function () {
    // start jqtweet!
    JQTWEET.loadTweets();
});

Conclusion

That's it my friend. If you have knowledge in both backend and frontend, web development can be easier. We can reuse a lot of scripts, for example, we use CodeBird, Gridalicious, my previous Twitter scripts. What we did right here, we use it, modify it, and bang, we have a working beautiful twitter feed interface.

I hope you will find this tutorial useful, if you have any question, don't hesitate to drop us a comment. :)

Demo Download
Join the discussion

Comments will be moderated and rel="nofollow" will be added to all links. You can wrap your coding with [code][/code] to make use of built-in syntax highlighter.

185 comments
Faruk Akkan 11 years ago
Wow, this is the 5th twitter integration topic I see but all are fetching links as t.co. I'm using my own short urls and want to show them not t.co. Is there a way to fetch real links (display links, I guess). Please help me with this.
Reply
Ez 11 years ago
Its not working for me...
Reply
Tatters 11 years ago
Hi Kevin, the code is working great and I've added a few extra bits here and there in the jquery, however I can't figure out how to make it read from a text area input on my site so a signed in member can search for anything they want (e.g. #football) instead of hard coding the search criteria into the code. I've looked at the Jquery site but still can't figure it out.

Many Thanks for your help.
Reply
Tatters 11 years ago
Hi Kevin, with reference to the above question.

I added the following to the JQTWEET code
//click the go button
$('#goButton').click( function() {

//get the value of the input
var myHashtag = $('#hashtagSearch').val();

//for testing
alert(myHashtag);

JQTWEET = {

//assign the value of the input to the search
search: myHashtag

//......put the rest of your code in here

}
});


and then added the following to the page where the <div id="jstwitter"></div>

<p>Enter your hashtag</p>
<input id="hashtagSearch" type="text">
<input id="goButton" type="submit" value="go">


apologies for all the questions...I'm just hoping to get this to work.

Many Thanks
Reply
kevin Admin 11 years ago
It should be something like this:

Keep this code of yours

var myHashtag = $('#hashtagSearch').val();


The click event should hook it up like this:

$('#goButton').click( function() {
JQTWEET.loadTweets();
});

Reply
Adam 11 years ago
Hey so where does this live.. Is it in the Html doc or the actual Jquery? Kinda new to the whole web development thing.
 $('#goButton').click( function() {
JQTWEET.loadTweets();
});

Reply
Samuli Viitasaari 11 years ago
It seems your code's copied pretty blatantly to another tutorial (a very badly documented one, if I may add): http://www.ma-no.org/en/content/index_how-to-retrieve-twitter-timeline-and-hashtags-in-php_1793.php

Jeez, I can see why someone would use the code you provided to their own projects (including myself), but publishing a tutorial with the exact same function names etc. and not mentioning sources... that's pretty harsh!

Thanks for yours, the great documentation that comes with it, and especially for the working sample you provided! It helps a lot, being the php/javascript n00b that I am :)
Reply
Kevin Admin 11 years ago
Hi Samuli, it happens :) It's not the first time, and nothing much I can do.
Reply
Jhon 11 years ago
Hi, I wanted to add a "load more" button, but I can figure out how to do so? would you help me out? Thx!
Reply
Adam 11 years ago
Hey jhon look at the code from Tatters. Same concept as what you are looking for just replace ok with LOAD and eliminate the search portion..
Reply
Vipul 11 years ago
Hi, can you please share code to share image on Twitter using Jquery and javascript ? thanks in advance
Reply
MovieTktMan 11 years ago
Hello Kevin & User Community,

I'm an IT Guy with a basic knowledge of HTML and general scripting but I need to have some Twitter feeds based on JSON that used to work when there was still an RSS feed available from Twitter. What's covered in this article is way, way beyond me.

Is there anyone out there who'd like to earn some cash working with me to create the feed sources I need?

Please let me know,
Thanks!
MovieTktMan


Reply
Erik 11 years ago
Hey Kevin,

I have tried your demo file, but it comes nothing in the browser. What should I do?
Reply
Kevin Admin 11 years ago
Hi Erik, did you notice any Javascript error?
Also, you need to fill in all the keys from twitter.
Reply
Erik 11 years ago
Hey Kevin,
I have registered my site to twitter, and get the keys and tokens, and filled to grabtweets.
But, It completely comes nothing, instead the info wrapper title.
Without javascript error. Just the title.
Do you have any idea what went wrong?
Thank you.
Reply
Erik 11 years ago
Hello Kevin,

my apologize, all my faults is i was testing on localhost. When I uploaded to live server, it works like char.

Thanks Kevin.
Reply
Kevin Admin 11 years ago
That's good :)
Reply
Hugo 11 years ago
Hey Kevin how can I search for all the tweets that contain a given hashtag? well, not all the tweets, just like the latest 5.
Reply
Kevin Admin 11 years ago
Hi Hugo, it's mentioned in the tutorial. In javascript section line 7 - 11.


search: '%23heroes2013', //leave this blank if you want to show user's tweet
user: 'quenesstestacc', //username
numTweets: 21, //number of tweets
Reply
mike ilz 11 years ago
doesn't work with jquery 1.10.1 any fix?
Reply
Daniele 11 years ago
Hi,
is there any way to show your favorite tweets instead of your timeline with this plugin?
Reply
kevin Admin 11 years ago
it doesn't do it in this tutorial. If you want to show favorite tweet, you might want to look at codebird doc and change the JS and PHP. If the JSON structure isn't the same, you will have to modify that as well.
Reply
Rob 11 years ago
I am getting a blank screen. No tweets loading up. On a server as well btw.
Reply
Kevin Admin 11 years ago
Any javascript or PHP error?
Reply
heena 9 years ago
I get alert after following all steps -
added details of my app and token ids+secrets

modified js file as shown

After visiting site only getting JS altert "No data returned"
Reply