Quora Ads Google Sheet Automated Reports

Overview

Supermetrics For Google Sheets connector for Quora Ads is a bit rough. So I set up a script that can be added to a Google Sheet to import data from Quora Ads and drop it directly into a Google Sheet.

Instructions

https://script.google.com/macros/d/SCRIPT_ID/usercallback


Script

function mainDataImportScript() {

 var m=0;

 while (m<3){

   try{

     //query params

     var today = new Date();

     var day = 1000*60*60*24;

     var endDate = new Date(today.getTime() - (1*day));

     var endDateFormat = Utilities.formatDate(endDate, 'GMT-5', 'yyyy-MM-dd');

     Logger.log(endDateFormat);

     var startDate = new Date(today.getTime() - (7*day));

     var startDateFormat = Utilities.formatDate(startDate, 'GMT-5', 'yyyy-MM-dd');

     Logger.log(startDateFormat);

     var fields_variable = 'accountId,accountName,adId,adName,landingURL,impressions,clicks,spend';


     //technical params

     var page_limit = 25;

     var accountList = ['XXXXXXXXXXXXXXX','YYYYYYYYYYYYYYY'];

     var access_token = generateAuthToken();

     Logger.log('Access Token = '+access_token);


     var myHeaders = {

       "Authorization": "Bearer "+access_token,

       "Cookie": "m-ans_frontend_early_version=d2c38ecc12a5037e; m-b=S-7Q9szJjszBS0glqUAEyA==; m-b_lax=S-7Q9szJjszBS0glqUAEyA==; m-b_strict=S-7Q9szJjszBS0glqUAEyA==; m-css_v=main-7d0dbdebc84e4c04; m-early_v=8f7d03da0b7a24ae; m-lat=pcu1540tbbDWt5hPkguMog==; m-login=1; m-s=BRTm4MjPo7a-mYZMwMM8SQ==; m-theme=light; m-tz=300; m-uid=1389034950"

     }

    

     var requestOptions = {

       method: 'GET',

       headers: myHeaders,

       followRedirects : true,

     };


     var numAccounts = accountList.length;

     Logger.log(numAccounts+' accounts found.');


     //establishes sheet

       var sheet = SpreadsheetApp.getActive().getSheetByName('Data Import');

     //builds array for fields

     var fields_array = fields_variable.split(",");

     //sets up array to build

       var rows = [],

           data;


     for (l = 0; l < numAccounts; l ++){

       var activeAccountId = accountList[l];

       Logger.log(activeAccountId);

       //get pagination values for iterative loop

       var initOutput = UrlFetchApp.fetch("https://api.quora.com/ads/v0/accounts/"+activeAccountId+"?level=AD&fields="+fields_variable+"&sort=spend&order=DESCENDING&startDate="+startDateFormat+"&endDate="+endDateFormat+"&offset=0", requestOptions);

       var initContent = initOutput.getContentText();

       var initJson = JSON.parse(initContent);


       try{

         var totalAds = initJson.paging.totalResults;

       }

       catch(e){

         var totalAds = initJson.data.length;

       }

       Logger.log(totalAds);


       for (k = 0; k < totalAds; k += page_limit){

         var output = UrlFetchApp.fetch("https://api.quora.com/ads/v0/accounts/"+activeAccountId+"?level=AD&fields="+fields_variable+"&sort=spend&order=DESCENDING&startDate="+startDateFormat+"&endDate="+endDateFormat+"&offset="+k, requestOptions);

         var content = output.getContentText();

         var json = JSON.parse(content);

         var ads = json.data;

        

         for (i = 0; i < ads.length; i++) {

           data = ads[i];

           var columns = [];

           //builds out array: columns

           for (j = 0; j < fields_array.length; j++) {

             if (fields_array[j] == 'spend'){

               columns.push((data[fields_array[j]]/10000));

             }

             else{

               columns.push(data[fields_array[j]]);

             }

           }

           rows.push(columns);

         }

       }

     }

    

     //clear import sheet

     var clearRange = sheet.getRange(2, 1, 1000, fields_array.length);

     clearRange.clearContent();

    

     //reset headers

     sheet.getRange('1:1').clearContent();

     for (z=0; z<fields_array.length; z++){

       sheet.getRange(1,z+1,1,1).setValue(fields_array[z]);

     }

    

     //log data

     if(rows.length > 0) {

       var dataRange = sheet.getRange(2, 1, rows.length, fields_array.length);

       dataRange.setValues(rows);

       sheet.getRange('I1').setValue(startDate);

     }

     else {

       Logger.log('No data available.');

       SpreadsheetApp.getActiveSpreadsheet().toast('No data available.','Error!');

     }

     break;

   }

   catch(e) {

     Utilities.sleep(5000);

     m++;

   }

 }

}


