🔑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:
| Property | Detail |
|---|---|
| Prefix | Anthropic keys typically start with sk-ant- |
| Length | Usually 80+ characters |
| Encoding | Alphanumeric with hyphens |
| Scope | Full access to the API under your account |
| Revocability | Can 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
// 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
// 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:
npm install dotenvCreate a .env file in your project root:
# .env — NEVER commit this file
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
NODE_ENV=developmentLoad it at the top of your entry file:
dotenv.config();
// Now process.env.ANTHROPIC_API_KEY is available
const client = new Anthropic();
// SDK automatically reads ANTHROPIC_API_KEY from envEnvironment Variable Best Practices
| Practice | Why |
|---|---|
Use .env only in development | Production should use secrets managers |
| Never log environment variables | Logs are often stored in plain text |
| Use different keys per environment | Limits blast radius of a compromise |
| Validate that the key exists on startup | Fail fast instead of at first API call |
// 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:
# .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:
# 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 --installIf 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
| Service | Provider | Key Features |
|---|---|---|
| AWS Secrets Manager | Amazon | Rotation, fine-grained IAM policies, audit trail |
| Google Secret Manager | Google Cloud | Versioning, IAM integration, automatic replication |
| Azure Key Vault | Microsoft | HSM-backed, access policies, certificate management |
| HashiCorp Vault | Open Source / Enterprise | Multi-cloud, dynamic secrets, leasing |
| Doppler | Doppler | Developer-friendly, syncs to all platforms |
Example: AWS Secrets Manager
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
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
- Generate a new key in the Anthropic Console
- Deploy the new key to your secrets manager or environment
- Verify that all services are using the new key
- Revoke the old key once you confirm everything works
- Log the rotation event for audit purposes
Automated Rotation Schedule
| Environment | Rotation Frequency | Reason |
|---|---|---|
| Development | Every 90 days | Lower risk, convenience |
| Staging | Every 60 days | Moderate exposure |
| Production | Every 30 days | Highest security |
| After incident | Immediately | Containment |
// 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)
// 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:
// 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
// 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.
// 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.
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
| Signal | What It May Indicate |
|---|---|
| Sudden spike in requests | Compromised key or runaway loop |
| Requests from unknown IPs | Unauthorized access |
| High error rate (401/403) | Brute force or invalid key usage |
| Unusual hours of activity | Automated abuse |
| Large token consumption | Prompt 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
-
.envfiles 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:
- Revoke the key immediately in the Anthropic Console
- Generate a new key and deploy it
- Review audit logs for unauthorized usage
- Check your bill for unexpected charges
- Investigate the source of the leak (git history, logs, etc.)
- Report to Anthropic if you see significant unauthorized usage
- Document the incident and update your security procedures
Summary
| Topic | Key Takeaway |
|---|---|
| API Key Anatomy | Keys start with sk-ant-, grant full access, and must be protected |
| Environment Variables | Use .env for dev, secrets managers for production |
| .gitignore | Always exclude .env and secret files from version control |
| Server-Side Only | Never expose keys in client-side code |
| Key Rotation | Rotate regularly: 30 days for production, immediately after incidents |
| Rate Limiting | Protect your backend with per-user rate limits |
| CORS | Whitelist only your own domains |
| Audit Logging | Log every API call for security and compliance |
Next: We will explore rate limits and scaling strategies to handle high-traffic production workloads.