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
Practice Details Use PropertiesService Never hardcode tokens or keys in your script Use muteHttpExceptions: true Handle auth errors gracefully Rotate keys regularly Store updated keys via setProperty() Use script-level properties ScriptProperties are per-project, not per-userAvoid logging sensitive data Don't Logger.log API tokens