function onOpen() {

 var ui = SpreadsheetApp.getUi();

 ui.createMenu('Sheet Controls')

     .addItem('Import Quora Ads Data', 'mainDataImportScript')

     .addToUi();

}


/**

* This script allows the stepping through of the Authorization Code Grant in

* order to obtain a refresh token.

*

* This script uses the out-of-band redirect URI, which is not part of the

* OAuth2 standard, to allow not redirecting the user. If this does not work

* with your API, try instead the OAuth playground:

* https://developers.google.com/oauthplayground/

*

* Execute script twice:

* Execution 1: will result in a URL, which when placed in the browser will

* issue a code.

* Execution 2: place the code in "CODE" below and execute. If successful a

* refresh token will be printed to the console.

*/

var CLIENT_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

var CLIENT_SECRET = 'XXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXX';


var SCOPES = ['ads_read'];


var AUTH_URL = 'https://www.quora.com/_/oauth/authorize';

var TOKEN_URL = 'https://www.quora.com/_/oauth/token';


// After execution 1, enter the OAuth code inside the quotes below:

var CODE = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';


function main() {

 if (CODE) {

   generateRefreshToken();

 } else {

   generateAuthUrl();

 }

}


/**

* Creates the URL for pasting in the browser, which will generate the code

* to be placed in the CODE variable.

*/

function generateAuthUrl() {

 var payload = {

   scope: SCOPES.join(' '),

   // Specify that no redirection should take place

   // This is Google-specific and not part of the OAuth2 specification.

   redirect_uri: 'https://script.google.com/macros/d/1fuyXFMAbtCIBNsZYNdTYdFvwjqtVNIbyvPZKI4rYCG7zKTXJVrzrsNRy/usercallback',

   response_type: 'code',

   access_type: 'offline',

   client_id: CLIENT_ID

 };

 var options = {payload: payload};

 var request = UrlFetchApp.getRequest(AUTH_URL, options);

 Logger.log(

     'Browse to the following URL: \n' + AUTH_URL + '?' + request.payload);

}


/**

* Generates a refresh token given the authorization code.

*/

function generateRefreshToken() {

 var payload = {

   code: CODE,

   client_id: CLIENT_ID,

   client_secret: CLIENT_SECRET,

   // Specify that no redirection should take place

   // This is Google-specific and not part of the OAuth2 specification.

   redirect_uri: 'https://script.google.com/macros/d/1fuyXFMAbtCIBNsZYNdTYdFvwjqtVNIbyvPZKI4rYCG7zKTXJVrzrsNRy/usercallback',

   grant_type: 'authorization_code'

 };

 var options = {method: 'POST', payload: payload};

 var response = UrlFetchApp.fetch(TOKEN_URL, options);

 var data = JSON.parse(response.getContentText());

 Logger.log(data);

 if (data.refresh_token) {

   var msg = 'Success! Refresh token: ' + data.refresh_token +

     '\n\nThe following may also be a useful format for pasting into your ' +

     'script:\n\n' +

     'var CLIENT_ID = \'' + CLIENT_ID + '\';\n' +

     'var CLIENT_SECRET = \'' + CLIENT_SECRET + '\';\n' +

     'var REFRESH_TOKEN = \'' + data.refresh_token + '\';';

   Logger.log(msg);

 } else {

   Logger.log(

       'Error, failed to generate Refresh token: ' +

       response.getContentText());

 }

}


