Fetching GitHub repository Traffic data using API v3

Fetching GitHub repository Traffic data using API v3

Table of contents

No heading

No headings in the article.

GitHub records the traffic data (number of views and clones) to every repository. This can be seen in the Insights -> Traffic in the repository. Generally, the graph in the traffic tab shows the views and clones for the last 14 days. It shows not just the total views but also the number of unique visitors to the repository as shown in the below image. How can we fetch these data to populate into our own UI or to consume into some analytics?

Traffic data

This is where GitHub API v3 comes into the picture. Note here that v3 stands for version 3 of API which works using standard HTTP. The latest version of GitHub API is v4 which works on GraphQL. We are specifically using v3 because the schema related to traffic data is not yet available for v4.

Let us get started. First of all, let us initialize the new project by executing the command npm init. This will create a package.json file in the current directory.

We need to create personal access token from GitHub to use API. Once we get the token, create a .env file to store the token. Do not forget to add the .env file to .gitignore so that you don't commit the token accidentally to the public repository. The .env file looks as follows:

TOKEN=<PASTE YOUR TOKEN HERE>

We can install the required dependencies using the below command:

$ npm install octokit
$ npm install dotenv

Octokit is an npm package created by GitHub to use GitHub API v3. It provides objects and functions to interact with API and to fetch the data smoothly.
Dotenv is also an npm package that lets us use the variables defined in the .env file.

Once the above setup is completed, we are ready to get started with the actual code to fetch the data. Initially, we import the Octokit class from the octokit package and create an authenticated client object as shown below:

import dotenv from 'dotenv';
import { Octokit } from 'octokit';

dotenv.config();

client = new Octokit({ auth: process.env.TOKEN });

We import dotenv and call the config() method in the beginning of the flight so that it loads all the variables defined in .env files and we can use them by process.env.<VARIABLE_NAME>.
We are using Octokit class to create a client object which takes the authentication token as an argument.

You can take a look at API specifications to understand more about the API. Here, we are only going to use /clones and /views endpoints to get the data but you can use any other endpoint as mentioned in the specification documentation.

As mentioned in the documentation, both endpoints /clones and /views requires information about the repository name for which you want to get the traffic data and also GitHub username of the owner of the repository. Let us create a method that takes the above arguments and makes an API call to fetch the data (views and clones)

async function getViewers(owner, repository) {
    return await octokit.request(
        `GET /repos/${owner}/${repository}/traffic/views`
    );
}

async function getCloners(owner, repository) {
    return await octokit.request(
        `GET /repos/${owner}/${repository}/traffic/clones`
    );
}

That is all we require to fetch the data. You can now observe that octokit makes life easier. We just need to provide URL of the endpoint and it gets the data. If you look at other endpoints in API documentation, you will see that it provides similar flexibility for POST, PUT, DELETE endpoints as well.

Let us call above created methods and print the fetched data on the console with the following code:

let owner = 'ashutosh1919';
let repository = 'masterPortfolio';

getViewers(owner, repository)
    .then(res => console.log(`Viewers: ${res}`));

getCloners(owner, repository)
    .then(res => console.log(`Cloners: ${res}`));

Note that getViewers() and getCloners() are async method so either we can use await to get the response synchronously or we can use then() to get the response asynchronously.

Below is the code for the entire file that we have written as of now:

import dotenv from 'dotenv';
import { Octokit } from 'octokit';

dotenv.config();

client = new Octokit({ auth: process.env.TOKEN });

async function getViewers(owner, repository) {
    return await octokit.request(
        `GET /repos/${owner}/${repository}/traffic/views`
    );
}

async function getCloners(owner, repository) {
    return await octokit.request(
        `GET /repos/${owner}/${repository}/traffic/clones`
    );
}

let owner = 'ashutosh1919';
let repository = 'masterPortfolio';

getViewers(owner, repository)
    .then(res => console.log(`Viewers: ${res}`));

getCloners(owner, repository)
    .then(res => console.log(`Cloners: ${res}`));

Above code does fetches the viewers and cloners (traffic) data and prints on the console. If the filename where the above code is stored is index.js, we can run it using node index.js. Make sure that you have latest stable version of NodeJS.

