How I made Instagram Content Fetching API in Node.js
Tags: express, instadownloader, javascript, node
Published: 2022-01-08 03:30:44

GitHub Repo : [instagram-api](https://github.com/theviralboy/instagram-api)

So I was planning to build an Instagram downloading website. That's I was researching about this on Google, then I found this amazing trick.

If you put **?\__a=1** after any Instagram link then it'll give a JSON in response.

For Instagram post

```
https://www.instagram.com/p/{post_id}/?__a=1
```

For Instagram user profile

```
https://www.instagram.com/p/{profile_username}/?__a=1
```

But if you try to fetch this API inside your code then, you'll get a CORS error like this.


![cors error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x5edrv9sm1zg7usb3rpy.png)
To overcome this problem we have to use our server. So let's start building a node server for this project.

First, make a new folder and open that folder.

```bash
mkdir instagram-api
cd instagram-api
```

Then initialize a node project.

```bash
npm init -y
```

Now install the following dependencies.

```bash
npm i request express
```

Make a new JavaScript file.

```bash
touch index.js
```

Open VS Code in this folder.

```
code .
```
Modify your `scripts` inside `package.json` file.

Before

```
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
},

```
After

```
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.js" // you must have to install nodemon globally by npm install -g nodemon
},
```

Open the `index.js` file in VS Code and import all dependencies.

```
const express = require("express");
const request = require("request");
```

Now start listening to the port you want to.

```
const express = require("express");
const request = require("request");

const PORT = 1234;

const app = express();
app.listen(PORT, () => {
  console.clear();
  console.log(`listing in http://localhost:${PORT}`);
});
```

You can start the server to see the results side by side.

```
npm start
```

![Listning](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajitx6bimaj7hd4pogul.png)

Now add a root route for testing and open the server in your browser.

```
app.get("/", (req, res) => {
  res?.send("I am working.");
});
```

![Testing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kiyi8cy66kkb6yy1ko8j.png)

Add another route for Instagram posts.

```
app.get("/post/:id", (req, res) => {
  const { id } = req?.params;
  if (id) {
    res.send(`Post id = ${id}`);
  }
});
```

Now I will use the `request` package to scrape the response data. It will scrape the data from Instagram API as a simple string and then I'll parse that string into JSON.

```
app.get("/post/:id", (req, res) => {
  const { id } = req?.params;
  if (id) {
    const link = `https://www.instagram.com/p/${id}/?__a=1`;
    request(link, (err, response, html) => {
      if (!err) {
        const json = JSON.parse(html);
        if (json) res.send(json);
      }
    });
  }
});
```

And now if I look into my server with a valid Instagram post id I'll see something like this.

![Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmgpqct4r8gdmjo264fx.png)
I am getting a lot of data but I need only a certain type of data.
So I'll destructor it.

```
const json = JSON.parse(html)?.graphql?.shortcode_media;
res?.send({
  id,
  mainContent: {
    displayUrl: json?.display_url,
    resolutions: json?.display_resources.map((item) => ({
      src: item?.src,
    })),
  },
  user: {
    username: json?.owner?.username,
    isVerified: json?.owner?.is_verified,
    profilePic: json?.owner?.profile_pic_url,
  },
  caption: {
    main: json?.edge_media_to_caption.edges[0]?.node.text,
    accessibile: json?.accessibility_caption,
  },
  location: json?.location,
  comments: json?.edge_media_to_parent_comment?.count,
  likes: json?.edge_media_preview_like?.count,
  isVideo: json?.is_video,
  videoUrl: json?.video_url,
  allImages: json?.edge_sidecar_to_children?.edges?.map((item) => ({
    displayUrl: item?.node?.display_url,
    resolutions: item?.node?.display_resources?.map((item) => ({
      src: item?.src,
      size: item?.config_width,
    })),
  })),
});
```

Now its much clean and it gives me what I need.

![Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zr6qtgm941jzv1s1u61v.png)
You can also destructor it according to your needs.
I'll also do same for the user profile.

```
app.get("/user/:username", (req, res) => {
  const { username } = req?.params;
  if (username)
    const link = `https://www.instagram.com/${username}/?__a=1`;
    request(link, (err, response, html) => {
      if (!err) {
        const json = JSON.parse(html)?.graphql?.user;
        if (json)
          res?.send({
            username,
            bio: json.biography,
            profilePic: {
              sd: json?.profile_pic_url,
              hd: json?.profile_pic_url_hd,
            },
            fullName: json?.full_name,
            following: json?.edge_followed_by.count,
            follows: json?.edge_follow.count,
            posts: {
              total: json?.edge_owner_to_timeline_media?.count,
              content: json?.edge_owner_to_timeline_media?.edges?.map(
                (item) => ({
                  id: item?.node?.shortcode,
                  displayUrl: item?.node?.display_url,
                  caption: {
                    main: item?.node?.edge_media_to_caption.edges[0].node.text,
                    accessibile: item?.node?.accessibility_caption,
                  },
                  isVideo: item?.node?.is_video,
                  likes: item?.node?.edge_liked_by.count,
                  location: item?.node?.location,
                  comments: item?.node?.edge_media_to_comment.count,
                })
              ),
            },
          });
      }
    });
});
```

Now it's time to use this API inside the frontend.
I'll use `React JS` as a frontend framework and `axios` for fetching API in this project.

I'll fetch this API and set images to `images`.
As you can see in this block of code.
```
useEffect(() => {
  const [images, setImages] = useState();
  const getData = async () => {
    const { data } = await axios("http://localhost:1234/post/CYa0_SRtUrf");
    if (data) {
      setImages(data);
      console.log(data);
    }
  };
  getData();
}, []);
```

Now if I check the console there will be another CORS error.

![cors error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dj4ho4u09lr3efsf6dee.png)

To solve this error install another npm package called `cros`.

```bash
npm i cors
```

Now import it and use it

```
const express = require("express");
const cors = require("cors");

