Analytics API

How to gather analytics and usage stats with the Rest API

The Analytics APIs allow you to query your usage independent of the Dolby.io dashboard and get details of how your users are consuming your streams.

To access the analytics API you must have an API token. To learn more on how to get your API token, please read the following article Acquiring Your API Token.

Another thing to note is that analytics API calls will not work directly from the Browser, there is no CORS support. To successfully call the API you must use Curl or a back end solution like Nodejs, Java, or anything outside the browser that can do HTTPS requests.

All requests made to the analytics API require that you provide a start and stop date range. The date must be structured in a UTC format containing the year, month, day and hour (eg 2020-01-01T00:00:00Z). These series based requests require you to set an analytics formatting resolution value of "Month", "Day" or "Hour", the resulting data will follow the specified format accordingly. The time part of your date string is optional and used only if you're querying the API for hourly information.

Access Restrictions

There are few restrictions to note regarding the Analytics API. Currently the stats are updated every 15 minutes of the hour (ex: XX:00, XX:15, XX:30, XX:45). Also, analytics providing a daily breakdown of your usage (Day) are stored for a window of 90 days, and the hourly data (Hour) is stored for a window of 7 days. This means you can query your data using the Day format for a maximum timeframe of the last 90 days. Hourly data is available to be queried for a maximum timeframe of last 7 days from the current day of your query. After the respective timeframes, this detailed data is purged from our system and you will then only have access to summarized monthly data.

A breakdown of per-stream data is also only available within a 7 day window. This includes geographic data as well as detailed bandwidth usage information per stream. If this detailed usage data is valuable to you, it is advised that you regularly download this data before it expires and is no longer available for querying.

To query for a particular month’s data, the API requires the date range to be from day 1 of the month in question to day 1 of the next month. An example of the query would look like this: startDate=2021-01-01&stopDate=2021-02-01&resolution=Month. This query would return the total bandwidth usage for the month of January 2021.

It is advised that you save your Daily and Hourly data if you feel that you would need it beyond the 90 and 7 day retention provided by Dolby.io Real-time Streaming.

Wildcards

The Dolby.io Real-time Streaming broadcaster token provides a Wildcard option which allows you to use an arbitrary stream name on-the-fly, rather than specifying the stream name in advance. This is beneficial if you are dynamically generating streams and are not sure how many streams you will need to generate or which names you will need to use. Because queries for stream analytics require a stream name, it is important for you to keep track of which stream names are being used if you would later like to query the analytics data for these individual streams.

Basic Call Structure

In this example we want to query the API to get usage information for our entire account. There are 3 main calls you can do to get this information, Account Total, Account Series and Account Geo. Each one respectively has its own format and function, however, each call at the minimum requires a start and a stop date.

Account Total will give you the total bandwidth usage in bytes for your overall account based on a specified date range. Account Geo will do the same but will provide the totals based on the geographic region of the viewers. Similar to Geo, the Account Series will also breakdown the totals based on your specified resolution. For example if you chose "monthly" as your resolution format, the data would be broken down by month and so on. You can save this data into your own database for your own querying later, or you can plug this data into data visualizers like Google Charts to visually display the results of your usage.

Example Tutorial - JS / Nodejs

