Key Takeaways
- Utilize the Instagram API to build a real-time photo update app that displays new photos tagged with a specific hashtag as they are posted on Instagram.
- Implement the app using Node.js and Express, with real-time functionality powered by Socket.io for handling notifications from Instagram.
- Begin by registering an Instagram developer account and creating an app to obtain necessary credentials like CLIENT ID and CLIENT SECRET.
- Configure server-side setups such as subscribing to desired hashtags and handling incoming data using various Node.js libraries and middleware.
- Enhance the front-end experience by preloading images, managing subscriptions, and updating the display dynamically as new content arrives.
HEADS UP! Instagram deprecated Realtime subscriptions for tags on Nov 17, 2015 so it’s no longer possible to follow along with this tutorial.
This article was peer reviewed by Jamie Shields, Edwin Reynoso and Tom Greco. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
Instagram is an online photo sharing network that enables its users to take pictures and videos, and share them on a variety of social networking platforms. Users can personalize each photo that they upload (for example by adding various effects) and categorize them with a hashtag.
In this tutorial we’ll be building a real-time photo update app. This will allow users to enter a hashtag to which they would like to subscribe. Then, every time a new photo with that hashtag is posted to Instagram, it will be sent to our app which will display it accordingly.
We’ll implement our app in Node.js using Express as the framework. The real-time part will be implemented using Instagram’s Real-time Photo Updates and Socket.io. As ever, the code for this tutorial is available on our Github repo.
How the Real-time Photo Updates Work
Instagram’s Real-time Photo Updates work by sending notifications to your server every time a new photo is posted to the subscription that you selected. Here’s a break down:
- First, your server sends a subscription request to Instagram.
- Instagram receives the request and verifies that you really want to subscribe by sending back a response which your server has to send back.
- Your server receives the data and sends it back to Instagram.
- If the data is the same, Instagram will start sending notifications to your server.
There are four types of subscriptions from which you receive real-time updates : users, tags, locations and geographies. You can read more about each of these in the documentation. In this tutorial, we’re only going to use the tags subscription. This allows you to receive notifications when a new photo is tagged with whichever tags you specify.
Register an App
The first thing that we need to do is to create an Instagram account then register as a developer.
Next, we need to register a new Instagram app. You can put any valid URL for the website and Redirect URL as they are not needed for our app to function properly.
Once the app is created, take note of the CLIENT ID
and CLIENT SECRET
as they will be needed later when making requests to the Instagram API.
The Server-side setup
The next thing to do is clone the repo and install the dependencies using npm.
git clone git@github.com:sitepoint-editors/express-instagramrealtime.git
cd express-instagramrealtime
npm install
This will pull in the following dependencies:
- Express is the de facto standard web application server framework for Node.js. It is used for serving the public-facing side of the app as well as receiving photo notifications from Instagram.
- Express Handlebars is used for implementing views in Express.js.
- body-parser is used for parsing the form data submitted by the user. In this case, the data is the tag that the user wants to subscribe to.
- instagram-node-lib is a Node.js library for working with the Instagram API. Once we have the tag provided by the user, this library is used to subscribe to the tag.
- socket.io — after subscribing to a specific tag, Instagram sends notifications to the server every time a new photo is posted with the tag that we used. This is where socket.io comes in. It is used for sending the photo data to the front-end every time a new notification is received by the server.
- moment is used for formatting the timestamp provided by the Instagram API.
Now we’re ready to look at the app. In app.js
we first need to require the dependencies we installed.
var express = require('express');
var exphbs = require('express-handlebars');
var moment = require('moment');
var bodyParser = require('body-parser');
var instagram = require('instagram-node-lib');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// set the file name of the default layout
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
// set the expressJS view engine to handlebars
app.set('view engine', 'handlebars');
// set the path to the front-end assets
app.use(express.static('public'));
Now, that we have required the necessary dependencies, we need to set the Instagram CLIENT ID
and CLIENT SECRET
. You will replace these values, with the values generated when you registered your app.
var instagram_client_id = 'YOUR-INSTAGRAM-CLIENT-ID';
var instagram_client_secret = 'YOUR-INSTAGRAM-CLIENT-SECRET';
instagram.set('client_id', instagram_client_id);
instagram.set('client_secret', instagram_client_secret);
Once we have our ID and Secret configured, the next thing we are going to do is create a server instance that will run on port 4000. You can check if the server has started running by using console.log
to output the host and port.
var server = app.listen(4000, function(){
var host = server.address().address
var port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
});
Next, have socket.io listen to the Express server. This binds socket.io to the same port as your Express server, so that later on you can use port 4000 when you connect to this socket on the client side.
var io = require('socket.io').listen(server);
Now let’s move on to creating a new route for the home page of the app. All it does is render the home template.
app.get('/', function(req, res){
res.render('home');
});
Creating the Views
According to the default handlebars configuration, all view files should be stored in the views
directory. The file home.handlebars
will render the form controls into which the user will enter the hashtag that will be used for the real-time photo updates:
<div id="form-wrapper">
<div class="form-group">
<label for="tag" class="control-label">Hashtag</label>
<input type="text" class="form-control input-lg" id="tag" name="tag" autofocus>
</div>
<div class="form-group">
<button id="start" class="btn btn-lg btn-block btn-primary">Start</button>
</div>
</div>
<div id="results" class="hidden">
<div class="row"></div>
</div>
Every time a new photo which has that hashtag is posted by any Instagram user, it will be immediately displayed by the app inside the div with the id of results
.
Earlier on the app.js
file, we set the file name for the default layout with the following code:
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
In handlebars, layout files are stored in the views/layouts
. In this directory the file main.handlebars
serves as the main layout. The main content is rendered inside the div with the id of wrapper
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Real-time Photo Updates</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="wrapper">
{{{body}}}
</div>
<script src="/js/jquery.min.js"></script>
<script src="/js/jquery.backstretch.min.js"></script>
<script src="/js/jquery.imagepreload.min.js"></script>
<script src="/js/vague.min.js"></script>
<script src="/js/socket.io.min.js"></script>
<script src="/js/handlebars.min.js"></script>
<script src="/js/moment.min.js"></script>
<script src="/js/livestamp.min.js"></script>
<script src="/js/script.js"></script>
</body>
</html>
As you can see, a few front-end dependencies are required. Here’s a brief description of each:
- Bootstrap is a front-end framework. This is my personal choice for almost every web thing that I build. If you also want to use Bootstrap, you can find free themes at bootswatch.com.
- jQuery is used for manipulating HTML and listening for click events on the page. It’s also the dependency of the 3 libraries below.
- jQuery BackStretch is used for making the current photo into a full page background image.
- The jQuery Image Preload plugin is used for preloading the image before showing it to the user.
- Livestamp helps keep the timeago text current when no new photo is received from the server side for too long.
- Vague.js applies a blur filter to the background image.
- handlebars is used for generating the HTML to be used for displaying photos.
- moment is used for displaying timeago text based on the timestamp.
These dependencies can be found in public/js
and public/css
. This is because we specified that this folder (public
) should contain our front-end assets:
app.use(express.static('public'));
You could also use bower to download and manage these assets. If you so chose, be sure to update the .bowerrc file so that it uses the static directory that you specified.
With that out of the way, we’re now ready to create the script.js
file.
The Meat and Potatoes
Inside of the file script.js
is where all of the front-end action happens. Here, we need to use jQuery’s $.get
function to fetch the handlebars template from the server. Once it has been fetched, it then must be compiled and stored in a variable. This is the template used for constructing the HTML for displaying the photo.
var template;
$.get('/templates/row.hbs', function(data){
template = Handlebars.compile(data);
}, 'html');
And this is what the handlebars template looks like (public/templates/row.hbs
):
<div class="row">
<div class="photo-container">
<img src="{{image}}" class="photo">
</div>
<div class="photo-details">
<div class="timestamp" data-livestamp="{{created_time}}">{{human_time created_time}}</div>
<img src="{{profile_pic}}" class="userphoto" alt="{{user}}">
<a href="http://instagram.com/{{user}}" target="_blank" class="username">{{user}}</a>
<div class="caption">{{caption}}</div>
</div>
</div>
This contains the code for displaying our application’s photos.
Back in script.js
we need to connect to the socket.io server.
var socket = io.connect('http://your-server.com:4000');
And register a helper to convert Unix timestamps into a human friendly form:
Handlebars.registerHelper('human_time', function(timestamp){
return moment.unix(timestamp).fromNow();
});
When the start button is clicked, we need to send the hashtag entered by the user to the server. Once this is successful, we want to hide the form, and show the photo container.
$('#start').click(function(){
var tag = $('#tag').val();
$.post(
'/tag/subscribe',
{ 'tag': tag },
function(response){
if(response.type == 'success'){
$('#form-wrapper').addClass('hidden');
$('#results').removeClass('hidden');
}
}
)
});
Back on the server-side (in app.js
), our app needs to unsubscribe to all current real-time subscriptions, and then subscribe to the new hashtag that the user has provided. We can do this by making use of the subscribe
method in the tags
object provided by the instagram-node-lib library. Once the server has received a valid response from Instagram, we then send a response that the subscription has been completed.
var current_tag;
app.post('/tag/subscribe', function(req, res){
current_tag = req.body.tag;
console.log('current tag: ' + current_tag);
instagram.tags.unsubscribe_all({
complete: function(unsubscribe_data) {
if(unsubscribe_data == null){
console.log('unsubscribed from everything!');
instagram.tags.subscribe({
object_id: current_tag,
callback_url: 'https://xxxxxxxx.ngrok.io/subscribe',
complete: function(subscribe_data){
if(subscribe_data){
res.send({type: 'success'});
}
}
});
}
}
});
});
When Instagram receives your request to subscribe to a new tag, it sends a GET
request your callback URL. This request contains a query parameter. All the server has to do is to send it back to Instagram for the verification to pass.
app.get('/subscribe', function(req, res){
res.send(req.query['hub.challenge']);
});
Every time a new photo with that tag is posted on Instagram, it automatically sends a notification to your server. This time its a POST
request to the callback_url
that you specified (you will have to change this in the deployment section). Note that this request doesn’t contain any data about the posted photo. It only contains data about the time and the subscription from which it originated. That is why you have to make a separate request to get the recently posted photo. Once a response is returned, create a new object named photo
and then store all the data that you want to return in a new variable. In this case, only the following is needed: username, profile photo, the timestamp with which the image was posted, the URL of the photo, and the caption text. Lastly, inform the client-side that a new photo is available.
app.post('/subscribe', function(req, res){
instagram.tags.recent({
name: current_tag,
count: 1,
complete: function(data){
var photo = {
'user': data[0].user.username,
'profile_pic': data[0].caption.from.profile_picture,
'created_time': data[0].created_time,
'image': data[0].images.standard_resolution.url,
'caption': data[0].caption.text
};
io.sockets.emit('new_photo', photo);
}
});
});
Displaying the Result
Going back to the client-side (script.js), let’s use the jQuery Image Preloader Plugin to preload the image when a new photo comes in. This will fully download the image on the client-side before showing it to the user. Once the image has been preloaded, construct the new HTML using the template
and the photo data. Next, we’re going to make use of the jQuery Backstretch plugin to set the image as the full page background image, as well as vague.js to blur the background. After that, you can append the HTML to the page and then show it with the fadeIn
effect. Finally, remove the last image that was shown.
socket.on('new_photo', function(data){
$.imgpreload(data.image, function()
{
console.log('loaded a new image');
var first_row = $('#wrapper .row:first');
var html = template(data);
$.backstretch(data['image']);
var vague = $('.backstretch').Vague({
intensity: 10,
forceSVGUrl: false
});
vague.blur();
$(html).hide().insertBefore(first_row).fadeIn('slow');
$('#wrapper .row:last').remove();
});
});
As we begin to wrap things up, let’s quickly add some CSS to our application. You can see this in public/css/style.css
. View the file on GitHub.
Deployment
At this point you can now run the app:
node app.js
However, when you navigate to http://localhost:4000/, enter a hashtag and click START, nothing will happen. And if you look at the console then you’ll see the following error:
APISubscriptionError occurred: Invalid response in _request
Hmm! What gives?
The problem is that the app should be accessible through the internet, so as to receive Instagram’s response. As we are running the app on localhost, this unfortunately won’t work. Luckily, we can use ngrok to expose our app the internet. Once you have downloaded and installed ngrok, you can run it by executing the following command in your terminal:
ngrok http 4000
This exposes the Express server to the internet. Be sure to change the callback_url
in the app.js
file, to use the https URL that ngrok returns. If you plan to deploy an app later on, it should also be an https URL.
instagram.tags.subscribe({
object_id: tag,
callback_url: 'https://xxxxxxxx.ngrok.io/subscribe',
...
});
Just copy the forwarding URL. Here’s a screenshot:
Now, if you restart the server, things should work as planned:
Once the user has subscribed, the app will start receiving photo data from the server via socket.io and then display it.
Taking Things Further
If you want to experiment with this app and make some of your own changes, you might want to look into nodemon. This automatically restarts the server every time you make a change to your Node application and is very handy for development.
Then there’s the question of persistence. Once you’re happy with things, and if you’re on a Unix-based system, you can install Supervisor on your server. This allows you to run the app persistently. Simply running the app with nodemon wouldn’t suffice, because the process is terminated the moment you log out of the server.
Within your current terminal window, execute the following command:
sudo apt-get install supervisor
Create a configuration file for the app:
sudo nano /etc/supervisor/conf.d/instagram-realtime.conf
[program:instagram-realtime]
command=nodemon app.js
directory=/home/ubuntu/www
stdout_logfile=/home/ubuntu/logs/instagram-realtime.log
redirect_stderr=true
And then add it to Supervisor by executing the following commands:
sudo supervisorctl
reread
add instagram-realtime
start instagram-realtime
Final Thoughts
That’s it! In this tutorial, you’ve learned to work with the real-time capabilities of the Instagram API using socket.io. Just keep in mind the limits, namely the API calls that you can make to Instagram (which means that the number of users who can subscribe to different tags is limited — especially if the tags are popular). If this is the case, then the server is going to receive a lot of notifications from Instagram and the number of API calls that you can make are going to run out easily. Other than that, you’re free to use the Instagram API however you like.
There are lots of other possibilities with this API, you can embed Instagram posts or integrate it with your mobile applications. As for our app, a perfect use case would be at an event, where you ask the participants to post photos with a specific tag. The event organizer could subscribe to this tag and project the app to a screen, so that everyone can see the photos being shared.
I’d love to hear your thoughts about this app and the Instagram API in general in the comments below.
Frequently Asked Questions (FAQs) about Real-Time Photo Update App with Instagram API
How can I get started with the Instagram API?
To get started with the Instagram API, you first need to create a Facebook App and set up Instagram Basic Display. You will need to add an Instagram Test User, and then authenticate the Test User and get an access token. This access token is what you will use to make API calls. Remember to handle the access token securely as it provides access to the API.
What is the Instagram Basic Display API?
The Instagram Basic Display API is an interface that allows you to access user profile data and media. It is designed for use cases where you need to provide a continuous, long-lived connection to Instagram user data. It is not intended for non-interactive, bulk, or background uses.
How can I get images using the Instagram API?
To get images using the Instagram API, you need to make a GET request to the /media endpoint. This will return a list of media objects from the authenticated user’s media collection. Each media object includes a URL to the actual image file.
What is the Instagram API changelog?
The Instagram API changelog is a document that lists all the changes made to the Instagram API over time. It includes information about new features, bug fixes, and deprecated features. It’s a useful resource for developers to keep track of changes and updates to the API.
How can I use the Instagram Web API package?
The Instagram Web API package is a Node.js module that provides a simple way to interact with the Instagram API. You can install it using npm, and then require it in your project. It provides methods for authenticating users, getting user profile data, and fetching user media.
What are the limitations of the Instagram API?
The Instagram API has some limitations and restrictions. For example, you can only make a certain number of requests per hour, and some endpoints require specific permissions. It’s important to read the API documentation and understand these limitations before you start developing your application.
How can I handle errors with the Instagram API?
The Instagram API returns standard HTTP status codes to indicate the success or failure of an API request. If an error occurs, the API will also return an error message that provides more information about what went wrong. You should implement error handling in your application to deal with these situations.
Can I use the Instagram API to post photos?
No, the Instagram API does not currently support posting photos. It is primarily designed for reading data, not writing data. If you need to post photos to Instagram programmatically, you will need to use the Instagram Graph API, which requires a business or creator account.
How can I paginate results with the Instagram API?
The Instagram API uses cursor-based pagination. This means that each response includes a ‘next’ cursor that you can use to fetch the next page of results. You simply include this cursor in your next API request to get the next page of data.
How can I rate limit with the Instagram API?
The Instagram API imposes rate limits to prevent abuse and ensure fair usage. The rate limits depend on the type of access token you are using and the type of request you are making. If you exceed the rate limits, your API requests will be throttled until the rate limit resets.
Wern is a web developer from the Philippines. He loves building things for the web and sharing the things he has learned by writing in his blog. When he's not coding or learning something new, he enjoys watching anime and playing video games.