const app = express();
app.use(cors());
```

Now It will work.

![response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s5kzd9aj3c7eojbr3ise.png)
But now, if I try to display images inside my webpage, it will again give me an error:

![error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7mdz7b17w8oss6t5myvm.png)
It is a bit difficult to deal with this error. But there are two ways to solve this problem. But I will show only the most convenient.

To solve this problem I'll convert to images I am getting from Instagram to base64.

If you don't know about base64 then it is.

Base64 is a group of binary-to-text encoding schemes that represent binary data (more specifically, a sequence of 8-bit bytes) in an ASCII string format by translating the data into a radix-64 representation. The term Base64 originates from a specific MIME content transfer encoding. Each non-final Base64 digit represents exactly 6 bits of data. Three bytes (i.e., a total of 24 bits) can therefore be represented by four 6-bit Base64 digits.
**Source Wikipedia** : [Base64](https://en.wikipedia.org/wiki/Base64)

To convert image to base64 first install another package called `image-to-base64`.

```bash
npm i image-to-base64
```

Import `image-to-base641` and make a new asynchronous function `getBase64`.

```
const imageToBase64 = require("image-to-base64");

const getBase64 = async (link) => {
  const base64 = await imageToBase64(link);
  return `data:image/jpeg;base64,${base64}`;
};
```

This function will take the URL of the Instagram image and return it to the base64 code.
Since this is an asynchronous function, I need to wait for the response.
I have to change my code so that I get base64 in response.
Here is the final code.

```
const express = require("express");
const request = require("request");
const imageToBase64 = require("image-to-base64");
const cors = require("cors");

const PORT = 1234;

const app = express();
app.listen(PORT, () => {
  console.clear();
  console.log(`Listing in http://localhost:${PORT}`);
});

app.use(cors());

const getBase64 = async (link) => {
  const base64 = await imageToBase64(link);
  return `data:image/jpeg;base64,${base64}`;
};

