Use Google Apps Script to Call SheetsAPI
Call SheetsAPI from inside Google Apps Script using UrlFetchApp, cache responses with CacheService, and trigger syncs on a time-based schedule.
Google Apps Script runs inside Google Workspace and has direct access to Spreadsheet data - so why would you call SheetsAPI from it? Because SheetsAPI exposes your sheet as a clean REST endpoint that other services (Zapier, Retool, your own app) can call. Apps Script is a great place to push data to SheetsAPI, sync rows from other APIs into your sheet, or orchestrate multi-sheet workflows triggered on a schedule.
Prerequisites
- A Google account with access to Google Apps Script (script.google.com)
- A Google Sheet with a header row
- A SheetsAPI account and a
YOUR_USER_KEYkey from the dashboard
1. Basic fetch with UrlFetchApp
Apps Script's UrlFetchApp is the equivalent of fetch() in the browser:
function listProducts() {
const userKey = "YOUR_USER_KEY";
const apiKey = "sk_your_api_key_here";
const url = `https://sheetsapi.gkit.mreshank.com/api/spreadsheets/${userKey}/Products?limit=20&sort=name`;
const resp = UrlFetchApp.fetch(url, {
method: "get",
headers: { Authorization: `Bearer ${apiKey}` },
muteHttpExceptions: true,
});
if (resp.getResponseCode() !== 200) {
Logger.log("Error: " + resp.getContentText());
return;
}
const { data, meta } = JSON.parse(resp.getContentText());
Logger.log(`Fetched ${data.length} of ${meta.total} products`);
data.forEach((p) => Logger.log(`${p.name} - $${p.price}`));
}Run this from the Apps Script editor (Run → listProducts) and see the output in the Execution log.
2. Add a row via POST
Append a row to your sheet using the SheetsAPI POST endpoint:
function addProduct(name, price, category) {
const userKey = "YOUR_USER_KEY";
const apiKey = "sk_your_api_key_here";
const url = `https://sheetsapi.gkit.mreshank.com/api/spreadsheets/${userKey}/Products`;
const payload = JSON.stringify({ name, price: String(price), category });
const resp = UrlFetchApp.fetch(url, {
method: "post",
contentType: "application/json",
headers: { Authorization: `Bearer ${apiKey}` },
payload,
muteHttpExceptions: true,
});
if (resp.getResponseCode() >= 400) {
throw new Error("SheetsAPI error: " + resp.getContentText());
}
Logger.log("Row added: " + resp.getContentText());
}
// Test it:
function testAddProduct() {
addProduct("Widget Pro", 49.99, "Widgets");
}3. Cache responses with CacheService
Apps Script's CacheService avoids redundant API calls when the same function runs
many times in a short window:
function getCachedProducts() {
const cache = CacheService.getScriptCache();
const cacheKey = "products_all";
const cached = cache.get(cacheKey);
if (cached) {
Logger.log("Cache hit");
return JSON.parse(cached);
}
const userKey = "YOUR_USER_KEY";
const apiKey = "sk_your_api_key_here";
const resp = UrlFetchApp.fetch(
`https://sheetsapi.gkit.mreshank.com/api/spreadsheets/${userKey}/Products?limit=500`,
{ headers: { Authorization: `Bearer ${apiKey}` } },
);
const data = JSON.parse(resp.getContentText());
cache.put(cacheKey, JSON.stringify(data), 300); // cache for 5 minutes
return data;
}CacheService supports up to 100 KB per entry and a 6-hour maximum TTL.
4. Fetch all pages
SheetsAPI paginates with limit and offset. Loop until all rows are fetched:
function fetchAllProducts() {
const userKey = "YOUR_USER_KEY";
const apiKey = "sk_your_api_key_here";
const base = `https://sheetsapi.gkit.mreshank.com/api/spreadsheets/${userKey}/Products`;
const pageSize = 100;
const all = [];
let offset = 0;
do {
const resp = UrlFetchApp.fetch(`${base}?limit=${pageSize}&offset=${offset}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const { data, meta } = JSON.parse(resp.getContentText());
all.push(...data);
offset += pageSize;
if (all.length >= meta.total) break;
} while (true);
Logger.log(`Fetched all ${all.length} products`);
return all;
}5. Sync SheetsAPI data into a different sheet tab
Pull data from SheetsAPI and write it into a separate tab in the same spreadsheet:
function syncProductsToTab() {
const products = fetchAllProducts();
if (!products.length) return;
const ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName("Synced Products");
if (!sheet) sheet = ss.insertSheet("Synced Products");
// Write header row
const headers = Object.keys(products[0]);
sheet.clearContents();
sheet.getRange(1, 1, 1, headers.length).setValues([headers]);
// Write data rows
const rows = products.map((p) => headers.map((h) => p[h] ?? ""));
sheet.getRange(2, 1, rows.length, headers.length).setValues(rows);
Logger.log(`Synced ${products.length} rows to "Synced Products" tab`);
}6. Time-based trigger (scheduled sync)
Set up a trigger to run the sync automatically:
function createDailyTrigger() {
// Delete existing triggers for this function first
ScriptApp.getProjectTriggers()
.filter((t) => t.getHandlerFunction() === "syncProductsToTab")
.forEach((t) => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger("syncProductsToTab").timeBased().everyHours(6).create();
Logger.log("Trigger created: sync every 6 hours");
}Run createDailyTrigger() once. After that, syncProductsToTab will run every 6 hours
without any intervention.
7. Error handling and retry
Wrap calls in a try/catch and implement a simple retry for transient errors:
function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const resp = UrlFetchApp.fetch(url, {
...options,
muteHttpExceptions: true,
});
const code = resp.getResponseCode();
if (code === 429) {
// Rate limited - back off
const retryAfter = parseInt(resp.getHeaders()["Retry-After"] || "10", 10);
Utilities.sleep(retryAfter * 1000);
continue;
}
if (code >= 500 && attempt < maxRetries) {
Utilities.sleep(1000 * attempt); // exponential back-off
continue;
}
return resp;
} catch (err) {
if (attempt === maxRetries) throw err;
Utilities.sleep(1000 * attempt);
}
}
}Query parameter reference
| Parameter | Example | Description |
|---|---|---|
limit | 100 | Rows per page (max 500) |
offset | 0 | Pagination offset |
search | category:widgets | Filter field:value |
sort | name or -price | Ascending / descending |
fields | name,price | Return only named columns |
Summary
| Task | Apps Script approach |
|---|---|
| Fetch rows | UrlFetchApp.fetch + JSON.parse |
| Add rows | UrlFetchApp.fetch with method: "post" |
| Cache results | CacheService.getScriptCache().put(key, val, ttl) |
| Paginate all rows | do/while loop with offset |
| Write to sheet | SpreadsheetApp.getActiveSpreadsheet().getRange().setValues() |
| Scheduled sync | ScriptApp.newTrigger().timeBased().everyHours(n).create() |
Apps Script is free, lives inside Google Workspace, and needs no server - making it one of the lowest-friction ways to automate syncs between SheetsAPI and the rest of your Google Workspace tools.
GKit is currently undergoing platform maintenance
Be the first to know when all Google Workspace tools are back online, plus get our engineering deep-dives and product announcements directly to your inbox.