HomeProduction & DeploymentAPI Keys & Security Best Practices
intermediate10 min read· Module 10, Lesson 1

🔑API Keys & Security Best Practices

Protect your keys, manage secrets, and secure your integration

API Keys & Security Best Practices

Your API key is the gateway to Claude. If it is compromised, attackers can run up massive bills, extract sensitive data, or abuse the API under your identity. This lesson covers everything you need to know to keep your keys safe and your integration secure.


Anatomy of an API Key

An Anthropic API key is a long, randomly generated string that uniquely identifies your account and grants access to the API. Here is what you need to know about its structure:

PropertyDetail
PrefixAnthropic keys typically start with sk-ant-
LengthUsually 80+ characters
EncodingAlphanumeric with hyphens
ScopeFull access to the API under your account
RevocabilityCan be revoked instantly from the Anthropic Console

Key points:

  • Each key is tied to a specific workspace and organization
  • You can create multiple keys for different environments (dev, staging, production)
  • Keys do not expire automatically — you must rotate them manually
  • A compromised key grants full API access until revoked

Environment Variables & .env Files

The most fundamental rule of API key security is: never hardcode keys in your source code.

The Wrong Way

TypeScript
// NEVER DO THIS — key is exposed in source code const client = new Anthropic({ apiKey: "sk-ant-api03-REAL-KEY-HERE-xxxxxxxxxx", });

The Right Way — Environment Variables

TypeScript
// Load the key from environment at runtime const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, });

Using dotenv for Local Development

Install dotenv to load variables from a .env file during development:

Terminal
npm install dotenv

Create a .env file in your project root:

Terminal
# .env — NEVER commit this file ANTHROPIC_API_KEY=sk-ant-api03-your-key-here NODE_ENV=development

Load it at the top of your entry file:

TypeScript
dotenv.config(); // Now process.env.ANTHROPIC_API_KEY is available const client = new Anthropic(); // SDK automatically reads ANTHROPIC_API_KEY from env

Environment Variable Best Practices

PracticeWhy
Use .env only in developmentProduction should use secrets managers
Never log environment variablesLogs are often stored in plain text
Use different keys per environmentLimits blast radius of a compromise
Validate that the key exists on startupFail fast instead of at first API call
TypeScript
// Validate the key exists before proceeding function getApiKey(): string { const key = process.env.ANTHROPIC_API_KEY; if (!key) { throw new Error( "ANTHROPIC_API_KEY is not set. " + "Please set it in your environment or .env file." ); } if (!key.startsWith("sk-ant-")) { throw new Error( "ANTHROPIC_API_KEY does not look like a valid Anthropic key." ); } return key; }

The .gitignore Shield

Your .gitignore file is the last line of defense against accidentally committing secrets. Always include these entries:

Terminal
# .gitignore — Secrets and environment files .env .env.local .env.*.local .env.production .env.staging # OS and editor files that may contain cached secrets .DS_Store *.swp *.swo # Build and dependency directories node_modules/ dist/ build/

Pre-commit Hooks for Extra Safety

Use a tool like git-secrets or husky to scan for accidental key commits:

Terminal
# Install git-secrets brew install git-secrets # Register Anthropic key patterns git secrets --add 'sk-ant-[a-zA-Z0-9-]+' # Install hooks to block commits containing secrets git secrets --install

If someone accidentally tries to commit a file containing sk-ant-..., the commit will be rejected automatically.


Secrets Managers for Production

For production deployments, environment variables alone are not enough. Use a dedicated secrets manager:

Popular Secrets Managers

ServiceProviderKey Features
AWS Secrets ManagerAmazonRotation, fine-grained IAM policies, audit trail
Google Secret ManagerGoogle CloudVersioning, IAM integration, automatic replication
Azure Key VaultMicrosoftHSM-backed, access policies, certificate management
HashiCorp VaultOpen Source / EnterpriseMulti-cloud, dynamic secrets, leasing
DopplerDopplerDeveloper-friendly, syncs to all platforms

Example: AWS Secrets Manager

