Claude API Authentication Headers: Example Code
Every request to Anthropic's Claude API needs three headers to authenticate and be understood correctly: x-api-key, anthropic-version, and content-type. Miss one of these and you'll get a 401 or 400 error instead of a completion. This article shows the exact header format, working code in curl and JavaScript, and the mistakes that most commonly break authentication.
The short answer: Claude does not use the Authorization: Bearer pattern that OpenAI and most REST APIs use. Instead, your API key goes into a custom header called x-api-key. If you copy-paste boilerplate from another provider's docs, this is almost always the first thing that breaks.
The Required Headers
Here's the minimal header set for any request to https://api.anthropic.com/v1/messages:
x-api-key: sk-ant-api03-xxxxxxxxxxxxxxxxxxxxx
anthropic-version: 2023-06-01
content-type: application/json
x-api-key— your secret key, generated in the Anthropic Console. Never prefix it withBearer.anthropic-version— a date-versioned string that pins the API behavior you're coding against. Anthropic updates this periodically; omitting it either fails outright or silently defaults to a version you didn't test against.content-type— must beapplication/jsonfor the/v1/messagesendpoint.
curl Example
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-20250514",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Explain what a vector database is in two sentences."}
]
}'
Store your key in an environment variable rather than hardcoding it. If you're testing locally, export ANTHROPIC_API_KEY=sk-ant-... in your shell before running curl.
JavaScript / Node.js Example
Using fetch directly, without any SDK:
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-20250514",
max_tokens: 1024,
messages: [
{ role: "user", content: "Summarize the plot of Hamlet in one paragraph." },
],
}),
});
const data = await response.json();
console.log(data.content[0].text);
This is the whole authentication flow — no OAuth handshake, no token refresh cycle, no signing algorithm. It's a static key in a header, which is simple but also means you're responsible for keeping it out of client-side code, logs, and version control.
Common Authentication Errors
401 Unauthorized — "invalid x-api-key" Usually means the key is malformed, expired, or you used Authorization: Bearer instead of x-api-key. Double-check you copied the full key string, including the sk-ant- prefix.
400 Bad Request — missing anthropic-version This header is mandatory. If you leave it out, some client libraries will silently inject a default, but raw fetch or curl calls will fail immediately.
403 Forbidden Your key is valid but doesn't have access to the requested model or feature (for example, a model still in limited access). Check the Anthropic Console for which models your account can call.
Key exposed in a public repo If you accidentally commit a key, rotate it immediately in the console — old keys don't expire automatically.
Server-Side vs Client-Side Requests
Because x-api-key is a long-lived secret with no scoping by default, you should never call the Claude API directly from a browser or mobile app. Anyone opening dev tools sees your key and can drain your usage limits. The standard pattern is:
- Your frontend calls your own backend.
- Your backend attaches
x-api-keyand forwards the request to Anthropic. - Your backend streams or returns the response to the frontend.
This adds a maintenance burden: you now own a proxy layer, plus key rotation, per-user rate limiting, and usage tracking if you want to bill different app users differently.
Simplifying Auth for Multi-User Apps
If you're building a product on top of Claude rather than a personal script, raw x-api-key headers get harder to manage once you have multiple environments, team members, or app users who each need scoped access. SubToAPI sits on top of your existing Claude access and issues its own application-level keys (sub_live_...) that you can create per app, per environment, or per customer, without touching your underlying Anthropic key.
Authenticating against SubToAPI still uses a familiar header pattern:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello"}]
}'
Here it's standard Authorization: Bearer, so it drops into existing HTTP client setups, SDKs, and API testing tools without custom header handling. You also get streaming, tool use, usage metadata per key, and team seats in one dashboard — useful if multiple developers or services need independent, revocable credentials instead of one shared secret. Start with a free trial at /signup, check /pricing for plan details, or read the /docs/quickstart for a full setup walkthrough. Header and request formats for messages and streaming are documented at /docs/messages and /docs/streaming.
Quick Checklist
Before you debug further, verify:
- Key uses
x-api-key, notAuthorization: Bearer(unless you're using a proxy like SubToAPI that explicitly uses Bearer) anthropic-versionheader is present and set to a valid date stringcontent-type: application/jsonis set- The key hasn't been rotated or revoked in the console
- Request body matches the JSON schema for
/v1/messages— malformed JSON returns a 400, not a 401, but it's easy to confuse the two when debugging
Questions
Does Claude API authentication use Bearer tokens? No. Anthropic's native API uses a custom x-api-key header with your raw key, not Authorization: Bearer. Some third-party layers built on top of Claude, like SubToAPI, do use standard Bearer tokens.
What happens if I omit the anthropic-version header? The request will typically fail with a 400 error, since the API needs to know which version of the request/response schema to apply. Always set it explicitly rather than relying on a default.
Can I call the Claude API directly from frontend JavaScript? Technically yes, but you shouldn't — your API key would be visible to anyone inspecting network requests. Route calls through a backend or a managed layer that issues scoped, revocable keys instead.