function generateAuthToken() {

 var clientId = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

 var clientSecret = 'XXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXX';

 var refreshToken = 'GET_FROM_CODE_IN_PREVIOUS_FUNCTION';

 var grantType = 'refresh_token';

 var tokenURL = 'https://www.quora.com/_/oauth/token';


 var payload = {

   refresh_token: refreshToken,

   client_id: clientId,

   client_secret: clientSecret,

   redirect_uri: 'https://script.google.com/macros/d/1fuyXFMAbtCIBNsZYNdTYdFvwjqtVNIbyvPZKI4rYCG7zKTXJVrzrsNRy/usercallback',

   grant_type: 'refresh_token'

 };


 var options = {method: 'POST', payload: payload};

 var response = UrlFetchApp.fetch(tokenURL, options);

 var data = JSON.parse(response.getContentText());

 Logger.log(data);

 return data.access_token;

}

Further Data Segmentation

Monthly Quora Ads Reporting

function mainMonthlyDataImportScript() {

 var m=0;

 while (m<3){

   try{

     //query params

     var fields_variable = 'accountName,campaignName,impressions,clicks,spend,conversions';


     //technical params

     var page_limit = 25;

     var accountList = ['XXXXXXXXXXXXXXX'];

     var access_token = generateAuthToken();

     Logger.log('Access Token = '+access_token);


     var myHeaders = {

       "Authorization": "Bearer "+access_token,

       "Cookie": "m-ans_frontend_early_version=d2c38ecc12a5037e; m-b=S-7Q9szJjszBS0glqUAEyA==; m-b_lax=S-7Q9szJjszBS0glqUAEyA==; m-b_strict=S-7Q9szJjszBS0glqUAEyA==; m-css_v=main-7d0dbdebc84e4c04; m-early_v=8f7d03da0b7a24ae; m-lat=pcu1540tbbDWt5hPkguMog==; m-login=1; m-s=BRTm4MjPo7a-mYZMwMM8SQ==; m-theme=light; m-tz=300; m-uid=1389034950"

     }

    

     var requestOptions = {

       method: 'GET',

       headers: myHeaders,

       followRedirects : true,

     };


     var numAccounts = accountList.length;

     Logger.log(numAccounts+' account(s) found.');


     //establishes sheet

     var sheet = SpreadsheetApp.getActive().getSheetByName('Monthly Data Import');

     //builds array for fields

     var fields_array = fields_variable.split(",");

     //sets up array to build

       var rows = [],

           data;


     //date params

     var today = new Date();

     var currentMonth = parseInt(Utilities.formatDate(today,'GMT-4','MM'));

     Logger.log('Current Month = '+currentMonth);


     //***LOOP 1*** iterate over months to today since 1/1/21 (var startDate below based on l=0)

     for (l = 0; l < currentMonth; l++){

       //establish blank array to aggregate data for each month. at end of each loop append this month's date and push to rows array above.

       var monthIteratorArray = [];

      

       //establish date variables

       startDate = new Date(2021,l,1);

       nextMonthStart = new Date(2021,l+1,1);

       thisMonthEnd = new Date(nextMonthStart - (1000*60*60*24));

       if (l == currentMonth-1){

         Logger.log('Current Month in For Loop = '+l);

         endDate = today;

       }

       else {

         endDate = thisMonthEnd;

       }

       startDateFormat = Utilities.formatDate(startDate,'GMT-4','yyyy-MM-dd');

       endDateFormat = Utilities.formatDate(endDate,'GMT-4','yyyy-MM-dd');


       //***LOOP 2*** iterate over number of accounts in accountList variable

       for (k = 0; k < numAccounts; k ++){

         var activeAccountId = accountList[k];

         Logger.log(activeAccountId);

         //get pagination values for iterative loop

         var initOutput = UrlFetchApp.fetch("https://api.quora.com/ads/v0/accounts/"+activeAccountId+"?level=CAMPAIGN&fields="+fields_variable+"&sort=spend&order=DESCENDING&startDate="+startDateFormat+"&endDate="+endDateFormat+"&offset=0", requestOptions);

         var initContent = initOutput.getContentText();

         var initJson = JSON.parse(initContent);

         try{

           var totalAds = initJson.paging.totalResults;

         }

         catch(e){

           var totalAds = initJson.data.length;

         }

        

         Logger.log(totalAds);


         //***LOOP 3*** loop over number of pages in results JSON data

         for (j = 0; j < totalAds; j += page_limit){

           var output = UrlFetchApp.fetch("https://api.quora.com/ads/v0/accounts/"+activeAccountId+"?level=CAMPAIGN&fields="+fields_variable+"&sort=spend&order=DESCENDING&startDate="+startDateFormat+"&endDate="+endDateFormat+"&offset="+j, requestOptions);

           var content = output.getContentText();

           var json = JSON.parse(content);

           var ads = json.data;

          

           //***LOOP 4*** loop over all ads on each page

           for (i = 0; i < ads.length; i++) {

             data = ads[i];

             var columns = [];

             //***LOOP 5*** builds out array: columns

             for (h = 0; h < fields_array.length; h++) {

               if (fields_array[h] == 'spend'){

                 columns.push((data[fields_array[h]]/10000));

               }

               else{

                 columns.push(data[fields_array[h]]);

               }

             }

             columns.push(startDateFormat, endDateFormat);

             monthIteratorArray.push(columns);

           }

         }

       }

       //concat contents of monthIteratorArray to rows and end loop

       rows = rows.concat(monthIteratorArray);

     }


     fields_array.push("Start Date","End Date");

    

     //clear import sheet

     var clearRange = sheet.getRange(2, 1, rows.length, fields_array.length);

     clearRange.clearContent();

    

     //reset headers

     sheet.getRange('1:1').clearContent();

     for (z=0; z<fields_array.length; z++){

       sheet.getRange(1,z+1,1,1).setValue(fields_array[z]);

     }

    

     //log data

     if(rows.length > 0) {

       var dataRange = sheet.getRange(2, 1, rows.length, fields_array.length);

       dataRange.setValues(rows);

     }

     else {

       Logger.log('No data available.');

       SpreadsheetApp.getActiveSpreadsheet().toast('No data available.','Error!');

     }

   break;

   }

   catch(e) {

     Utilities.sleep(5000);

     m++;

   }

 }

}