TypeScript
SecretsManagerClient, GetSecretValueCommand, } from "@aws-sdk/client-secrets-manager"; async function getAnthropicKey(): Promise<string> { const client = new SecretsManagerClient({ region: "us-east-1" }); const command = new GetSecretValueCommand({ SecretId: "prod/anthropic-api-key", }); const response = await client.send(command); if (!response.SecretString) { throw new Error("Secret not found or empty"); } const secret = JSON.parse(response.SecretString); return secret.ANTHROPIC_API_KEY; }

Example: Google Secret Manager

TypeScript
async function getAnthropicKey(): Promise<string> { const client = new SecretManagerServiceClient(); const [version] = await client.accessSecretVersion({ name: "projects/my-project/secrets/anthropic-api-key/versions/latest", }); const payload = version.payload?.data?.toString(); if (!payload) { throw new Error("Secret payload is empty"); } return payload; }

Key Rotation Strategy

Key rotation is the practice of periodically replacing your API keys with new ones. This limits the damage if a key is leaked without your knowledge.

Rotation Steps

  1. Generate a new key in the Anthropic Console
  2. Deploy the new key to your secrets manager or environment
  3. Verify that all services are using the new key
  4. Revoke the old key once you confirm everything works
  5. Log the rotation event for audit purposes

Automated Rotation Schedule

EnvironmentRotation FrequencyReason
DevelopmentEvery 90 daysLower risk, convenience
StagingEvery 60 daysModerate exposure
ProductionEvery 30 daysHighest security
After incidentImmediatelyContainment
TypeScript
// Log rotation events for auditing function logKeyRotation(environment: string, oldKeyPrefix: string): void { const event = { type: "API_KEY_ROTATION", environment, oldKeyPrefix: oldKeyPrefix.slice(0, 12) + "...", rotatedAt: new Date().toISOString(), rotatedBy: process.env.USER || "system", }; console.log(JSON.stringify(event)); // Send to your logging/SIEM system }

Server-Side Only — Never Expose Keys to the Client

API keys must never be sent to the browser, mobile app, or any client-side code. This is a non-negotiable rule.

Why Server-Side Only?

  • Browser JavaScript is fully visible to anyone with DevTools
  • Mobile apps can be decompiled to extract embedded strings
  • Client-side keys can be intercepted via network inspection
  • There is no way to secure a secret on the client

The Correct Architecture

Browser/App --> Your Backend Server --> Anthropic API (no key) (key stored here) (receives key)
TypeScript
// Express backend — key stays on the server const app = express(); const anthropic = new Anthropic(); // reads key from env app.post("/api/chat", async (req, res) => { // Authenticate YOUR user first const user = await authenticateUser(req); if (!user) { return res.status(401).json({ error: "Unauthorized" }); } // Then call Claude on their behalf const message = await anthropic.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 1024, messages: req.body.messages, }); res.json(message); });

Rate Limiting Your Own API

Even with server-side keys, you need to protect your backend from abuse. Implement rate limiting on your own endpoints:

TypeScript
// Limit each user to 20 requests per minute const apiLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: 20, message: { error: "Too many requests. Please try again in a minute.", }, keyGenerator: (req) => { // Rate limit by authenticated user, not IP return req.user?.id || req.ip; }, standardHeaders: true, legacyHeaders: false, }); app.use("/api/chat", apiLimiter);

Tiered Rate Limits

TypeScript
// Different limits for different user tiers function getRateLimit(userTier: string) { const limits: Record<string, number> = { free: 10, pro: 50, enterprise: 200, }; return rateLimit({ windowMs: 60 * 1000, max: limits[userTier] || 10, keyGenerator: (req) => req.user?.id || req.ip, }); }

CORS Configuration

Cross-Origin Resource Sharing (CORS) controls which domains can access your API. Misconfigured CORS can expose your endpoints to unauthorized frontends.

TypeScript
// WRONG — allows any website to call your API app.use(cors()); // Do not do this in production // RIGHT — only allow your own frontend app.use( cors({ origin: [ "https://myapp.com", "https://app.myapp.com", ], methods: ["GET", "POST"], allowedHeaders: ["Content-Type", "Authorization"], credentials: true, }) );