app.get("/", (req, res) => {
  res?.send("I am working.");
});

app.get("/post/:id", (req, res) => {
  const { id } = req?.params;
  if (id) {
    const link = `https://www.instagram.com/p/${id}/?__a=1`;
    request(link, (err, response, html) => {
      if (!err) {
        const json = JSON.parse(html)?.graphql?.shortcode_media;
        const promiseArray = json?.edge_sidecar_to_children?.edges?.map(
          async (item) => ({
            displayUrl: await getBase64(item?.node?.display_url),
            resolutions: item?.node?.display_resources?.map((item) => ({
              src: item?.src,
              size: item?.config_width,
            })),
          })
        );
        let allImages;
        if (promiseArray) allImages = Promise.all(promiseArray);
        if (json) {
          (async () => {
            res?.send({
              id,
              mainContent: {
                displayUrl: await getBase64(json?.display_url),
                resolutions: json?.display_resources.map((item) => ({
                  src: item?.src,
                })),
              },
              user: {
                username: json?.owner?.username,
                isVerified: json?.owner?.is_verified,
                fullName: json?.owner?.full_name,
                profilePic: await getBase64(json?.owner?.profile_pic_url),
              },
              caption: {
                main: json?.edge_media_to_caption.edges[0]?.node.text,
                accessibile: json?.accessibility_caption,
              },
              location: json?.location,
              comments: json?.edge_media_to_parent_comment?.count,
              likes: json?.edge_media_preview_like?.count,
              isVideo: json?.is_video,
              videoUrl: json?.video_url,
              allImages: await allImages,
            });
          })();
        }
        if (!json) res?.status?.send("error");
      }
    });
  }
});
app.get("/user/:username", (req, res) => {
  const { username } = req?.params;
  if (username) {
    const link = `https://www.instagram.com/${username}/?__a=1`;
    request(link, (err, response, html) => {
      if (!err) {
        const json = JSON.parse(html)?.graphql?.user;
        const promiseArray = json?.edge_owner_to_timeline_media?.edges?.map(
          async (item) => ({
            displayUrl: await getBase64(item?.node?.display_url),
            id: item?.node?.shortcode,
            location: item?.node?.location,
            caption: {
              main: item?.node?.edge_media_to_caption.edges[0].node.text,
              accessibile: item?.node?.accessibility_caption,
            },
            comments: item?.node?.edge_media_to_comment.count,
            isVideo: item?.node?.is_video,
            likes: item?.node?.edge_liked_by.count,
            isCollection: item?.node?.edge_sidecar_to_children ? true : false,
            resolutions: item?.node?.thumbnail_resources,
          })
        );
        let allImages;
        if (promiseArray) allImages = Promise.all(promiseArray);
        if (json)
          (async () => {
            res?.send({
              username,
              bio: json.biography,
              isVerified: json?.is_verified,
              category: json?.category_name,
              externalURL: json?.external_url,
              profilePic: {
                sd: await getBase64(json?.profile_pic_url),
                hd: await getBase64(json?.profile_pic_url_hd),
              },
              fullName: json?.full_name,
              following: json?.edge_followed_by.count,
              follows: json?.edge_follow.count,
              posts: {
                total: json?.edge_owner_to_timeline_media?.count,
                content: await allImages,
              },
            });
          })();
        if (!json) res?.status(400).send("ERROR");
      }
    });
  }
  if (!username) res?.send(`Error`);
});
```

Now if you try this API in the frontend it will perfectly work. I have build an Instagram Downloader web-app with the help of this API.

Here is the preview.
Post
![Download Page](https://i.ibb.co/TKrPVs6/download-page.png)
User
![User Profile](https://i.ibb.co/wzFvYfn/user-profile.png)

## End

Now let me tell you more about myself that it is my very first API. I don't know much about building an API. So if you don't like my code and want to give some feedback then feel free to suggest me in comments, and also check out my GitHub repo and contribute to make this API more useful.