---
term: 'API Key Security'
seoTitle: 'API Key Security: Storage, Rotation, Leaks, Client-Side Keys'
headline: 'What is API Key Security?'
slug: api-key-security
category: auth-security
shortDefinition: 'An API key is a unique string identifying the calling app to an API; API key security is the discipline of scoping and protecting it.'
relatedTerms:
  - json-web-token-jwt
  - api-rate-limiting-throttling
  - cors-cross-origin-resource-sharing
  - data-encryption-at-rest-transit
contrastsWith:
  - json-web-token-jwt
aboutTerms:
  - 'Publishable vs. Secret Keys'
  - 'Key Rotation'
  - 'Secrets Management'
faq:
  - question: 'What is an API key?'
    answer: 'A unique string an API provider issues to a registered application, sent with each request — ideally in a header — so the server can identify the caller, apply its permissions, meter usage, and enforce rate limits. Notably, no standard defines it: API keys are a convention, not a protocol.'
  - question: 'Is an API key a password?'
    answer: 'A secret key is functionally password-class: it is a bearer credential, so anyone holding it is trusted as you — same storage discipline, same breach consequences. The differences: keys identify applications rather than people, and many never expire unless you rotate them.'
  - question: 'What is the difference between an API key and a token?'
    answer: 'Keys identify apps; tokens authenticate users. A key is static, admin-generated, and app-scoped; an OAuth access token is issued at login, short-lived, refreshable, and carries a specific user''s permissions. Server-to-server identification suits keys; anything user-specific belongs to tokens.'
  - question: 'Where should I store API keys?'
    answer: 'Never in source code. Environment variables from an untracked file are the baseline — with the caveat that they leak through logs, process dumps, and container definitions — and a secrets manager is the team standard: encrypted at rest, access-controlled, audited, and rotatable.'
  - question: 'Can I put an API key in frontend or mobile code?'
    answer: 'Only a publishable key designed for it. Anything in a JavaScript bundle or app binary is public — extraction is routine and obfuscation only slows it. Secret keys stay server-side; when a client needs a secret-keyed service, route the call through your own backend.'
  - question: 'What should I do when an API key leaks?'
    answer: 'Immediately: revoke the key, deploy a replacement, purge it from code and git history, audit usage logs for abuse, and rotate anything stored alongside it. Move fast — bots test keys committed to public repositories within minutes — and remember revocation stops future use, not data already taken.'
  - question: 'How often should API keys be rotated?'
    answer: 'Risk-based: every 30–90 days for broad-scope or externally exposed keys, longer for low-risk internal ones, and immediately on suspected exposure or staff departure. Zero-downtime rotation uses an overlap window in which old and new keys are both valid while deployments catch up.'
  - question: 'How do API keys get leaked?'
    answer: 'In order of infamy: committed to git repositories, shipped in client bundles and mobile binaries, placed in URLs where server logs and browser history capture them, printed into application and CI logs, and pasted into chats and tickets. Every vector is preventable, which is what makes the list depressing.'
  - question: 'Should API keys be scoped?'
    answer: 'Always — least privilege per key: one key per application per environment, restricted to the operations it needs, with IP or domain restrictions where the provider supports them. Scoping turns a leak from a master-credential event into a bounded, revocable one.'
  - question: 'Should the key go in the URL or a header?'
    answer: 'A header, always — query strings are recorded in browser history, server access logs, and referrer headers, turning every log file into a credential store. Send keys in an Authorization or custom header, over HTTPS only.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'OWASP Secrets Management Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html'
  - name: 'OWASP API Security Top 10 (2023)'
    url: 'https://owasp.org/API-Security/editions/2023/en/0x11-t10/'
  - name: 'OWASP Key Management Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html'
  - name: 'RFC 6750 — OAuth 2.0 Bearer Token Usage'
    url: 'https://datatracker.ietf.org/doc/html/rfc6750'
  - name: 'API key — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/API_key'
cta:
  title: 'Keys designed to be shipped'
  text: 'Back4app''s client keys are publishable by design — data is protected by CLPs and ACLs enforced server-side, not by key secrecy — while the Master Key stays where secrets belong: on the server.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-30'
translationKey: api-key-security
---

