Claude API HTTP Request Example with curl
If you want to send a raw HTTP request to Claude without installing an SDK, curl is the fastest way to see exactly what the API expects and returns. This article walks through a complete, working curl example, explains every header and field, and covers the mistakes that cause most first requests to fail.
The short answer: you POST a JSON body to https://api.anthropic.com/v1/messages with three required headers (x-api-key, anthropic-version, content-type), and the body needs at minimum a model, a max_tokens value, and a messages array. Here's the full example.
A minimal curl request to Claude
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Explain what a race condition is in two sentences." }
]
}'
If this succeeds, you get back a JSON object that looks roughly like:
{
"id": "msg_01XYZ...",
"type": "message",
"role": "assistant",
"content": [
{ "type": "text", "text": "A race condition occurs when..." }
],
"model": "claude-sonnet-4-20250514",
"stop_reason": "end_turn",
"usage": { "input_tokens": 15, "output_tokens": 42 }
}
Breaking down the required headers
x-api-key— your secret key, not anAuthorization: Bearerheader. This trips people up because most other APIs use Bearer tokens.anthropic-version— a date string that pins the API version you're coding against. Omitting it either defaults to the latest version (which can change behavior under you) or errors, depending on the endpoint.content-type: application/json— required for the body to be parsed correctly.
Breaking down the request body
model— the specific model ID, e.g.claude-opus-4-20250514orclaude-sonnet-4-20250514. There's no "default model" fallback, so a missing or misspelled model string returns a 404-style error.max_tokens— the maximum number of tokens Claude is allowed to generate in the response. This is required, not optional, and it caps output length, not input length.messages— an array of turns, each with arole(userorassistant) andcontent. For simple text,contentcan be a plain string; for multi-part input (text + images, or tool results) it's an array of content blocks.
Adding a system prompt
System instructions are a separate top-level field, not a message with role: system:
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"system": "You are a terse code reviewer. Reply in bullet points only.",
"messages": [
{ "role": "user", "content": "Review this function: function add(a,b){return a+b}" }
]
}'
Streaming with curl
Add "stream": true and pass -N to curl to disable output buffering, so you see server-sent events as they arrive instead of all at once:
curl -N https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"stream": true,
"messages": [{ "role": "user", "content": "Count from 1 to 10." }]
}'
You'll get a sequence of event: / data: lines (message_start, content_block_delta, message_stop, etc.) rather than one JSON blob.
Common curl mistakes with the Claude API
- Using
Authorization: Bearerinstead ofx-api-key. This is the single most common error for people coming from OpenAI's API. - Forgetting
max_tokens. The request is rejected outright if it's missing. - Missing
anthropic-version. You'll get a 400 error telling you the header is required. - Sending
contentas a bare string when using multi-modal input. Images and documents need the array-of-blocks format, not a plain string. - Not checking
stop_reason. A response can stop because it hitmax_tokensrather than finishing naturally — if your output looks cut off, check this field before assuming something else is wrong.
If you want this without managing raw API keys
Calling api.anthropic.com directly with curl is the right way to learn the request/response shape and to debug fast. But if you're building something that needs per-application keys, usage tracking per key, team billing, or you just want to avoid wiring up streaming and tool-call parsing from scratch every time, a thin layer on top helps.
SubToAPI turns your existing Claude access into an HTTPS API with its own sub_live_... keys, so you can issue a separate key per app or environment, see usage per key in a dashboard, and share access across a team without sharing one shared secret. The request shape is close enough to what you already wrote above that migrating a curl script is mostly a URL and header swap — see the quickstart and the messages docs for the exact fields. Streaming works the same way curl shows above, documented at /docs/streaming, and tool use is covered at /docs/tools. Plans start at €9/month with a free trial at signup; full pricing is at /pricing.
questions
Do I need the Anthropic SDK to call Claude, or is curl enough? curl is enough for testing, scripting, or any language without an official SDK. The API is plain HTTPS with a JSON body, so any HTTP client works — curl is just the quickest way to verify headers and payload shape before writing real code.
Why does my curl request return a 401 or "authentication_error"? Almost always because the key is passed as Authorization: Bearer <key> instead of the required x-api-key: <key> header, or the key itself is invalid/revoked. Double-check the header name first.
Why do I get an error about a missing max_tokens field? max_tokens is required on every request to the Messages endpoint — there's no default. Add an integer value (e.g. 1024) to the JSON body and the request will go through.