Weekly Quora Ads Reporting

function mainWeeklyDataImportScript() {

 var m=0;

 while (m<3){

   try

     //query params

     var fields_variable = 'accountName,adName,impressions,clicks,spend,conversions';


     //technical params

     var page_limit = 25;

     var accountList = ['XXXXXXXXXXXXXXX'];

     var access_token = generateAuthToken();

     Logger.log('Access Token = '+access_token);


     var myHeaders = {

       "Authorization": "Bearer "+access_token,

       "Cookie": "m-ans_frontend_early_version=d2c38ecc12a5037e; m-b=S-7Q9szJjszBS0glqUAEyA==; m-b_lax=S-7Q9szJjszBS0glqUAEyA==; m-b_strict=S-7Q9szJjszBS0glqUAEyA==; m-css_v=main-7d0dbdebc84e4c04; m-early_v=8f7d03da0b7a24ae; m-lat=pcu1540tbbDWt5hPkguMog==; m-login=1; m-s=BRTm4MjPo7a-mYZMwMM8SQ==; m-theme=light; m-tz=300; m-uid=1389034950"

     }

    

     var requestOptions = {

       method: 'GET',

       headers: myHeaders,

       followRedirects : true,

     };


     var numAccounts = accountList.length;

     Logger.log(numAccounts+' account(s) found.');


     //establishes sheet

     var sheet = SpreadsheetApp.getActive().getSheetByName('Weekly Data Import');

     //builds array for fields

     var fields_array = fields_variable.split(",");

     //sets up array to build

       var rows = [],

           data;


     //date params

     var today = new Date();

     //firstWeekDate = 1/4/21 (first monday of podcasts is 11/2/2020)

     var firstWeekDate = new Date(2020,10,2);

     var dateDif = (today-firstWeekDate)/1000/60/60/24;

     var weekDif = Math.ceil(dateDif/7);

     Logger.log('Days since '+firstWeekDate+' = '+dateDif+'; weeks since start date = '+weekDif);


     //***LOOP 1*** iterate over months to today since 11/2/20 (var firstWeekDate above based on l=0)

     for (l = 0; l < weekDif; l++){

       //establish blank array to aggregate data for each month. at end of each loop append this month's date and push to rows array above.

       var weekIteratorArray = [];

       //establish date variables

       daysAdded = 1000*60*60*24;

       Logger.log(l+' weeks added')

       startDate = new Date(firstWeekDate.getTime() + 7 * l * daysAdded);

       weeksEnd = new Date((startDate.getTime() + 7 * daysAdded) - daysAdded);

       if (today < weeksEnd){

         endDate = today;

       }

       else {

         endDate = weeksEnd;

       }

       startDateFormat = Utilities.formatDate(startDate,'GMT-4','yyyy-MM-dd');

       Logger.log(startDateFormat);

       endDateFormat = Utilities.formatDate(endDate,'GMT-4','yyyy-MM-dd');

       Logger.log(endDateFormat);


       //***LOOP 2*** iterate over number of accounts in accountList variable

       for (k = 0; k < numAccounts; k ++){

         var activeAccountId = accountList[k];

         Logger.log(activeAccountId);

         //get pagination values for iterative loop

         var initOutput = UrlFetchApp.fetch("https://api.quora.com/ads/v0/accounts/"+activeAccountId+"?level=AD&fields="+fields_variable+"&sort=spend&order=DESCENDING&startDate="+startDateFormat+"&endDate="+endDateFormat+"&offset=0", requestOptions);

         var initContent = initOutput.getContentText();

         var initJson = JSON.parse(initContent);

         try{

           var totalAds = initJson.paging.totalResults;

         }

         catch(e){

           var totalAds = initJson.data.length;

         }

        

         Logger.log(totalAds);


         //***LOOP 3*** loop over number of pages in results JSON data

         for (j = 0; j < totalAds; j += page_limit){

           var output = UrlFetchApp.fetch("https://api.quora.com/ads/v0/accounts/"+activeAccountId+"?level=AD&fields="+fields_variable+"&sort=spend&order=DESCENDING&startDate="+startDateFormat+"&endDate="+endDateFormat+"&offset="+j, requestOptions);

           var content = output.getContentText();

           var json = JSON.parse(content);

           var ads = json.data;

          

           //***LOOP 4*** loop over all ads on each page

           for (i = 0; i < ads.length; i++) {

             data = ads[i];

             var columns = [];

             //***LOOP 5*** builds out array: columns

             if(data['adName'].indexOf("Podcast")>-1 && data['impressions']>0){

               for (h = 0; h < fields_array.length; h++) {

                 if (fields_array[h] == 'spend'){

                   columns.push((data[fields_array[h]]/10000));

                 }

                 else{

                   columns.push(data[fields_array[h]]);

                 }

               }

             }

             else{

               continue;

             }

             columns.push(startDateFormat, endDateFormat);

             weekIteratorArray.push(columns);

           }

         }

       }

       //concat contents of weekIteratorArray to rows and end loop

       rows = rows.concat(weekIteratorArray);

     }


     fields_array.push("Start Date","End Date");

    

     //clear import sheet

     var clearRange = sheet.getRange(2, 1, rows.length, fields_array.length);

     clearRange.clearContent();

    

     //reset headers

     sheet.getRange('1:1').clearContent();

     for (z=0; z<fields_array.length; z++){

       sheet.getRange(1,z+1,1,1).setValue(fields_array[z]);

     }

    

     //log data

     if(rows.length > 0) {

       var dataRange = sheet.getRange(2, 1, rows.length, fields_array.length);

       dataRange.setValues(rows);

     }

     else {

       Logger.log('No data available.');

       SpreadsheetApp.getActiveSpreadsheet().toast('No data available.','Error!');

     }

     break;

   }

   catch(e) {

     Utilities.sleep(5000);

     m++;

   }

 }

}