How to migrage Instagram Legacy API platform to Instagram Basic Display API

This article explains the steps required to update Instagram Legacy API to the newer Instagram Basic Display API.

Instagram will disable its Legacy API Platform on June 29, 2020. Originally, it was set to be disabled on March 31, 2020, but the deadline was extended. See the Instagram Developer page for more information on this deadline. https://www.instagram.com/developer

Instagram Notice:

Instagram Notice

If you are displaying your Instagram feed on a website, then the feed will stop working after the deadline. You will have to update your website to use the Instagram Basic Display API. This article will explain how to display your Instagram feed on a website. The article uses references from https://developers.facebook.com/docs/instagram-basic-display-api/getting-started

In order to display Instagram feed using Basic Display API, you will need to have to the followings:

  1. A Facebook Developer Account
  2. Account credentials (username/email and password) for the Instagram Account you want to display the feed
  3. Instagram Access Token
  4. A terminal or software like Postman to do API calls

Once you have #1 and #2, we need to create an access token for the Instagram Account.

Steps to generate Instagram Access Token to access Basic Instagram Display API

We have to use a Facebook account to create an access token for Instagram. You can no longer create access token through your Instagram account like we did previously. The first thing we need to do is create a Facebook App in your Facebook account and add Instagram App as a product to that Facebook app. It involves a number of steps to create an Instagram Access Token. Let's start the steps.

Step I: Create a Facebook App

Create new app

  • In the App Dashboard, go to Settings > Basic > click Add Platform at the bottom of the page. App Dashboard
  • Select Website, add https://www.example.com/ as the Site URL and save. https://www.example.com/ should be replaced with your website domain. Also, note the '/' at the end of the URL. You must include '/' at the end, otherwise the further steps will issue error. Website Site URL

Step II: Set up Instagram Product

  • Click Products > Instagram > Set Up. Products Set Up
  • Click Basic Display > Create New App (at the bottom of the page). Create New App
  • In the form, enter the fields as below and save changes
  • Display Name: Name of the Facebook App created (For example: Sample-IGAPP)
  • Valid OAuth Redirect URIs: https://www.example.com/ [Do not miss the / at the end of the URI]
  • Deauthorize Callback URL: https://www.example.com/
  • Data Deletion Request Callback URL: https://www.example.com/

Step III: Add Instagram Account Test User

  • Add an Instagram Test User by clicking Roles > Roles > Add Instagram Testers (in the Instagram Testers section) Add Instagram Test User
  • Enter Instagram account username that you want the feed from. Once you start typing, it should auto-populate the Instagram Username for you to select. Select the correct Instagram Account from the popup list and click submit to send the invitation.
  • Now, login to the Instagram account that you just sent the invitation.
  • Go to Edit Profile > Apps and Websites > Tester Invites and accept the invitation. Tester Invite

Once the invite is accepted, the Tester Invites page should look like this: Tester Invite Accepted

Step IV: Authenticate and Obtain Authorization Code

  • On your Facebook Developer Account, go to App Dashboard > Products > Instagram > Basic Display > Instagram App ID
  • Open the link below on a new browser tab after replacing the Client ID and Redirect URI with your values. Don't forget the '/' at the end of the Redirect URI.

https://api.instagram.com/oauth/authorize?client_id={Instagram App ID}&redirect_uri={Valid OAuth Redirect URI}&scope=user_profile,user_media&response_type=code

For example:

