Google Sheets

How to integrate Scope3 data with a Google Sheet

Installation

In your Google sheet, go to Extensions->Apps Script

Paste in the script(s) that you want to use and hit Save

Go back to your spreadsheet and set up the fields you need. Here's an example for creative API. Note the formula in the calculation cell.

Here's an example for campaign API including the formula:

Apps Script for Campaign API

This script acts on an array of domains, so you only need to put the calculation formula in one cell.

/**
 * Model the carbon footprint of an advertising campaign using the Scope3 API.
 * 
 * @param string accessClientID the access client ID from Scope3
 * @param string accessClientSecret the access client token from Scope3
 * @param string format the format (video, audio, display, text) of the primary asset of the creative.
 * @param number payloadSize the size of the creative. Set to 0 for a streaming video
 * @param number durationSeconds the duration of the creative (video only)
 * @param deviceType string the device type: tv, phone, pc, tablet
 * @param network string the network type: fixed, mobile
 * @param country string the two-letter code for the country
 * @param seller string the seller of the inventory (SSP or publisher)
 * @param buyingMethod string the buying method: direct, programmatic-pmp, programmatic-open
 * @param Array<string> domains An array of domains
 * @return ad selection emissions, media distribution emissions, creative distribution emissions, supply directness, gmp eligibility
 * @customfunction
 */
function getCampaignCarbonFootprint1_2(accessClientID, accessClientSecret, format, payloadSize, durationSeconds, deviceType, network, country, seller, buyingMethod, domains) {
  var url = 'https://api.scope3.com/v1/calculate/daily?includeRows=true&previewMethodology=true'
  var yesterday = new Date(Date.now() - 86400000);
  var data = {
    rows: domains.filter(Boolean).map(domain => ({date: yesterday.toISOString().split('T')[0], network, country, seller, buyingMethod, deviceType, site: {domain: domain[0]}, creative: {format, payloadSize, durationSeconds}, impressions: 1000}))
  }

  Logger.log(JSON.stringify(data));
var options = {
  'method' : 'post',
  'contentType': 'application/json',
  'payload' : JSON.stringify(data),
  'headers': {accessClientID, accessClientSecret},
};
  var text = UrlFetchApp.fetch(url, options).getContentText()
  var json = JSON.parse(text)
  Logger.log(json);
  return json.rows.map(row => [row.adSelectionEmissions, row.mediaDistributionEmissions, row.creativeDistributionEmissions, row.supplyDirectness, row.gmpEligible])

}

Apps Script for Creative API

This script acts on a single row, so you will need a calculation call for each row. Happy to make a bulk version if there is interest!

/**
 * Model the carbon footprint of an advertising creative using the Scope3 API.
 *
 * @param {date} date the date the creative was served. Must be prior to today to get accurate grid mix data.
 * @param {string} format the format of the creative. Supports "video", "text", or "banner".
 * @param {number} duration the duration of the creative (video only)
 * @param {number} payloadSize the size of the payload delivered by the creative (banner only)
 * @param {string} network the network where the creative was served. Supports 'fixed' or 'mobile'.
 * @param {string} deviceType the consumer device where the creative was served. Supports 'pc, tablet, phone, or tv'.
 * @param {string} country the country where the creative was served. Use the two-letter country code.
 * @param {string} region the state or province where the creative was served. Use the two or three letter code. Only supported in US, CA, and AU.
 * @param {impressions} impressions the number of times the creative was served.
 * @return the carbon footprint of the creative
 * @customfunction
 */
function getCreativeCarbonFootprint1_2(date, format, duration, payloadSize, country, region, network, deviceType, impressions) {
  var url = 'https://api.scope3.com/v1/creative'
  var data = {date: date.toISOString().split('T')[0], format, country, impressions}
  if (format === 'video') {
    data.durationSeconds = duration
  }
  if (format === 'banner') {
    data.payloadSize = payloadSize
  }
  if (region) {
    data.region = region
  }
  if (network) {
    data.network = network
  }
  if (deviceType) {
    data.deviceType = deviceType
  }
  Logger.log(JSON.stringify(data));
var options = {
  'method' : 'post',
  'contentType': 'application/json',
  'payload' : JSON.stringify(data)
};
  var text = UrlFetchApp.fetch(url, options).getContentText()
  var json = JSON.parse(text)
  Logger.log(json);
  if ('message' in json) {
    return json.message
  } else if ('totalEmissions' in json) {
    return json.totalEmissions
  } else {
    return json
  }

}

Apps Script for Planning API

/**
 * Model the carbon footprint of a media plan using the Scope3 API. Currently ignores start date, end date, property list, and duration.
 * 
 * @param string accessClientID the access client ID from Scope3
 * @param string accessClientSecret the access client token from Scope3
 * @param bool previewMethodology whether to use the latest methodology
 * @param bool debug whether to include debug data
 * @param {Array<Array<string|number>>} sellers An array: Start date	End date	Country	Channel	Weighted Property List	Format	Duration	DeviceType	Impressions
 * @return ad selection emissions, media distribution emissions, creative distribution emissions
 * @customfunction
 */
function getMediaPlanFootprint(accessClientID, accessClientSecret, previewMethodology, debug, colRows) {
  var url = 'https://api.scope3.com/v1/planning?includeRows=true&previewMethodology=' + previewMethodology + '&debug=' + debug

  var data = {
    rows: colRows.filter(row => row[9] > 0).map(row => {
      var weightedSellers = [] 
      if (row[4]) {
        const sellers = row[4].split(',');
        sellers.forEach((s) => {
          const info = s.trim().split(':')
          weightedSellers.push({
            seller: info[0],
            weight:  parseFloat(info[1],10)
          })
        })
      }
      
      
      return ( 
        {
          country: row[2],
          channel: row[3],
          creative: {
            format: row[5],
            duration:  row[6] ? row[6] : null,
            payloadSize:  row[7] ? row[7] : null
          },
          deviceType: row[8],
          impressions: row[9],
          weightedSellerList: weightedSellers,
        }
      )
    })
  }

  Logger.log(JSON.stringify(data));
  var options = {
    'method' : 'post',
    'contentType': 'application/json',
    'payload' : JSON.stringify(data),
    'headers': {accessClientID, accessClientSecret},
  };
  var text = UrlFetchApp.fetch(url, options).getContentText()
  var json = JSON.parse(text)
  Logger.log(json);
  return json.rows.map(row => [row.totalEmissions, row.adSelectionMediaDistributionEmissions, row.creativeDistributionEmissions, row.debug, row.error])
}