**An API key is a unique string identifying the calling app to an API; API key security is the discipline of scoping and protecting it.** Precision first, because most definitions blur it: a key *identifies* the application, provides only *weak authentication* (it's a bearer credential — whoever holds it, is it), and carries *coarse authorization* (whatever scope was attached at creation). Users are authenticated by [tokens](/glossary/json-web-token-jwt/); apps are identified by keys — and no RFC defines the API key at all. It is a convention, which is exactly why its security is your configuration, not a standard's guarantee.

## Key takeaways

| Question | Answer |
| --- | --- |
| What a key does | Identifies the app · meters usage · anchors [rate limits](/glossary/api-rate-limiting-throttling/) |
| The two animals | Publishable keys (built to ship) vs. secret keys (password-class) |
| The storage ladder | Hardcoded: never → env vars: baseline → secrets manager: standard |
| The iron law | Anything in a client bundle is public — plan for extraction |
| Leak response | Revoke → replace → purge history → audit — within minutes, not days |

## The request, and the two kinds of keys

```text
GET /v1/search?q=espresso HTTP/1.1
Host: api.example.com
X-Api-Key: pk_live_7f2c…      ← in a HEADER — URLs end up in logs,
                                 history, and referrers

Two different animals share one name:
publishable key   ships in web/mobile bundles · identifies the app,
                  meters usage · designed knowing it WILL be extracted
secret key        server-side only · password-class bearer credential ·
                  anyone holding it is you
```

The publishable model in practice — keys that ship because security lives elsewhere:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
Parse.initialize(APP_ID, JS_KEY); // both ship in your bundle — by design
Parse.serverURL = 'https://parseapi.back4app.com';
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request

// The one key that never ships: the Master Key bypasses every ACL and CLP.
// Server-only (Cloud Code / trusted backend), read from env or secret manager.
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
await Parse().initialize(
  appId, // ships in the app — by design
  'https://parseapi.back4app.com',
  clientKey: clientKey, // publishable, extractable, NOT a secret
);
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request
// The Master Key bypasses every ACL and CLP: server-only, never in the app.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
ParseSwift.initialize(
    applicationId: appId,  // ships in the IPA — by design
    clientKey: clientKey,  // publishable, extractable, NOT a secret
    serverURL: URL(string: "https://parseapi.back4app.com")!
)
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request
// The Master Key bypasses every ACL and CLP: server-only, never in the app.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
Parse.initialize(
    Parse.Configuration.Builder(context)
        .applicationId(APP_ID) // ships in the APK — by design
        .clientKey(CLIENT_KEY) // publishable, extractable, NOT a secret
        .server("https://parseapi.back4app.com")
        .build()
)
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request
// The Master Key bypasses every ACL and CLP: server-only, never in the app.
```

## API keys vs. tokens vs. JWTs

| | API key | OAuth access token | JWT |
| --- | --- | --- | --- |
| Identifies | The application | The user (and grant) | Whatever its claims say |
| Issued | Once, by an admin | Per login, by a flow | It's a *format*, not an issuance |
| Lifetime | Until rotated (often never) | Minutes to hours | Whatever `exp` says |
| Scope | Fixed at creation | Per-grant scopes | Claims-defined |
| Standard | None — convention | [OAuth 2.0](/glossary/oauth-2-social-login/) | RFC 7519 |
| Right job | Server-to-server ID, metering | User-delegated API access | Signed claims transport |

The comparison collapses into one sentence worth memorizing: **keys identify apps; tokens authenticate users.** Using a key where a user's identity matters rebuilds authentication badly; using per-user tokens for anonymous app metering is machinery without a purpose.

## Where keys live: the storage ladder

**Hardcoded — never.** Source code is copied, forked, and committed; git remembers forever, and secret-scanning bots find keys in public commits within minutes. **Environment variables — the baseline**, with the caveat the [OWASP cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html) is blunt about: env vars leak through error logs, process dumps, and container definitions; they keep secrets out of git, not out of trouble. **A secrets manager — the team standard**: encrypted at rest, access-controlled per service, audited per read, rotatable centrally (open-source options include Vault, SOPS, and Infisical). Add the hygiene that makes leaks survivable: keys generated cryptographically random, *prefixed* (`sk_live_…`-style) so scanners recognize them, hashed at rest on the provider side like passwords, one key per app per environment — and secret scanning (gitleaks, trufflehog) wired into CI so the commit that leaks a key fails before it lands.

## The client-side problem, honestly

Every explainer says "don't put secret keys in client code"; almost none says the second half: **your bundle is public.** Web JavaScript is readable by definition; mobile binaries are unpacked and string-dumped routinely; obfuscation raises the effort from minutes to hours, once. Two consequences follow. First, the only keys that belong in clients are *publishable* ones — designed to identify, not to protect, with real authorization enforced server-side per request. Second, when a client must use a secret-keyed third-party service, the secret stays behind your own backend — the proxy pattern:

```mermaid
flowchart LR
  accTitle: Proxy pattern keeping secret keys server-side
  accDescr: The client app holds only a publishable key and calls your backend. The backend, which holds the secret key in a secrets manager, calls the third-party API and returns results, so the secret never ships to the client.
  C["Client app<br/>publishable key only"] -->|"your API"| B["Your backend<br/>secret key from<br/>secrets manager"]
  B -->|"X-Api-Key: sk_live_…"| T["Third-party API"]
  T --> B --> C
  X["Attacker unpacks the bundle"] -.->|"finds nothing<br/>worth stealing"| C
```

## When a key leaks: the runbook

The clock matters — bots monitor public repositories and exploit committed keys in **one to five minutes**. In order: **1 · Revoke** the key at the provider — before investigating, before the standup. **2 · Replace** — issue the new key and deploy it through config, not code. **3 · Purge** — remove from source *and git history*; a deleted line lives on in every clone. **4 · Audit** — provider logs for the leak window: what was read, created, spent. **5 · Widen** — anything co-located with the key (the same .env, the same repo) is presumed burned; rotate it too. And the caveat that separates real response from ritual: **revocation stops future use — it does not un-exfiltrate data.** What was taken during the window is an incident, not a rotation.

## Rotation without downtime

Rotation caps the value of undetected leaks — a stolen key with 60 days left is a different asset than one valid forever. Risk-based cadence: 30–90 days for broad-scope or externally shared keys, up to a year for narrow internal ones, *immediately* on suspected exposure or departure of anyone who held it. The zero-downtime move is the **dual-key overlap**: issue the new key while the old stays valid, migrate deployments at leisure, then revoke the old — the same trick refresh-token systems formalize. Compliance regimes increasingly require the calendar; the security case never needed it.

## Common use cases

- **Server-to-server integration** — the key's native habitat: one service identifying itself to another.
- **Usage metering and billing** — the key as the unit providers count, throttle, and invoice.
- **Publishable client identification** — app bundles carrying keys built for exposure, with authorization elsewhere.
- **Environment separation** — test and live keys keeping staging accidents out of production data.
- **Abuse containment** — per-key [rate limits](/glossary/api-rate-limiting-throttling/) and revocation as the blast-radius controls.

## Which credential should you use? A decision matrix

| Situation | Reach for |
| --- | --- |
| Backend calling a third-party API | Secret key, in a secrets manager |
| Identifying your app from web/mobile | Publishable key + server-side authorization |
| Acting on behalf of a signed-in user | OAuth tokens, not keys |
| Signed claims across services | [JWTs](/glossary/json-web-token-jwt/) |
| Client needs a secret-keyed service | Your backend as proxy — secret never ships |
| Machine-to-machine with user-like auth | OAuth client credentials flow |

## Limitations and trade-offs

- **Keys can't prove possession.** A bearer string offers no cryptographic binding to the caller; for high-assurance service auth, mutual TLS and signed requests exist for a reason.
- **Static credentials age badly.** No expiry means every leak is open-ended until noticed; rotation is the manual substitute for the lifecycle tokens get for free.
- **Coarse scopes overshare.** One key with broad permissions is a skeleton key; granular scoping costs administration and pays in blast radius.
- **Keys identify, not authenticate.** Building user-level trust on app-level identification is the [broken-authentication](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) pattern auditors look for first.
- **Inventory drift is real.** Unused keys from old integrations stay valid until deleted — the zombie-credential cousin of zombie endpoints.

## API keys on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Its key model is the client-side section made concrete: the Application ID and client keys **ship inside your apps by design** — Back4app's own docs are explicit that client keys are not security mechanisms — because authorization never depends on them: every request is checked server-side against [class-level permissions](/glossary/class-level-permissions-clp/) and [ACLs](/glossary/access-control-lists-acl/), so an extracted key grants an attacker exactly what an anonymous user gets. The one true secret is the **Master Key**, which bypasses every ACL and CLP: it lives server-side only — Cloud Code, trusted backends, env or secrets manager — and never in a bundle. The code tabs show the split in practice; the runbook applies to the master key alone, which is the point: one secret to guard is a security posture, forty is a spreadsheet.