CORS Checklist

  • Only whitelist domains you own
  • Never use origin: "*" with credentials
  • Restrict methods to what your API actually supports
  • Keep the allowed headers list minimal

Audit Logging

Every API call should be logged for security review. This helps you detect anomalies, investigate incidents, and meet compliance requirements.

TypeScript
interface AuditLog { timestamp: string; userId: string; action: string; model: string; inputTokens: number; outputTokens: number; ip: string; userAgent: string; status: "success" | "error"; errorCode?: string; } function logApiCall(details: AuditLog): void { // Write to your logging infrastructure console.log(JSON.stringify(details)); } // Middleware to log every Claude API call app.post("/api/chat", async (req, res) => { const startTime = Date.now(); let status: "success" | "error" = "success"; let errorCode: string | undefined; try { const message = await anthropic.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 1024, messages: req.body.messages, }); logApiCall({ timestamp: new Date().toISOString(), userId: req.user.id, action: "messages.create", model: "claude-sonnet-4-20250514", inputTokens: message.usage.input_tokens, outputTokens: message.usage.output_tokens, ip: req.ip, userAgent: req.headers["user-agent"] || "unknown", status: "success", }); res.json(message); } catch (error: any) { status = "error"; errorCode = error.status?.toString() || "unknown"; logApiCall({ timestamp: new Date().toISOString(), userId: req.user?.id || "anonymous", action: "messages.create", model: "claude-sonnet-4-20250514", inputTokens: 0, outputTokens: 0, ip: req.ip, userAgent: req.headers["user-agent"] || "unknown", status: "error", errorCode, }); res.status(error.status || 500).json({ error: error.message }); } });

What to Monitor in Your Logs

SignalWhat It May Indicate
Sudden spike in requestsCompromised key or runaway loop
Requests from unknown IPsUnauthorized access
High error rate (401/403)Brute force or invalid key usage
Unusual hours of activityAutomated abuse
Large token consumptionPrompt injection or data exfiltration

Security Checklist

Use this checklist before deploying any Claude-powered application:

Key Management

  • API key is stored in environment variables or a secrets manager
  • API key is never hardcoded in source code
  • .env files are listed in .gitignore
  • Pre-commit hooks block accidental key commits
  • Different keys are used for dev, staging, and production
  • Key rotation schedule is documented and followed

Architecture

  • API calls are made server-side only
  • Client applications never see the API key
  • Backend authenticates users before proxying to Claude
  • Request validation sanitizes user input before sending to Claude

Rate Limiting & Access Control

  • Rate limiting is applied per user, not just per IP
  • Tiered limits match your user plans
  • CORS is restricted to your own domains
  • Authentication is required for all API-proxying endpoints

Monitoring & Logging

  • Every API call is logged with user ID, timestamps, and token usage
  • Alerts are set for unusual activity patterns
  • Logs are stored securely and reviewed periodically
  • Incident response plan exists for key compromise

Incident Response

  • You know how to revoke a key immediately
  • You have a backup key ready for emergencies
  • Team members know the escalation path
  • Post-incident review process is documented

What Happens When a Key Is Compromised

If you suspect a key has been leaked:

  1. Revoke the key immediately in the Anthropic Console
  2. Generate a new key and deploy it
  3. Review audit logs for unauthorized usage
  4. Check your bill for unexpected charges
  5. Investigate the source of the leak (git history, logs, etc.)
  6. Report to Anthropic if you see significant unauthorized usage
  7. Document the incident and update your security procedures

Summary

TopicKey Takeaway
API Key AnatomyKeys start with sk-ant-, grant full access, and must be protected
Environment VariablesUse .env for dev, secrets managers for production
.gitignoreAlways exclude .env and secret files from version control
Server-Side OnlyNever expose keys in client-side code
Key RotationRotate regularly: 30 days for production, immediately after incidents
Rate LimitingProtect your backend with per-user rate limits
CORSWhitelist only your own domains
Audit LoggingLog every API call for security and compliance

Next: We will explore rate limits and scaling strategies to handle high-traffic production workloads.