https://api.instagram.com/oauth/authorize?client_id={240439832112592}&redirect_uri=https://www.example.com/&scope=user_profile,user_media&response_type=code

  • You should now see an Authorization Window that display the Instagram User's name and the App's name and permission details requested by the app. Authorize Window
  • Click Authorize button and the page should redirect to the redirect URI you included in the previous steps (https://www.example.com/)
  • The redirected page should include an authorization code at the end of the URL with code parameter and #_ characters.

https://www.example.com/?code=AQCpFnU...#_

  • Copy the code value without the #_. This is the Authorization code that will be used to obtain access token. The Authorization code will expire in an hour.

Authorization code = AQCpFnU...

Step V: Generate Instagram Short Lived Access Token

  • Open a terminal window and run the following CURL command after replacing the values for app-id, app-secret, redirect-uri and code:

    curl -X POST \
    https://api.instagram.com/oauth/access_token \
    -F client_id={app-id} \
    -F client_secret={app-secret} \
    -F grant_type=authorization_code \
    -F redirect_uri={redirect-uri} \
    -F code={code}
    

    The app-id, app-secret, redirect-uri and code values can be obtained from App Dashboard > Products > Instagram > Basic Display

app-id = Instagram App ID

app-secret = Instagram App Secret

redirect-uri = https://www.example.com/


code = Authorization code obtained from previous step

For example:

curl -X POST \
  https://api.instagram.com/oauth/access_token \
  -F client_id=240139... \
  -F client_secret=143ef0... \
  -F grant_type=authorization_code \
  -F redirect_uri=https://www.example.com/ \
  -F code=AQCpFnU...
  • Once the CURL command is run successfully, a similar output is expected.

    {
    "access_token": "IGQVJYaUNsMW...",
    "user_id": 17841400614018206
    }
    

    The access token obtained is a short lived token and will expire in an hour.

Step VI: Verify Short Lived Access Token

  • In order to check if the short lived access token obtained from the previous step is working, run the following CURL command:

    curl -X GET \
    'https://graph.instagram.com/me?fields=id,username&access_token=IGQVJYaUNsMW...'
    

or run the alternate command:

curl -X GET \
  'https://graph.instagram.com/{user-id}?fields=id,username&access_token={access-token}'

Once the command runs, the following output is expected:

{
  "id": "17841400614018206",
  "username": "YourInstagramUsername"
}

Step VII: Obtain Long Lived Access Token

The access token obtained in the previous step is short lived and expires in an hour. In order to extend the expiration of the access token we must obtain long lived access token. The long lived access token expires in 60 days and can be refreshed after 24 hours once it is created and before the 60 day. We have to use the short lived access token to obtain the long lived access token.

  • Run the following CURL command to get the long lived access token:

    curl -i -X GET "https://graph.instagram.com/access_token?grant_type=ig_exchange_token&client_secret=instagram-app-secret}
    &access_token={short-lived-access-token}"
    

    short-lived-access-token = short lived access token must not be expired

instagram-app-secret = App Dashboard > Products > Instagram > Basic Display > Instagram App Secret

For example:

curl -i -X GET "https://graph.instagram.com/access_token?grant_type=ig_exchange_token&client_secret=240139...&access_token=IGQVJYaUNsMW..."

The following output is expected:

{
    "access_token":"{long-lived-access-token}",
    "token_type":"bearer",
    "expires_in":5183906
}

Step VIII: Refresh Long Lived Access Token (optional)

The long lived access token can be refreshed once it is 24 hours old and has not expired. The refreshed access token will expire in 60 days and can be refreshed before it expires.

  • To refresh the long lived access token:

    curl -i -X GET "https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token={long-lived-access-token}"
    

    The following output is expected:

    {
    "access_token":"{long-lived-user-access-token}",
    "token_type": "bearer",
    "expires_in": 5183944 // Number of seconds until token expires
    }
    

Step IX: Save Long Lived Access Token

  • Copy the long lived access token and save it to a file with the same name as the instagram account.

For example: /var/tokens/mytokenfile The file should have the following file permissions:

-rwxrw-r-x 1 root apache 149 Mar 19 19:48 mytokenfile

You can run the following commands to update the correct file permissions.

chgrp apache mytokenfile - changes the file group to apache user. [Here I am using CentOS Linux, but depending on the Linux distribution you are using, it may be www-data or other web server user.]

chmod 765 mytokenfile - change the file mode so that the group can read and write the tokenfile.

NOTE: You may need to use sudo to run the above commands.

We will be referencing this file in our PHP file to obtain the Instagram feed.

Step X: Fetch Instagram Feed and Display on the webpage

The following two PHP files fetch the Instagram feed from your account and display them on the webpage. InstagramFeed.php is the main file that send the request to get the feed to Instagram using the long lived access token we generated in our previous step. The getFeed.php script references InstagramFeed.php file to display the feed on the webpage. You can modify the code as per your requirement. This is just an example of how we can retried feed from Instagram to our webpage.

NOTE: To reduce call to Instagram server, we are utilizing a cache file to store the fetched feed and use that cache file for 24 hours. The access token file is also updated every 24 hours to generate a new long lived access token. We also have a feed limit of 4 by default so that we do not pull all the feed at once.

Copy the code below and save it as InstagramFeed.php.

<?php
namespace InstagramFeedApp;

define('TWENTY_FOUR_HOURS', 86400);
define('INSTAGRAM_REFRESH_TOKEN_API_URL', 'https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token');
define('INSTAGRAM_FEED_API_URL', 'https://graph.instagram.com/me/media?fields=caption,media_url,media_type,thumbnail_url,permalink');
//fields=id,caption,media_type,media_url,permalink,thumbnail_url,username,timestamp

class InstagramFeed
{
  protected $tokenFile;
  protected $cacheFile;
  protected $feedLimit;
  protected $defaultCaption = 'Default Caption';
  protected $accessToken;

  public function __construct($tokenFile, $cacheFile, $feedLimit = '4')
  {
    $this->tokenFile = $tokenFile;
    $this->cacheFile = $cacheFile;
    $this->feedLimit = $feedLimit;
  }

    public function getInstagramFeed()
    {
      $feeds = $this->getFeedFromCache();

      if (! $feeds) {
          $this->accessToken = $this->getAccessToken();
          $feeds = $this->sendCurlRequest(INSTAGRAM_FEED_API_URL . "&limit={$this->feedLimit}&access_token={$this->accessToken}");
          file_put_contents($this->cacheFile, serialize($feeds));
      }

      return $this->getFormattedFeed($feeds);
    }

    private function getFormattedFeed($feeds)
    {
      $formattedFeed    = [];
      $feedLimit                = $this->feedLimit;
      foreach ($feeds->data as $feed) {
        $caption = (isset($feed->caption)) ? $feed->caption : $this->defaultCaption;
        $image = ($feed->media_type == 'VIDEO') ? $feed->thumbnail_url : $feed->media_url;
        $formattedFeed[] = [
          'caption'       => $caption,
          'image'         => $image,
          'link'          => $feed->permalink,
        ];
       }

      return $formattedFeed;
    }

    private function getFeedFromCache()
    {
      $cacheFile = $this->cacheFile;
      if (file_exists($cacheFile)) {
          if ($this->isFileModifiedLessThan24Hours($cacheFile)) {
              return unserialize(file_get_contents($cacheFile));
      }
      }

      return FALSE;
    }

    private function getAccessToken()
    {
        try {
            $tokenFile = $this->tokenFile;
            if (file_exists($tokenFile)) {
                $accessToken = file_get_contents($tokenFile);
                $accessToken = trim($accessToken);
                if ($this->isFileModifiedLessThan24Hours($tokenFile)) {
                    return $accessToken;
                }

                $newAccessToken = $this->refreshAccessToken($accessToken);

                file_put_contents($tokenFile, $newAccessToken);

                return $newAccessToken;
            } else {
                throw new \Exception('Token File Missing');
            }
        } catch(\Exception $e) {
            die($e->getMessage());
        }
    }

    private function refreshAccessToken($currentAccessToken)
    {
        $curlResponse = $this->sendCurlRequest(INSTAGRAM_REFRESH_TOKEN_API_URL . "&access_token=$currentAccessToken");

        return $curlResponse->access_token;
    }
    private function isFileModifiedLessThan24Hours($fileName)
    {
        $fileModifiedTime = time() - filemtime($fileName);
      return ($fileModifiedTime < TWENTY_FOUR_HOURS);
    }

    private function sendCurlRequest($curlURL)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $curlURL);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 20);
        $response = curl_exec($ch);
        if(curl_error($ch)) {
            echo "CURL ERROR: " . curl_error($ch) . "<br>";
        }
        curl_close($ch);

        return json_decode($response);
    }
}

