Protect API Keys

Su Suriya Ravichandran Updated 10 Nov 2026
5 min read ·Lesson 171 of 200

The Key Is the Account

An API key is a bearer credential: whoever holds it can make requests as the account that issued it, and the bill for those requests goes to that account. Nothing else is checked. There is no second factor, no device, and no location that must match. For that reason the key deserves the same care as a bank password, and an unprotected key is one of the most common ways that a model integration causes real damage, through unexpected charges, data exposure, or misuse of the account's privileges.

This lesson covers where keys should and should not appear, how to scope them so that a leaked key does less harm, how to recognize a key in text so that it can be removed, and what to do when a key has been exposed. The lessons that follow build on this one: .env files (Lesson 172), version control (Lesson 173), and usage limits (Lesson 179) all assume the key has been handled correctly from the start.


Where a Key Must Never Go

A key is a secret in the same sense that a password is, and it tends to leak through the places developers use most. The list below is the working checklist:

LocationWhy it leaks
Source codeEvery copy of the repository, and every clone, carries the key
Version control historyDeleting the file later does not remove the key from earlier commits
Logs and error messagesLog aggregators are read by many people, and they are kept for a long time
Screenshots and shared documentsPasted into chat, tickets, or slides, the key is visible to everyone who sees them
Client-side codeA key in a web page or a mobile app can be read by anyone who downloads the app
Request URLs and query stringsURLs are recorded by proxies, browsers, and servers

The client-side row deserves emphasis. An application that calls the model from a browser or a phone must send its requests through a server that holds the key, never directly from the device. The key is a server-side secret.


Scoping Keys to Limit the Damage

A single key that works everywhere is a single point of failure. Scoping divides the risk: each environment, each service, and each team uses its own key, so that a leak affects one part of the system, and revoking the key does not disrupt the rest. Keys can be organized by workspace, and a workspace can be given its own spending limits (Lesson 179). The rule is simple: a key should have the narrowest scope that still lets its service work.

Scoping also makes investigation possible. When a key appears in an unexpected place, its scope shows which service it belonged to, and which set of logs to search (Lesson 180).


Recognizing a Key in Text

A key that appears in logs, documentation, or a chat transcript should be found and removed. Keys have a recognizable prefix, which makes them detectable by pattern. The function below finds keys in text and replaces them with a placeholder, so the text can be stored or shared safely. The pattern is a sanity check on the prefix and the characters that follow it, and it should be combined with a scanner in the version control system (Lesson 173):

"""redact_keys.py: find API keys in text and replace them before the text is stored or shared."""

import re

KEY_PATTERN = re.compile(r"sk-ant-[A-Za-z0-9_-]{10,}")


def redact(text: str) -> str:
    return KEY_PATTERN.sub("[REDACTED KEY]", text)


if __name__ == "__main__":
    line = "Calling the API with key sk-ant-api03-EXAMPLEkeyvalue123 for the batch job."
    print(redact(line))
    print("contains a key after redaction:", bool(KEY_PATTERN.search(redact(line))))

Output:

Calling the API with key [REDACTED KEY] for the batch job.
contains a key after redaction: False

How It Works

  1. The pattern matches the key prefix and what follows it. The characters after the prefix must be at least ten letters, digits, underscores, or hyphens, which covers the body of a key without matching ordinary words.
  2. Replacement is complete for every match. sub replaces each occurrence, so a line that mentions two keys loses both.
  3. The check at the end confirms the result. After redaction, the pattern no longer matches, which is the property that makes the text safe to store.
  4. The example key is fabricated. It has the shape of a key, and no real credential appears anywhere in this lesson.

The pattern cannot find every secret. It finds values that look like this platform's keys, and a key stored in a different format, or a password, needs its own pattern or a scanner. Redaction is a last line of defense, and the first line is never letting the key reach the text at all.


When a Key Has Been Exposed

A key that has been seen by someone it should not have reached must be treated as compromised, even if there is no evidence of misuse. The response has a fixed order. First, revoke the key, so that it stops working. Second, create a replacement and deploy it to the services that use it. Third, review the usage logs for the period when the key was exposed, to see whether it was used by someone else (Lesson 180). Fourth, find and remove every copy of the key, including in version control history, and rotate any other secret that was stored alongside it.

The order matters. Revoking first stops the damage, and replacing second restores service. Investigating before revoking lets the exposure continue while the investigation runs.


Summary

An API key is a bearer credential that authorizes spending and access, and it must never appear in source code, logs, screenshots, client-side code, or URLs. Scoping keys by environment and service confines the damage from a leak, and pattern-based redaction removes keys from text that must be stored or shared. An exposed key is revoked first, replaced second, investigated third, and purged everywhere it was copied. The next lesson shows how a .env file keeps local keys out of the code while still making them available to the application.

