How do I get Snipe-IT API to retrieve data using a Google App Script and populate it on a Google Sheet? - javascript

I am attempting to create a visualization of the data we have on Snipe-IT Asset Management on Google Data Studio. To do so, I am creating a Google Sheets spreadsheet with an App Script extension that will communicate with the Snipe-IT API, retrieve the data, and populate it on the Google Sheet. So far, I've been able to get the API to populate some of our data on the terminal but not on the spreadsheet itself.
I wrote a simple script that should list out the assets assigned to one particular user, for testing purposes. Again, the script works fine, it populates the data I need on the terminal but not on the Google Sheet. Here is my exact code (excluding SETUP section details):
serverURL = 'SERVER-URL'; (ignore)
apiKey = 'API-KEY' (ignore)
function onOpen(e) {
function createCommandsMenu() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Run Script')
.addItem('Get Assets By Department', 'runGetAssetsByDepartment')
function testGetAssetsByUser(){
//Get assets for a user by id
//Returns a list of assets by id
function getAssetsByUser(userID) {
var url = serverURL + 'api/v1/users/' + userID + '/assets';
var headers = {
"Authorization" : "Bearer " + apiKey
var options = {
"method" : "GET",
"contentType" : "application/json",
"headers" : headers
var response = JSON.parse(UrlFetchApp.fetch(url, options));
var rows = response.rows;
var assets = []
for (var i=0; i<rows.length; i++) {
var row = rows[i];
if ( == "Laptop" || == "Desktop" || == "2-in-1") {
var asset =
return assets

There are several ways to write values into an spreadsheet
The basics
Grab the spreadsheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
This works in bounded projects and add-ons
Grab the destination sheet
var sheet = spreadsheet.getSheetByName('put here the sheet name');
You might use SpreadsheetApp.getActiveSheet() if your spreadsheet has only one sheet, but for functions being called from a custom menu this one is risky.
Write values into the destination sheet
Preparation: Make a 2D Array
var output = => [v]);
Do: Write values into the destination sheet
sheet.getRange(1,1,output.length, 1).setValues(output);


Extend seach range for all sheets

I am using this script and an input form to search a Google spreadsheet, this script has a range of one sheet,i.e. Data , but I need to extend my range to all sheets in the same spreadsheet, any ideas?
function doGet(e) {
return HtmlService.createTemplateFromFile("Index").evaluate()
.setTitle("WebApp: Search By Password")
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
function processForm(formObject) {
var concat = formObject.searchtext + formObject.searchtext2;
var result = "";
if (concat) {//Execute if form passes search text
result = search(concat);
return result;
function search(searchtext) {
var spreadsheetId = ' '; //** CHANGE !!!!
var sheetName = "Data"
var range = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName).getDataRange();
var data = range.getValues();
var ar = [];
data.forEach(function (f) {
if (~[f[8]].indexOf(searchtext)) {
ar.push([f[2], f[3], f[4], f[5], f[6], f[7]]);
return ar;
I believe your goal is as follows.
You want to search for a text from all sheets of a Google Spreadsheet.
You want to achieve this by modifying the script in your question.
In this case, I would like to propose modifying the function search as follows.
Modified script:
function search(searchtext) {
return SpreadsheetApp
.flatMap(s =>
.filter(r => r[8] && r[8].includes(searchtext))
.map(([,,c,d,e,f,g,h]) => [c,d,e,f,g,h])
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".

Google sheets scripts function UrlFetchApp.fetch does not run from .onEdit(e) but works from editor

I have created a google sheet with a lot of info for a beach volleyball cup and I want to call an API I have created when a checkbox is checked in this sheet.
function onEdit(e){
const ui = SpreadsheetApp.getUi();
const spreadsheets = SpreadsheetApp.getActive();
const configSheet = spreadsheets.getSheetByName("Config")
var tourneyId = String(configSheet.getRange(2,4).getValue())
var tourneyTitle = String(configSheet.getRange(2,5).getValue())
var sheet = spreadsheets.getActiveSheet()
if (sheet.getName() == "LiveScore"){
var actRng = sheet.getActiveRange();
var editColumn = actRng.getColumn();
var rowIndex = actRng.getRowIndex();
actRng = actRng.getCell(1, 1);
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
if(editColumn == 7 && rowIndex != 1){
onStartBroadcastClicked(actRng, ui, sheet, rowIndex, editColumn, tourneyTitle);
There is never any problems with this part as I see it. But when i get into the function onStartBroadcastClicked:
function onStartBroadcastClicked(actRng, ui, sheet, rowIndex, editColumn, tourneyTitle){
var homeTeam = String(sheet.getRange(rowIndex, 14).getValue());
... // more setting variables
var endTime = new Date(startTime.getTime() + MILLIS_PER_MATCH);
var response = ui.alert("You are about to start a new broadcast. Are you sure?" +
"\n Title: " + title, ui.ButtonSet.YES_NO);
if (response == ui.Button.YES) {
var httpRequest = "https://someUrl";
var options =
'contentType': 'application/json',
'payload' : JSON.stringify({
"title" : title,
... // setting all variables
"description" : description
'muteHttpExceptions' : true,
'headers' : {
"Authorization": "Basic " + Utilities.base64Encode(USERNAME + ":" + PASSWORD)
var result = UrlFetchApp.fetch(httpRequest, options);
The issue is that it always gets to the line ui.alert("Waiting......."), but when triggered from the checkbox, it never succeeds the http POST request. If I click play inside the editor, it succeeds and I got the response in the alertbox.
Could it be some timeout or some autosave issues? Does anyone have any idea if where to keep looking? I've been stuck here for some time now and I would be really happy if anyone can point me to the correct direction.
The modification point of your issue is to use the installable trigger of OnEdit event. When the methods which are required to authorize used at the simple trigger, the error occurs. This situation makes us think that it seems the script doesn't work.
In order to avoid this error, please use the installable triggers of OnEdit event trigger.
As an important point, before you install the trigger, please rename the function name of onEdit() to other name. And install the renamed function name as the OnEdit event trigger. By this, the duplicate run of onEdit() can be prevented. If onEdit() function is installed as the installable trigger, when a cell is edited, the function is run 2 times. Ref.
By above settings, when the cell is edited, UrlFetchApp.fetch() works.
Simple Triggers
Installable Triggers
Asynchronous Processing using Event Triggers
I was able to get a script to work with a trigger if I created the script from and call the Google Sheet and tab from the script. I'm manually entered in my API calls per cell within a specified Row:
function fetchUrls() {
var spreadsheetId = "ENTER GOOGLE SHEET ID";
var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
var sheet = spreadsheet.getSheetByName("ENTER SHEET NAME");
var range = sheet.getRange("ENTER RANGE OR FULL COLUMN"); // specify the range of cells in column B
var urls = range.getValues(); // get the values of the cells and store them
in an array
var cache = CacheService.getScriptCache();
for (var i = 0; i < urls.length; i++) {
if(urls[i][0] != "") { // check if the current cell is not empty
var url = urls[i][0];
var result = cache.get(url);
if(!result) {
var response = UrlFetchApp.fetch(url);
result = response.getContentText();
cache.put(url, result, 21600);
sheet.getRange(i+1,3).setValue(result); // set the value of the current cell to the result of the API call in column C

Reading JSON in Google Sheets with Google Script

I am currently pulling data from an external API to Google Sheets and wanted to get a certain part of the data to show from the JSON. The only part that should be added to the cell is $440.00 but currently it shows {"data":[{"cost":"$440.00"}],"success":true,"totalNumRows":1}. Is there a way to just pull that?
// Add menu to run program
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Get Crobo Cost')
.addItem('Display daily cost', 'callCrobo')
// Function to call Crobo
function callCrobo() {
// Call Crobo daily spend
var apiKey = '****omitted****'
var response = UrlFetchApp.fetch("" + apiKey + "&start_date=2018-03-12&end_date=2018-03-12&field[]=Stat.revenue")
// Parse to JSON reply
var json = response.getContentText();
var obj = JSON.parse(json);
//Gets hold of ActiveSheet in current workbook
var sheet = SpreadsheetApp.getActiveSheet();
// Set value in cell A1
sheet.getRange(sheet.getLastRow() + 1,1).setValue([response]);
The logger's current output is:
[18-03-13 17:22:02:377 EDT] {"data":
[18-03-13 17:22:02:378 EDT] undefined
Thank you

Export data from Google AppMaker Datasource automatically

Does anyone know how we can generate report from data in datasource in Google AppMaker automatically (e.g generate report at 12a.m.) instead of manually click export data in deployments every time user need the report.
I have seen something similar on Exporting data out of Google AppMaker but also no one tried to answer that.
Really appreciate if there is anyone who know how to solve this :)
This can be achieved by using Installable Triggers.
Say for example, you have a model with students data that has three fields; name(string), age(number) and grade(number). On the server script you can write something like this:
//define function to do the data export
function dataExport() {
//create sheet to populate data
var fileName = "Students List " + new Date(); //define file name
var newExport = SpreadsheetApp.create(fileName); // create new spreadsheet
var header = ["Name", "Age", "Grade"]; //define header
newExport.appendRow(header); // append header to spreadsheet
//get all students records
var ds = app.models.students.newQuery();
var allStudents =;
for(var i=0; i< allStudents.length; i++) {
//get each student data
var student = allStudents[i];
var studentName =;
var studentAge = student.age;
var studentGrade = student.grade;
var newRow = [studentName, studentAge, studentGrade]; //save studen data in a row
newExport.appendRow(newRow); //append student data row to spreadsheet
console.log("Finished Exporting Student Data");
//invoke function to set up the auto export
function exportData(){
//check if there is an existing trigger for this process
var existingTrigger = PropertiesService.getScriptProperties().getProperty("autoExportTrigger");
//if the trigger already exists, inform user about it
if(existingTrigger) {
return "Auto export is already set";
} else { // if the trigger does not exists, continue to set the trigger to auto export data
//runs the script every day at 1am on the time zone specified
var newTrigger = ScriptApp.newTrigger('dataExport')
var triggerId = newTrigger.getUniqueId();
if(triggerId) {
PropertiesService.getScriptProperties().setProperty("autoExportTrigger", triggerId);
return "Auto export has been set successfully!";
} else {
return "Failed to set auto export. Try again please";
Then, to delete/stop the auto export, in case you need to, you can write the following on the server script too:
function deleteTrigger() {
//get the current auto export trigger id
var triggerId = PropertiesService.getScriptProperties().getProperty("autoExportTrigger");
//get all triggers
var allTriggers = ScriptApp.getProjectTriggers();
//loop over all triggers.
for (var i = 0; i < allTriggers.length; i++) {
// If the current trigger is the correct one, delete it.
if (allTriggers[i].getUniqueId() === triggerId) {
//else delete all the triggers found
} else {
return "Auto export has been cancelled";
You can check the demo app right here.
The reference to the script properties service is here.
The reference to the Time Zones list is here.
I hope this helps!
It seems that you are looking for daily database backups. App Maker Team recommends migrating apps to Cloud SQL if you haven't done this so far. Once you start using Cloud SQL as your data backend you can configure backups through Google Cloud Console:

Using Google sheet script to push data from HTML form to sheet not working when sheet is not open

I am using a Google App script to send data from an HTML form to a Google spreadsheet. This works perfectly when I have the spreadsheet open in my browser; the data I put in the form is submitted to the sheet. On the other hand, when I close the spreadsheet and fill out the form, noting is submitted to the sheet.
Here is my script.
// 1. Enter sheet name where data is to be written below
var SHEET_NAME = "Sheet1";
// 2. Run > setup
// 3. Publish > Deploy as web app
// - enter Project Version name and click 'Save New Version'
// - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously)
// 4. Copy the 'Current web app URL' and post this in your form/script action
// 5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
return handleResponse(e);
function doPost(e){
return handleResponse(e);
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1]
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
} finally { //release lock
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
I suspect that there is no "active spreadsheet" when you have the spreadsheet closed.
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
I recommend that you hardcode the spreadsheet id into your code.