Skip to content
// Apps Script

Store API keys with PropertiesService in Apps Script.

Stop hardcoding API keys in Apps Script source. Use PropertiesService to keep credentials out of version history, shared copies, and accidental clipboard leaks.

I need to store an API key in my Apps Script project without it appearing in my source code, version history, or shared copies.

The script

copy · paste · trigger
apiKey.gs
Apps Script
// Store and retrieve an API key using Script Properties
// Run setApiKey() once manually; never commit the key in source

function setApiKey() {
  var scriptProps = PropertiesService.getScriptProperties();
  scriptProps.setProperty('WEATHER_API_KEY', 'your-key-here');
  Logger.log('Key saved.');
}

function getWeather(city) {
  var scriptProps = PropertiesService.getScriptProperties();
  var apiKey = scriptProps.getProperty('WEATHER_API_KEY');
  if (!apiKey) {
    throw new Error('WEATHER_API_KEY not set. Run setApiKey() first.');
  }
  var url = 'https://api.openweathermap.org/data/2.5/weather?q=' + city + '&appid=' + apiKey;
  var response = UrlFetchApp.fetch(url);
  return JSON.parse(response.getContentText());
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why hardcoded keys are a real problem in Apps Script

Every Apps Script project has automatic version history. When you type an API key directly into a string literal and save, that key is now in every revision Google silently stores. Revoke it later and you have to assume it was exposed — version history is not pruned when you delete lines. The same problem compounds when you share the project: File > Share gives collaborators full editor access, which includes reading source. A key embedded in code is readable by anyone with edit rights, on any device they happen to use, indefinitely.

The fix is to move credentials out of source entirely. PropertiesService is a key-value store built into Apps Script that persists between executions and is not exposed in the code editor, the version history, or copies made via File > Make a copy.

ScriptProperties vs UserProperties: pick the right scope

Apps Script exposes three property scopes. ScriptProperties are shared across all users of the script — one key, visible to every authorized runner. UserProperties are per-Google-account, so each user who runs the script has their own isolated store. DocumentProperties (only available in container-bound scripts) are tied to the parent file.

For API keys that belong to the project, ScriptProperties is almost always the right call. The key lives once, all executions use it, and only editors can read or overwrite it through the Properties Service UI (Project Settings > Script Properties). If you are building a multi-tenant add-on where each user supplies their own API key — think a connector that each person authenticates separately — then UserProperties is the correct scope. I keep a comment at the top of any properties-touching file noting which scope is in use; the name PropertiesService gives no hint.

One thing to be clear about: these properties are access-controlled by Google's IAM model, not encrypted at rest in any end-to-end sense. A project owner with console access can read them. They are not a substitute for a secrets manager in a high-compliance environment. For most Workspace automation, they are exactly the right tradeoff — they gate casual exposure, prevent source leaks, and require no external infrastructure.

Setting properties once, reading them every run

The pattern in the snippet above is the one I use in every Apps Script project that touches an external API. Write a small one-off function — setApiKey() or similar — that you run exactly once from the Apps Script editor by selecting it in the function dropdown and clicking Run. That function calls setProperty() with the real key. After it runs, delete the string literal from the function body (or leave the placeholder 'your-key-here'); the value is now stored in the properties store, not in source.

Every subsequent run calls getProperty() to retrieve the key at runtime. If the property is missing — because a new collaborator deployed the script on a fresh project, for instance — the guard clause throws a descriptive error immediately rather than producing a mysterious 401 from the downstream API. That early failure with a clear message saves a lot of confused debugging.

There is no synchronous secret rotation built into PropertiesService: to rotate, you run setApiKey() again with the new value. For an unattended automation you might want a trigger-driven rotation function, but for most projects, manual rotation on a cadence is fine. The key point is that rotation is now a one-function operation that never touches committed code.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Can other people who have view-only access to my script read Script Properties?
No. Viewers can see the source code, but Script Properties are not surfaced in the code editor. Only editors and owners can see them via Project Settings > Script Properties. However, if your code logs the key value with Logger.log() or returns it in a cell, viewers can read it that way — the property store is access-controlled, not a content firewall.
What happens to Script Properties when someone makes a copy of my spreadsheet or script?
File > Make a copy does not copy Script Properties. The new project starts with an empty property store. This is the correct behavior — it means a copied script cannot silently inherit credentials that belong to the original project. Your setApiKey() one-time setup step just needs to be documented (or guarded with that missing-key error) so the person who copied it knows to run it.
Is there a size limit on what I can store in PropertiesService?
Yes. Each property value is capped at 9 KB. The total storage per property store is 500 KB. API keys and short tokens are well within limits; do not try to store large JSON payloads or base64-encoded certificates here. For larger config blobs, store them in a hidden sheet or a Drive file and store only the file ID in properties.
Can I set Script Properties without opening the Apps Script editor — for example, from a deployment pipeline?
Yes, via the Apps Script API (scripts.projects.getContent and the Properties service REST endpoints), but it requires OAuth and a GCP project with the API enabled. For simple cases, the one-time manual run of setApiKey() in the editor is far less friction. The API route makes sense if you are managing credentials for dozens of scripts from a central pipeline.
// one good script a week

Get a working Apps Script snippet in your inbox, weekly.