How to Use an Anthropic API Key in Your Code
Once you have an Anthropic API key, the next step is putting it to work: authenticating requests, sending messages to Claude, and handling responses correctly. This guide covers exactly how to use your key in real code, what headers are required, and the mistakes that trip up most developers on their first request.
The short version: you pass your key as an x-api-key header on every request to https://api.anthropic.com/v1/messages, along with an anthropic-version header and a JSON body describing the model and messages. Below is the full picture, including how streaming, tool use, and error handling fit in.
Setting up your key as an environment variable
Never hardcode your API key in source files. Store it as an environment variable and load it at runtime.
export ANTHROPIC_API_KEY="sk-ant-..."
In your code:
const apiKey = process.env.ANTHROPIC_API_KEY;
This keeps the key out of version control and makes it easy to rotate or swap between environments (dev, staging, production) without touching code.
Making your first request with curl
The Messages API is the primary endpoint you'll use. Here's a minimal request:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Explain what a race condition is."}
]
}'
Three headers matter here:
x-api-key— your authentication credentialanthropic-version— a required date-stamped version string for the APIcontent-type— alwaysapplication/json
Miss any of these and you'll get a 400 or 401 error rather than a response.
Using the key in JavaScript
Most developers use the official SDK rather than raw fetch calls, but it helps to understand what's happening underneath:
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Write a haiku about databases." }],
}),
});
const data = await response.json();
console.log(data.content[0].text);
If you're using the official SDK instead, the key is passed once when you instantiate the client, and the SDK handles headers, retries, and error types for you.
Streaming responses
For chat interfaces or anything with a visible response time, you'll want streaming rather than waiting for the full completion. Set "stream": true in the request body and process Server-Sent Events as they arrive:
const stream = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
stream: true,
messages: [{ role: "user", content: "List five debugging tips." }],
}),
});
Each chunk arrives as a data: line with a JSON payload describing content deltas, so you parse and append text incrementally rather than waiting for the full body.
Using the key for tool calls
If your app needs Claude to call functions — looking up data, running calculations, hitting internal APIs — you define tools in the request and the key authenticates that call the same way as a plain message. The response will include a tool_use block when Claude decides to invoke a tool, and you send the result back in a follow-up message with the same key and headers.
Common errors and what they mean
- 401 Unauthorized — the key is missing, malformed, or revoked. Double-check the environment variable is actually set in the process running your code.
- 429 Too Many Requests — you've hit a rate limit. Add backoff and retry logic rather than hammering the endpoint.
- 400 Bad Request — usually a missing
anthropic-versionheader or malformed JSON body. - 529 Overloaded — the API is temporarily at capacity; retry with exponential backoff.
Wrapping your requests in a retry helper that checks status codes saves you from flaky failures in production.
When a single key isn't enough
A raw Anthropic API key gives you one shared credential for everything. That works fine for a solo prototype, but it breaks down once you're shipping a product: you can't issue separate keys per customer or environment, there's no per-key usage breakdown, and you have to build your own streaming and tool-use plumbing on top.
This is the gap SubToAPI fills. It sits on top of your existing Claude access and issues application-facing keys (sub_live_...) that behave like a standard HTTPS API — streaming, tool use, and usage metadata included, with team seats so multiple people or environments can work off one underlying subscription. If you're past the "just call the API from a script" stage and building something you'll actually ship, check the quickstart or the messages and streaming docs to see how the request shape compares to using a raw Anthropic key directly. Plans start at €9/month with a free trial at signup.
questions
Do I need a different key for each model? No. A single Anthropic API key works across all available Claude models — you select the model per request using the model field in the request body, not through separate credentials.
Can I use my API key in frontend/browser code? No. API keys should only be used server-side. Exposing a key in client-side JavaScript lets anyone extract it from the browser and rack up usage on your account.
What's the difference between using the key directly and through a service like SubToAPI? Using the raw key means you build your own request handling, streaming, and per-user usage tracking. A layer like SubToAPI issues scoped application keys and handles streaming, tool use, and usage metadata for you — see the tools docs for how tool calls are structured.