In the following example, we will use the Analytics API to query a Dolby.io Real-time Streaming account for a date range of usage and display it in a graph using Google Charts (see: https://developers.google.com/chart/interactive/docs/gallery/areachart).

Before proceeding we assume you are familiar with Nodejs and have some familiarity with Google Charts. For this example we used a Nodejs server version 12.19 (the latest stable version as of February 2021).

As with all API calls to our platform you must have your API token ready, you can find this in your Dolby.io dashboard.

Let's start by creating a new Nodejs project:

npm init

Create an app.js file that will run the code for the API calls. Then add the following code snippet to your app.js file.

const express = require('express');  
const https = require('https');  
const fs = require('fs');  

const port = 8443;  
const startUTC = '2020-08-01';//UTC year-month-day  
const stopUTC = '2021-01-01';  
const token = 'c0bda74e6701f9b23aec4451225b6bf9b1cfdc1aaeed5a4db38f538d2dd';  

//Dolby.io Real-time Streaming request details  
const options = {  
  hostname: 'api.millicast.com',  
  path: '/api/analytics/account/series?startDate=' + startUTC + '&stopDate=' + stopUTC + '&resolution=Month',  
  method: 'GET',  
  headers: {  
    'Authorization': 'Bearer '+token,  
    'Content-Type': 'application/json'  
  }  
}  
console.log('options: ',options);  

const app = express();  

// app.use(bodyParser.json());  
app.use( (req, resp, next) => {  
  resp.setHeader('Access-Control-Allow-Origin', '*');  
  next();  
});  

app.get( '/usage', (req, resp, next) => {  
  console.log('app.get ',arguments.length);  
  //call api  
  let apiReq = https.request(options, res => {  
    console.log('result:',res.statusCode);  
    let body = '';  
    res.on('data', d => {  
      body = body + d;  
    })  
    res.on('end', () => {  
      let s = JSON.parse(body);  
      console.log('END:',s);  
      resp.send(body);  
    })  
  })  
  .on('error', e => {  
    console.log('ERROR',e);  
    resp.satus(404).json(e);  
  });  
  apiReq.end();  
});  

https.createServer(  
  {key: fs.readFileSync('ssl/key.pem'),  
  cert: fs.readFileSync('ssl/cert.pem')},  
  app )  
.listen(port);

console.log('running! see port https://localhost:'+port+'/usage');

In this example we use the standard Express module along with the built in HTTPS module to handle secure requests coming from the client HTML side and for calls going out to the Dolby.io Real-time Streaming from the server. This example uses openssl self-signed certificates to satisfy the key and conf requirement in the HTTPS module on Nodejs. We are testing locally so we run a simple local web server from the Visual Studio Code editor to do the calls over HTTPS locally (https://localhost:8443/). In this case, you would only need to bypass the browser warning that comes up to access your HTML file.

The express "get" method handles the request call to the API when the HTML user makes calls via the "/usage" path specified in the method. The path here is an arbitrary label, feel free to use whatever path name you prefer, just remember the call on the HTML counterpart has to match the path.

app.get( '/usage', (req, resp, next) => {  
  console.log('app.get ',arguments.length);  
  //call api  
  let apiReq = https.request(options, res => {  
    console.log('result:',res.statusCode);  
    let body = '';  
    res.on('data', d => {  
      body = body + d;  
    })  
    res.on('end', () => {  
      let s = JSON.parse(body);  
      console.log('END:',s);  
      resp.send(body);  
    })  
  })  
  .on('error', e => {  
    console.log('ERROR',e);  
    resp.satus(404).json(e);  
  });  
  apiReq.end();  
});

The GET call will run a HTTPS request to query the API for the account usage using the "account/series" call (see: Analytics AccountSeries API). It is required that you add a date range which you can adjust yourself using the "startUTC" and "stopUTC" variables at the top of this example. Remember, the time value you use needs to be set in a UTC format, the data that is returned will also be in UTC time format so please take note of this.

If you wanted to do a quick test without the client counterpart you can run your node server (node app.js) and your webserver, then browse to "https://localhost:8443/usage" (again, bypass the security message if you are using a self-signed cert) to see the results.

Next, we can create our client html code that will do the actual call to our nodejs server using Javascript and the built in "fetch" call.

First open your editor and create a blank html file, save it as index.html. In your editor add the following bit of code into your index.html file.

<!DOCTYPE html>  
<html lang="en">  
<head>  
  <meta charset="UTF-8">  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  <title>Document</title>  
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>  
</head>  
<body>  
  <button id="fetchButton">Get Account Usage</button>  
  <div id="chart_div" style="width: 100%; height: 500px;"></div>  
  <script type="text/javascript">  

    const btn = document.getElementById('fetchButton');  

    var months = ['January','February','March','April','May','June','July','August','September','October','November','December'];  
    var bwData = {};  

    btn.addEventListener('click', e => {  
      const url = **'https://localhost:8443/usage'**;  
      console.log('fetching data:',url);  
      **fetch**(url)  
        .then( resp =>{  
          return resp.json();  
        }).then( o => {  
          bwData = o.data.bandwidth;  
          google.charts.load('current', {'packages':['corechart']});  
          google.charts.setOnLoadCallback(drawBandwidthChart);  
        }).catch( e => {  
          console.log('error: ',e);  
        });  
    })  

    function drawBandwidthChart(){  
      console.log('bw',bwData);  
      // table headers  
      let o = [ ['Month', 'bytesIn', 'bytesOut'] ];  
      let n = 1;//shift array position to next slot  
      for(let i in bwData){  
        let mth = new Date(i).**getUTCMonth()**;  
        let bw = bwData[i];  
        o[n++] = [months[mth], bw.bytesIn, bw.bytesOut];  
      }  
      console.log('sorted table:',o);  
      let data = google.visualization.arrayToDataTable(o);  

      let opt = {  
        title: 'Dolby.io Real-time Streaming Usage',  
        hAxis: {title: 'Month', titleTextStyle: {color: '#333'}},  
        vAxis: {minValue: 0}  
      };  

      let chart = new google.visualization.AreaChart(document.getElementById('chart_div'));  
      chart.draw(data, opt);  
    }  

  </script>  
</body>  
</html>

On the client end, there is only a simple button to fetch the data using the dates specified on the server. You can design the user application as advanced as you want, however, for the sake of simplicity we are just doing a simple request here to show its function.

460460

A simple click on the button will call our Nodejs server counterpart using the "fetch" command. Once the data from the API is received on the client JavaScript, we convert the data back into JSON and then sort it in a table that Google Charts can understand and display.

Remember the data is in UTC format which a simple Date object can accept, however, calling "getMonth" from the date object will result in a date that is based on your local time, in the case above we’ve kept it UTC time so that there is no confusion.

Once the data is sorted and the chart is created, you can push the table data to the Google Chart to display it visually.

10431043

You can add the other API calls in the Nodejs side to load more information about your usage. You could also provide a HTML form to allow the user to specify query dates to send up to Nodejs, just remember to format the date information to UTC before you query it.

See more API calls here: Account Analytics

Bandwidth Per Stream

The Analytics API allows you to not only get the bandwidth you need from your account itself but also from each individual stream. These calls follow the same rules as the call structure explained above (Basic Call Structure).

There is one slight difference we have to be aware of. For the fact that we are requesting data for a stream, in the query it is necessary to include the stream names along with the start and stop times. The use of multiple stream names is also supported. In order to use this feature you simply add each stream to a "streamNames" variable in the query, as an example your query stream might look like this:
startDate=2020-01-01&stopDate=2020-04-01&streamNames=mystream&streamNames=otherstream
You can add up to 10 stream names at a time.

To begin, below is the new modified app.js code from the example above, that supports a request for the stream data. The calls are modified to accept incoming data from the client side, making our data gathering more dynamic.

const express = require('express'); const https = require('https');  
const fs = require('fs');  

const port = 8443;  
const token = 'c0bda74e6701f9b23aec4451225b6bf9b1cfdc1aaeed5a4db38f538d2dd';  
const app = express();  

app.use(express.urlencoded({extended: false}));  
app.use( (req, resp, next) => {  
  resp.setHeader('Access-Control-Allow-Origin', '*');  
  next();  
});  

app.get( '/usage', (req, resp, next) => {  
  console.log('/usage - time start:',req.query.start,' stop:',req.query.stop);  
  let start = req.query.start;  
  let stop = req.query.stop;//must be max 90 day UTC window form today for monthly or 7 days for daily/hourly.  
  let o = {  
    hostname: 'api.millicast.com',  
    path: '/api/analytics/account/series?startDate='+start+'&stopDate='+stop+'&resolution=Month',  
    method: 'GET',  
    headers: {  
      'Authorization': 'Bearer ' + token,  
      'Content-Type': 'application/json'  
    }  
  }  
  //call api  
  let apiReq = https.request(o, res => {  
    console.log('result:',res.statusCode);  
    let body = '';  
    res.on('data', d => {  
      body = body + d;  
    })  
    res.on('end', () => {  
      let s = JSON.parse(body);  
      resp.send(body);  
    })  
  })  
  .on('error', e => {  
    console.log('ERROR',e);  
    resp.satus(404).json(e);  
  });  
  apiReq.end();  
});  

app.**get( '/streamusage'**, (req, resp, next) => {  
  console.log('/streamusage - streamName:',req.query.streamNames,'time start:',req.query.start,' stop:',req.query.stop);  
  //Millicast API request details  
  let **streamNames** = req.query.streamNames;  
  let start = dreq.query.start;  
  let stop = req.query.stop;//must be max 90 day UTC window form today for monthly or 7 days for the daily/hourly data.  
  let o = {  
    hostname: 'api.millicast.com',  
    path: '/api/analytics/streams/series?startDate='+start+'&stopDate='+stop+'&streamNames='+streamNames+'&resolution=Day',  
    method: 'GET',  
    headers: {  
      'Authorization': 'Bearer '+token,  
      'Content-Type': 'application/json'  
    }  
  }  
  console.log('app.get ',o);  
  //call api  
  let apiReq = https.request( o, res => {  
    console.log('result:',res.statusCode);  
  let body = '';  
    res.on('data', d => {  
      body = body + d;  
    })  
    res.on('end', () => {  
      let s = JSON.parse(body);  
      console.log('END:',s);  
      resp.send(body);  
    })  
  })  
  .on('error', e => {  
    console.log('ERROR',e);  
    resp.satus(404).json(e);  
  });  
  apiReq.end();  
});  

https.createServer(  
  {key: fs.readFileSync('ssl/key.pem'),  
    cert: fs.readFileSync('ssl/cert.pem')},  
  app )  
.listen(port);  

console.log('running! see port https://localhost:'+port+'/');

We’ve changed the original "/usage" call to get its start and stop time from the request (req) argument instead of a global variable. With that, we moved the call options object into the method get '/usage' itself in order to update every time new dates are sent by the client.

The new method, labeled '/streamusage' is what securely handles the call to the API for our client code, which is also updated. The call basically mirrors the original one above it, however we use a "streamNames" variable to handle the requirement needed for querying streams. You can definitely join these into 1 method but for the example we'll keep them separated, feel free to modify as needed.

Notice, in the method, where we receive the start, stop and streamName strings, these variables are to be sent by our client code so they must match.

Below is a sample of the new client index.html code.

<!DOCTYPE html>  
<html lang="en">  
<head>  
  <meta charset="UTF-8">  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  <title>Document</title>  
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>  
  <style>  
    #forms {  
      display: flex;  
    }  
    .m-r-20 {  
      margin-right: 20px;  
    }  
  </style>  
</head>  
<body>  
  <div id="forms">  
    <div class="m-r-20">  
      <h3>Stream </h3>  
      <div>  
        <p>Stream Name/s:</p>  
        <input id="strmNameTxt" type="text" value="myfeed1">  
      </div>  
      <div>  
        <p>Start UTC:</p>  
        <input id="startTxt" type="text" value="2021-02-01">  
      </div>  
      <div>  
        <p>Stop UTC:</p>  
        <input id="stopTxt" type="text" value="2021-02-13">  
      </div>  
      <br/>  
      <button id="strmButton">Get Stream Usage</button>  
    </div>  
    <div>  
      <h3>Account </h3>  
      <div>  
        <p>Start UTC:</p>  
        <input id="accStartTxt" type="text" value="2020-10-01">  
      </div>  
      <div>  
        <p>Stop UTC:</p>  
        <input id="accStopTxt" type="text" value="2021-02-13">  
      </div>  
      <br/>  
      <button id="acctButton">Get Account Usage</button>  
    </div>  
  </div>  
  <hr/>  

  <div id="chart_div" style="width: 100%; height: 500px;"></div>  

  <script type="text/javascript">  

    const btnAccnt = document.getElementById('acctButton');  
    const btnStrm = document.getElementById('strmButton');  
    const txtStart = document.getElementById('startTxt');  
    const txtStop = document.getElementById('stopTxt');  
    const txtStrmName = document.getElementById('strmNameTxt');  

    var months = ['January','February','March','April','May','June','July','August','September','October','November','December'];  
    var acctBWData = {};  
    var strmBWData = {};  
    var selStreamName = '';  

    btnAccnt.addEventListener('click', e => {  
      let startDt = accStartTxt.value;  
      let stopDt = accStopTxt.value;  
      const url = 'https://localhost:8443/usage?start='+startDt+'&stop='+stopDt;  
      console.log('fetching data:',url);  
      fetch(url)  
        .then( resp =>{  
          return resp.json();  
        }).then( o => {  
          acctBWData = o.data.bandwidth;  
          google.charts.load('current', {'packages':['corechart']});  
          google.charts.setOnLoadCallback(drawAcctBWChart);  
        }).catch( e => {  
          console.log('error: ',e);  
        });  
    })  

    btnStrm.addEventListener('click', e => {  
      //get user input data  
      selStreamName = txtStrmName.value;  
      let startDt = txtStart.value;  
      let stopDt = txtStop.value;  
      const url = 'https://localhost:8443/streamusage?streamNames='+selStreamName+'&start='+startDt+'&stop='+stopDt;  
      console.log('fetching data at:',url);  
      fetch(url)  
        .then( resp =>{  
          return resp.json();  
        }).then( o => {  
          console.log('o ',o);  
          let d = o.data[selStreamName];  
          strmBWData = !!d ? d.bandwidth : {};  
          google.charts.load('current', {'packages':['corechart']});  
          google.charts.setOnLoadCallback(drawStrmBWChart);  
        }).catch( e => {  
          console.log('error: ',e);  
        });  
    })  

    function drawAcctBWChart(){  
      console.log('drawAcctBWChart',acctBWData);  
      // table headers  
      let o = [ ['Month', 'bytesIn', 'bytesOut'] ];  
      let n = 1;//shift array position to next slot  
      for(let i in acctBWData){  
        let mth = new Date(i).getUTCMonth();  
        let bw = acctBWData[i];  
        o[n++] = [months[mth], bw.bytesIn, bw.bytesOut];  
      }  
      console.log('sorted table:',o);  
      let data = google.visualization.arrayToDataTable(o);  

      let opt = {  
        title: 'Millicast Account Usage',  
        hAxis: {title: 'Month', titleTextStyle: {color: '#333'}},  
        vAxis: {minValue: 0}  
      };  

      let chart = new google.visualization.AreaChart(document.getElementById('chart_div'));  
      chart.draw(data, opt);  
    }  

    function **drawStrmBWChart()**{  
      console.log('drawStrmBWChart',strmBWData);  
      // table headers  
      let o = [ ['Day', 'bytesIn', 'bytesOut'] ];  
      let n = 1;//shift array position to next slot  
      for(let i in strmBWData){  
        let mth = new Date(i).getUTCMonth();  
        let dy = new Date(i).getUTCDate();  
        let bw = strmBWData[i];  
        o[n++] = [(mth+1)+'/'+dy, bw.bytesIn, bw.bytesOut];  
      }  
      console.log('sorted table:',o);  
      let data = google.visualization.arrayToDataTable(o);  

      let opt = {  
        title: 'Millicast '+selStreamName+' Usage',  
        hAxis: {title: 'Days', titleTextStyle: {color: '#333'}},  
        vAxis: {minValue: 0}  
      };  

      let chart = new google.visualization.AreaChart(document.getElementById('chart_div'));  
      chart.draw(data, opt);  
    }  

  </script>  
</body>  
</html>

The new index.html file has been modified to be more dynamic and add more interactivity than the original one. In this example, we’ve added some rudimentary forms for each call to send the dates and stream names to the server. Remember to write your dates in UTC format (2021-02-01) so that our Nodejs server counterpart can send them successfully.

If you noticed in the new method "drawStrmBWChart", the sorting procedure is different than the one for account bandwidth. The data is similar but formatted to separate not just the dates but the streams as well. Here, we also use a "Day" format for daily breakdown instead of a monthly one. This format breaks down the data for daily totals instead of monthly.

The Google chart is updated accordingly and we can see each visual as we switch it out by pressing the corresponding buttons and adding the dates.

19991999

Notice, the data sent to the server is in the same format that is expected on the methods we added to the app.js file earlier. You can try another method using the Geo calls, just remember to format accordingly.

This concludes our tutorial on the Analytics API. For additional questions feel free to open an intercom chat with us.

GraphQL API

Dolby.io Real-time Streaming has added support for GraphQL to help the users that require a little more control over how the data is received in their custom applications and workflows.

To learn more about GraphicQL you can follow this link here https://www.graphql.com/.

To get started, you will need the URL entrypoint for the GraphQL API which will allow you to query both stats and real time info of the service.

https://api.millicast.com/graphql

Like the Analytics API above the GraphQL API requires you to have a proper authentication token to query the data. To learn more on how to get your API token, please read the following article on Acquiring Your API Token.

Once you have acquired your token, you will need to add it to your API requests using a bearer Authorization request header. However, even though we have temporarily enabled the GraphicQL UI to perform queries directly via the same url, it does not include the option to append the Authorization header to the requests. In this case you would need to use a browser extension for including it, like this one https://bewisse.com/modheader/.

16481648

With this extension you will be able to query easily from the browser.

These are the entities that are currently available to you in the query:

  • Feeds - Information about a publication for a stream by a broadcaster.
  • Inactive - Information about a stream view by a viewer.

NOTE:
The detailed information will be accessible for only 30 days after the broadcast has ended and the number of records returned are limited in its detail. See the Access Restriction section above to learn more about the analytic limitations.

You can also query for live aggregated stats, each one aggregated in 5 minute intervals.

Here is what is available to you in the query:

  • StreamStats - Aggregated stats in 5 minutes intervals for each stream (started views, ended views, active views, duration of the views).
  • AccountStats - Aggregated stats in 5 minutes intervals for the global account.

The stats will be available without any time limit, so you can perform historical queries. Below is an example on how to do this:

To find the current active publications you can perform the following query:

{  
  feedFindMany(filter: {active: true}) {  
    started  
    active  
    ended  
    streamId  
  }  
}

Be advised that for running a query between two dates, you will need to use the “_operators” keyword.

Here is an example to count the number of viewers between two dates:

{  
  streamViewCount(filter:{  
    active: false,  
    streamId: "accoutId/streamName",  
    _operators:{  
      started:{  
        gt:"2021-09-28T02:00:00Z",  
        lt: "2021-09-28T03:00:00Z"  
      }  
    }  
  })  
}

For additional questions feel free to open an intercom chat with us.


Did this page help you?