Copy the code below and save it as getFeed.php

 <?php
namespace InstagramFeedApp;  

require_once 'InstagramFeed.php';
//Required to set these values
$tokenFile= 'mytokenfile';
$cacheFile = '/tmp/feed_cache.txt';
$igApp = new \InstagramFeedApp\InstagramFeed($tokenFile, $cacheFile);
$feeds = $igApp->getInstagramFeed('html');
?>
<div style="padding: 10px;">
    <a href="<?= $feeds[0]['link'] ?>"><img src="<?= $feeds[0]['image'] ?>" alt="<?= $feeds[0]['caption'] ?>" width="300" /></a>
</div>
<div style="padding: 10px;">
    <a href="<?= $feeds[1]['link'] ?>"><img src="<?= $feeds[1]['image'] ?>" alt="<?= $feeds[1]['caption'] ?>" width="300" /></a>
</div>
<div style="padding: 10px;">
    <a href="<?= $feeds[2]['link'] ?>"><img src="<?= $feeds[2]['image'] ?>" alt="<?= $feeds[2]['caption'] ?>" width="300" /></a>
</div>
<div style="padding: 10px;">
    <a href="<?= $feeds[3]['link'] ?>"><img src="<?= $feeds[3]['image'] ?>" alt="<?= $feeds[3]['caption'] ?>" width="300" /></a>
</div>

If you access the getFeed.php file from your server on a browser, it should display the images retrieved from your Instagram account.