Handle API Authentication

Most APIs require authentication. Apps Script supports all common auth patterns — API key headers, Bearer tokens, Basic Auth, and OAuth2. Equally important is where you store credentials: never hardcode secrets in your script.

Storing API Keys with PropertiesService

PropertiesService is the right place to store API keys and tokens in Apps Script. They are stored outside your code and are not visible to other users or in version history.

// Run this once to store your keys function storeApiKeys() { var props = PropertiesService.getScriptProperties(); props.setProperty("CRM_API_TOKEN", "your-secret-token-here"); props.setProperty("SLACK_WEBHOOK_URL", "https://hooks.slack.com/services/..."); Logger.log("API keys stored."); } // Then retrieve like this in your functions function getApiToken() { return PropertiesService.getScriptProperties().getProperty("CRM_API_TOKEN"); }

Bearer Token Authentication

function fetchWithBearerToken() { var token = PropertiesService.getScriptProperties().getProperty("CRM_API_TOKEN"); var options = { method: "GET", headers: { "Authorization": "Bearer " + token, "Content-Type": "application/json" }, muteHttpExceptions: true }; var response = UrlFetchApp.fetch("https://api.your-crm.com/v1/deals", options); Logger.log("Status: " + response.getResponseCode()); return JSON.parse(response.getContentText()); }

API Key as a Header

Some APIs use a custom header like X-API-Key:

function fetchWithApiKeyHeader() { var apiKey = PropertiesService.getScriptProperties().getProperty("ENRICHMENT_API_KEY"); var options = { method: "GET", headers: { "X-API-Key": apiKey }, muteHttpExceptions: true }; var response = UrlFetchApp.fetch("https://api.example-enrichment.com/company?domain=techventures.com", options); Logger.log(response.getContentText()); }

API Key as a Query Parameter

function fetchWithQueryParamKey() { var apiKey = PropertiesService.getScriptProperties().getProperty("WEATHER_API_KEY"); var url = "https://api.openweathermap.org/data/2.5/weather?q=NewYork&appid=" + apiKey; var response = UrlFetchApp.fetch(url, { muteHttpExceptions: true }); Logger.log(response.getContentText()); }

Basic Authentication

Basic Auth encodes username:password in Base64:

function fetchWithBasicAuth() { var username = "acmecorp"; var password = PropertiesService.getScriptProperties().getProperty("API_PASSWORD"); var encoded = Utilities.base64Encode(username + ":" + password); var options = { method: "GET", headers: { "Authorization": "Basic " + encoded }, muteHttpExceptions: true }; var response = UrlFetchApp.fetch("https://api.example.com/v1/deals", options); Logger.log(response.getContentText()); }

Refreshing an Expired Token

Some APIs use short-lived access tokens. Detect a 401 and re-authenticate automatically:

function fetchWithTokenRefresh(url) { var token = PropertiesService.getScriptProperties().getProperty("ACCESS_TOKEN"); var options = { headers: { "Authorization": "Bearer " + token }, muteHttpExceptions: true }; var response = UrlFetchApp.fetch(url, options); if (response.getResponseCode() === 401) { Logger.log("Token expired, refreshing..."); token = refreshAccessToken(); options.headers["Authorization"] = "Bearer " + token; response = UrlFetchApp.fetch(url, options); // Retry } return response; } function refreshAccessToken() { var props = PropertiesService.getScriptProperties(); var refreshToken = props.getProperty("REFRESH_TOKEN"); var clientId = props.getProperty("CLIENT_ID"); var clientSecret = props.getProperty("CLIENT_SECRET"); var response = UrlFetchApp.fetch("https://auth.example.com/token", { method: "POST", payload: { grant_type: "refresh_token", refresh_token: refreshToken, client_id: clientId, client_secret: clientSecret }, muteHttpExceptions: true }); var data = JSON.parse(response.getContentText()); props.setProperty("ACCESS_TOKEN", data.access_token); Logger.log("Token refreshed."); return data.access_token; }

Security Best Practices

PracticeDetails
Use PropertiesServiceNever hardcode tokens or keys in your script
Use muteHttpExceptions: trueHandle auth errors gracefully
Rotate keys regularlyStore updated keys via setProperty()
Use script-level propertiesScriptProperties are per-project, not per-user
Avoid logging sensitive dataDon't Logger.log API tokens