appscript.dev
Guide Beginner

Store API keys and secrets securely

Use Script Properties the right way — never paste keys into code.

Published Jul 3, 2025

Almost any useful script eventually needs a secret — an API key to call an external service, an authentication token, a password. The tempting move is to paste it straight into a .gs file where the code can read it. That single shortcut is the most common security mistake in Apps Script, and it is easy to avoid.

A secret in source code does not stay where you put it. It travels into version history, into copies of the project, into screenshots and shared editor views. Apps Script gives you a proper place to keep secrets out of the code entirely: the Properties service. This guide shows how to use it correctly.

The rule

API keys, tokens, and passwords live in Script Properties — never hard-coded in a .gs file. The code reads the secret at runtime; the secret itself is stored as project configuration, separate from the logic.

Hard-coding a key causes problems that have nothing to do with the script running correctly:

  • Anyone who can see the code can see the key, including people you only meant to share read access with.
  • The key is captured in every backup, every “Make a copy”, and every commit if the project is under version control.
  • Rotating the key means editing and redeploying code, instead of changing one configuration value.

Keeping the secret in Script Properties fixes all three at once.

Setting them

Script Properties are edited from the project’s settings, not from code, so you never have to write a secret into a file even temporarily.

  1. In the editor, open Project Settings (the gear icon).
  2. Scroll to Script Properties and choose Add property.
  3. Enter a descriptive key — for example ANTHROPIC_API_KEY — and paste the secret as the value, such as sk-....
  4. Save. The property is now available to every function in the project.

Use clear, uppercase names so it is obvious in the code that a value comes from configuration rather than being a literal. One property per secret keeps rotation simple.

Reading them

In code, a single call fetches the stored value. There is no secret anywhere in the file — only the name of the property to look up.

// Fetch the key by name at runtime. The actual secret is never in this file.
const KEY = PropertiesService.getScriptProperties().getProperty('ANTHROPIC_API_KEY');

If the property is missing, getProperty returns null rather than throwing, so it is worth failing fast with a clear message:

const KEY = PropertiesService.getScriptProperties().getProperty('ANTHROPIC_API_KEY');

// Catch a misconfigured project early, with a message that names the problem.
if (!KEY) {
  throw new Error('ANTHROPIC_API_KEY is not set in Script Properties');
}

That guard turns a confusing downstream “401 Unauthorized” from the API into an obvious “you forgot to set the property” at the top of the run.

Three property scopes

PropertiesService exposes three separate stores. They differ in who can see a value and how long it lives, and choosing the wrong one either leaks data between users or loses it unexpectedly.

ScopeAccessorLifetimeShared with
ScriptgetScriptProperties()Until the script is deletedEveryone who runs the script
UsergetUserProperties()Until the script is deletedOnly the current user
DocumentgetDocumentProperties()Until the bound file is deletedEveryone using that file

The practical guidance:

  • Script Properties for shared configuration the whole project needs — a service-wide API key, a webhook URL.
  • User Properties for per-person values — an individual’s preference, a token tied to one user’s account.
  • Document Properties for state that belongs to a specific spreadsheet or document, such as a last-synced timestamp for that file.

Watch out for

  • Script Properties are visible to anyone with edit access to the project. They keep secrets out of the code, but they are not encrypted in a way that hides them from a fellow editor — treat edit access as access to the secrets.
  • For a script shared widely where the key must stay private, deploy it as a web app that executes as the owner. Users invoke the wrapper, the wrapper reads the key, and the callers never see it.
  • Do not log the secret. A stray console.log(KEY) writes it into the execution log, which is its own form of leak.
  • A copied project does not carry its Script Properties across. After “Make a copy” the new project’s properties are empty — set them again rather than assuming the key came along.
  • Avoid passing secrets in URL query strings where you can. They end up in server logs and browser history; prefer request headers or a POST body.