How I hacked instagram’s sandboxed API to get direct links to my latest pictures

Goal: automatically display my latest Instagram picture on my personal website.

Simple, right? Instagram has an API and they use oauth. I should just be able to make an app, get the client id and secret, then use that to serve content. Here’s why that doesn’t work.

They use oauth 1.0, but if you’re not an ‘approved’ app you’re stuck in ‘sandbox’ mode. Essentially your users have to auth with instagram, then exchange that code for an access token. That sounds fine, until you realize they limit you to only 10 users while you’re sandboxed! Here’s how I beat the system in a *nearly* automated way.

Getting the access token

Once you have an access token you can use every endpoint in instagram’s API. This token expires randomly, so to keep it valid you will have to manually log in every few months. I set up a cron job to check my endpoint every hour to make sure the token is still valid. If the endpoint fails, then it sends an email and I know to re-authenticate with instagram.

Create a node project with the following packages, then make a file server.js with the following packages

const express = require('express')
const app = express()
const fs = require('fs')
const axios = require('axios')
const opn = require('opn')
const bodyParser = require('body-parser')
const ig = require('instagram-node').instagram();

You don’t really need instagram-node, but I had issues authing with axios. Annoyingly you can only authenticate with instagram using form data, not json.

In server.js add the following code with your client id and secret

app.use(bodyParser.json())
app.use(express.static('public'))
app.post('/auth', (req, res) => {
ig.use({client_id: 'YOUR CLIENT ID', client_secret: 'YOUR CLIENT SECRET', })
ig.authorize_user(req.body.code, req.body.origin, function(err, result) {
if (err) console.log('err', err)
if (result)
fs.writeFile('access_token.txt', result.access_token, 'utf8') app.listen(3001, () => {
console.log('Server running on port 3001') opn('http://localhost:3001')
})

Now create a public folder with index.html

All you need your ‘frontend’ to do is pass along an access code to the server to get an access token

Put this between <script> tags in index.html

function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
const code = getParameterByName('code')
if (!code) {
const instaHttp = new XMLHttpRequest();
const instaUrl=`https://api.instagram.com/oauth/authorize/?client_id=YOURCLIENTID&redirect_uri=YOURREDIRECTURI&response_type=code`;
instaHttp.open('GET', instaUrl);
instaHttp.send();
instaHttp.onreadystatechange = (e) => {
  if (instaHttp.readyState === 4) { 
location.replace(instaHttp.responseURL)
}
}
} else {
const authHttp = new XMLHttpRequest();
const authUrl=`/auth`;
authHttp.open('POST', authUrl);
authHttp.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
const origin = window.location.origin;
authHttp.send(JSON.stringify({ code, origin }));
}

The idea is we check the page for a query parametercode , which is our access code. if we do not have it, then we authenticate with instagram. If we have it we send the code to our backend to get the access token.

Run your server which should pop open your browser to localhost:3001 and it should ask you to authenticate with instagram. I intentionally didn’t automate this step because I was afraid instagram may have some ‘Are you a robot’ check if they saw you repeatedly logging in through here.

If everything worked, you should have a file named ‘access_token.txt’ in your root directory.

Using your access token

With that access token you have total access to all the endpoints instagram offers. All you need to do is read in the code from that file, and store it as something like accessToken . Then, you can retrieve data from endpoints like so: https://api.instagram.com/v1/users/self/media/recent/?access_token=${accessToken}