0 Comments

Reviewed before they appear

No comments yet.

Anthropic SDK Using Python
Anthropic SDK Intro Claude API Intro SDK Requirements Install Anthropic SDK Upgrade the SDK Create an API Key Set API Keys Use Environment Variables Import Anthropic Create a Client Send Your First Message Choose a Claude Model Set Max Tokens Read a Response Print Response Text Understand Message Blocks Use System Prompts Send User Messages Set Temperature Handle Stop Reasons Messages API Intro Create Messages Use Message Roles Send Conversation History Continue a Conversation Build a Chat Loop Reset Chat History Use Multiple Turns Inspect Message IDs Read Usage Data Write Clear Prompts Set a System Prompt Add Context to Prompts Use Prompt Templates Add Examples to Prompts Request JSON Output Use XML Tags Control Response Length Improve Prompt Quality Avoid Prompt Injection Read Text Blocks Read Content Blocks Check Stop Reasons Count Input Tokens Count Output Tokens Count Tokens Before Sending Limit Output Tokens Use Stop Sequences Read Request IDs Parse API Responses Streaming Intro Stream Text Output Use Stream Events Use Stream Helpers Get Final Messages Stream Async Responses Handle Stream Errors Build a Live Chat Stream Long Responses Close Streams Safely Build a Chatbot Keep Chat History Manage Chat Sessions Add Chat Memory Build a CLI Chatbot Build a Web Chatbot Use Flask with Claude Use FastAPI with Claude Return JSON from an API Add Chat Error Messages Async SDK Intro Create an Async Client Send Async Messages Use asyncio Run Parallel Requests Stream Async Text Handle Async Errors Set Async Timeouts Close Async Clients Choose Sync or Async Tool Use Intro Define a Tool Create Tool Schemas Send Tools to Claude Read Tool Calls Run Python Tools Return Tool Results Continue After Tool Use Control Tool Choice Handle Tool Errors Use the beta_tool Decorator Create a Tool Runner Add Tool Docstrings Validate Tool Inputs Use Multiple Tools Limit Tool Calls Log Tool Activity Build a Weather Tool Build a Search Tool Build a Calculator Tool Structured Output Intro Request JSON Data Validate JSON Output Use Pydantic Models Handle Invalid JSON Extract Fields from Text Generate Typed Data Use Image URLs Send Base64 Images Read PDFs with Claude Use Output Schemas Build Data Extraction Test Structured Responses Send Image Inputs Upload Files Use File IDs Process Text Files Summarize Documents Extract Table Data Handle File Errors Read Cache Usage Reduce Repeated Input Costs Debug Cache Misses Prompt Caching Intro Enable Prompt Caching Cache System Prompts Cache Long Context Test Cache Performance Use Cache Breakpoints Cache Best Practices Message Batches Intro Create a Message Batch Set Batch IDs Check Batch Status List Message Batches Get Batch Results Process Batch Outputs Handle Failed Batch Items Use Batches for Bulk Tasks Compare Batches and Requests Handle API Errors Handle Rate Limits Retry Failed Requests Set Request Timeouts Handle Network Errors Use Exponential Backoff Build Reliable API Calls Use Extended Thinking Log API Errors Track Request IDs Paginate API Results Set Thinking Budgets Read Thinking Blocks Use Tool Search Use Web Search Tools Use Computer Tools Use Citations Use Token Counting Read Raw Responses Set Custom Headers Use Claude on AWS Bedrock Use Claude on Vertex AI Use Claude on Microsoft Foundry Configure Cloud Credentials Choose a Cloud Provider Compare API Integrations Configure Provider Clients Handle Cloud Errors Use Cloud SDK Extras Deploy Cloud Apps Protect API Keys Use a .env File Ignore Secrets in Git Validate User Input Reduce Prompt Injection Limit Tool Permissions Avoid Unsafe Tool Calls Sanitize Tool Results Set Usage Limits Review Security Logs Test SDK Functions Mock API Responses Test Error Handling Test Streaming Code Measure Token Usage Track API Costs Configure Logging Deploy with Docker Deploy a FastAPI App Monitor Production Apps Build a Claude Chatbot Build a Text Summarizer Build a Code Explainer Build a JSON Extractor Build a PDF Assistant Build a Streaming Chat App Build a Tool-Calling Agent Build a Batch Processor Build a Research Assistant Build a Claude API Dashboard
Ask about this post
AI Ask about this post

Ask questions about Protect API Keys and get answers drawn from it.

Signed-in readers only.