On executing the above file, we get result shown as below:

Viewers: {
.
.
.
"data": {
    "count": 2973,
    "uniques": 970,
    "views": [
      {
        "timestamp": "2021-11-18T00:00:00Z",
        "count": 1,
        "uniques": 1
      },
      {
        "timestamp": "2021-11-19T00:00:00Z",
        "count": 189,
        "uniques": 63
      },
      {
        "timestamp": "2021-11-20T00:00:00Z",
        "count": 156,
        "uniques": 55
      },
      {
        "timestamp": "2021-11-21T00:00:00Z",
        "count": 186,
        "uniques": 76
      },
      {
        "timestamp": "2021-11-22T00:00:00Z",
        "count": 175,
        "uniques": 76
      },
      {
        "timestamp": "2021-11-23T00:00:00Z",
        "count": 278,
        "uniques": 77
      },
      {
        "timestamp": "2021-11-24T00:00:00Z",
        "count": 182,
        "uniques": 75
      },
      {
        "timestamp": "2021-11-25T00:00:00Z",
        "count": 181,
        "uniques": 84
      },
      {
        "timestamp": "2021-11-26T00:00:00Z",
        "count": 128,
        "uniques": 68
      },
      {
        "timestamp": "2021-11-27T00:00:00Z",
        "count": 208,
        "uniques": 73
      },
      {
        "timestamp": "2021-11-28T00:00:00Z",
        "count": 310,
        "uniques": 82
      },
      {
        "timestamp": "2021-11-29T00:00:00Z",
        "count": 331,
        "uniques": 104
      },
      {
        "timestamp": "2021-11-30T00:00:00Z",
        "count": 212,
        "uniques": 92
      },
      {
        "timestamp": "2021-12-01T00:00:00Z",
        "count": 282,
        "uniques": 83
      },
      {
        "timestamp": "2021-12-02T00:00:00Z",
        "count": 154,
        "uniques": 67
      }
}

Cloners: {
.
.
.
"data": {
    "count": 164,
    "uniques": 112,
    "clones": [
      {
        "timestamp": "2021-11-19T00:00:00Z",
        "count": 12,
        "uniques": 5
      },
      {
        "timestamp": "2021-11-20T00:00:00Z",
        "count": 1,
        "uniques": 1
      },
      {
        "timestamp": "2021-11-21T00:00:00Z",
        "count": 9,
        "uniques": 7
      },
      {
        "timestamp": "2021-11-22T00:00:00Z",
        "count": 12,
        "uniques": 12
      },
      {
        "timestamp": "2021-11-23T00:00:00Z",
        "count": 15,
        "uniques": 11
      },
      {
        "timestamp": "2021-11-24T00:00:00Z",
        "count": 11,
        "uniques": 8
      },
      {
        "timestamp": "2021-11-25T00:00:00Z",
        "count": 9,
        "uniques": 8
      },
      {
        "timestamp": "2021-11-26T00:00:00Z",
        "count": 11,
        "uniques": 9
      },
      {
        "timestamp": "2021-11-27T00:00:00Z",
        "count": 24,
        "uniques": 11
      },
      {
        "timestamp": "2021-11-28T00:00:00Z",
        "count": 7,
        "uniques": 6
      },
      {
        "timestamp": "2021-11-29T00:00:00Z",
        "count": 24,
        "uniques": 15
      },
      {
        "timestamp": "2021-11-30T00:00:00Z",
        "count": 13,
        "uniques": 13
      },
      {
        "timestamp": "2021-12-01T00:00:00Z",
        "count": 7,
        "uniques": 7
      },
      {
        "timestamp": "2021-12-02T00:00:00Z",
        "count": 9,
        "uniques": 7
      }
}

The above data can be consumed for different purposes in the applications, dashboards or CI/CD pipelines.

I (Ashutosh Hathidara) am an artificial intelligence engineer, full-stack developer, and designer. I can be reached via:
Email:
Twitter: @ashutosh_1919
Github: @ashutosh1919
Discord: DevSense

Did you find this article valuable?

Support Ashutosh Hathidara by becoming a sponsor. Any amount is appreciated!