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

Written by Kevin Liew on 19 Mar 2013
215,304 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
marvin 10 years ago
Thanks for posting this tutorial about retrieving Twitter timeline and hashtag. http://webdesignfan.com/
Reply
Jeroen Lam 10 years ago
Hi Kevin,

Thanks for the great example
1 note in your download file "grabTweets.php" is spelled with a capital T, and "jquery.jstwitter.js" is refering to "grabtweets.php".

Cheers !
Reply
david 10 years ago
seem to be getting an error

Method name must be a string in grabTweets.php on line 33
Reply
Chris 10 years ago
Yes, I had the same error. If you just fix the typo in jquery.jstwitter.js from grabtweets.php to grabTweets.php you'll be up and running.
Reply
david 10 years ago
thanks
Reply
Kevin Liew Admin 10 years ago
Sorry guy, my bad. I have updated the zip file. Thanks Chris.
Reply
stella 9 years ago
Hello, I'm a beginner here.
I still experience the "Method name must be a string in grabTweets.php" error.
I use this url : http://stellamathilda.site88.net/grabtweets.php

I would be very glad for any clue. Thank you
Reply
Tom 10 years ago
I downloaded the files and corrected the typo, but still doesn't work, while the online version is up and running.

What am I missing?
Reply
Aaron 10 years ago
I found that it would only work when uploaded, did you try to upload?
Reply
Aaron 10 years ago
Thanks for this. Your tutorial was great and informative and I managed to get it to work finally. You're a star!
Reply
James 10 years ago
Thanks Kevin. Question, for numtweets I put 51, and I did multiple users like so: from:X OR from:Y OR from:Z and I only get the top 51 tweets from user X. How can I get the top 51 tweets of all user?
Reply
Kevin Admin 10 years ago
The thing is, it returns first 51 results instead of 51 results for each user. I'm not so sure how to go around it, you might have to do 3 different calls. But then, those result will not be mixed together.
Reply
David 9 years ago
I'm trying to do something similar. If I had a group of users in a list, how would i go about getting the latest tweet from each user, and no more?
Reply
dAVID 10 years ago
Hey great tutorial, it works really well for me. Would it be possible to extend this to twitter lists as well as individual users?
Reply
Kevin Admin 10 years ago
Yea, if you removed "search" at line 9:


search: '%23heroes2013', //leave this blank if you want to show user's tweet
user: 'quenesstestacc', //username
numTweets: 21, //number of tweets


Type in the twitter username. It will return a list of tweet for that particular user.
Reply
dAVID 10 years ago
Thanks Kevin, but what I meant was to display tweets from a twitter list, such as this one: https://twitter.com/13News/norfolk

Could I just replace the "search" option with "list"?
Reply
Kevin Admin 10 years ago
oh! I see. I played with for a while. First of all, you need to find the ID of the list. There is a easy way to do it, you need to have firebug or web developer tool from chrome.

Go the twitter, and to the list you want. Use the inspector to inspect the "Subscribe" button. In this case, 13News/norfolk's id is 66003619

Then, in the Javascript, empty "search", type in "13News" in the user. Then, modify JS to the following:


if (JQTWEET.search) {
request = {
q: JQTWEET.search,
count: JQTWEET.numTweets,
api: 'search_tweets'
}
} else {
request = {
q: JQTWEET.user,
count: JQTWEET.numTweets,
list_id: '66003619',
api: 'lists_statuses'
}
}


and then, PHP code


$q = $_POST['q'];
$count = $_POST['count'];
$api = $_POST['api'];
$list_id = $_POST['list_id'];

$params = array(
'owner_screen_name' => $q,
'q' => $q,
'count' => $count,
'list_id' => $list_id
);


It should return the twitter list you want now. :)
Reply
David 10 years ago
Awesome, thanks Kevin, that did the trick
Reply
James 10 years ago
Thanks Kevin. I decided to solve it a different way. Another question: How can I add a loading image in the script? In some instances it takes about 12 seconds for the tweets to load.
Reply
Kevin Admin 10 years ago
If you look at the script, line 38, where the AJAX call is. You can add another option:


$.ajax({
......
beforeSend: function(){
//do something here, eg display loading animation
},
......
});
Reply
Renton 10 years ago
Hi kevin. It is a really nice tutorial. I am wondering how should i make it refresh ajax call after a period of time?
Thanks.
Reply
Jonathan Kempf 10 years ago
Thanks for the tutorial, although I must say that I couldn't get it to work. I was using the basic twitter user_timeline feed for my personal website, and I wanted to parse out the urls, mentions, and hashtags so I figured I would give this a shot.

However, being inexperienced in js and PHP, I got lost when you started requiring gridaLiscious in the code, as I already have a styling and place for the tweets to go to. Is there an easy way to just get the urls and mentions to work in an existing user_timeline, or do I have to go this route and then start peeling back functionality to get a basic feed?
Reply
Kevin Admin 10 years ago
Hi Jonathan, if you don't want to use gridalicious, you can disable it via line 13, mark it as false.

useGridalicious: false,


Also, you will need to edit the template to meet your styling if the current html markup isn't of what you want.
Reply
Scott 10 years ago
Any way to introduce moderation to the feed? So it can be used on public display and only has the chosen tweets/images? Obviously with a real time monitor doing that work, but we would use the images/tweets for a live event display
Reply
James Baker 10 years ago
What do I need to do or modify in order for me to only display the mentions of my twitter account?
Reply
Amit 10 years ago
Hi Kevin,

Amazing tutorial.

Question: Can I use the same technique to display tweets from multiple users? I don't want to use Twitter lists, I want to

- add users to a DB
- pull the users from the DB
- show their tweets just as they are shown in my Twitter homepage.

Is it possible?

Reply