Claude API curl Request Example (Copy-Paste Ready)
If you're looking for a Claude API curl request example that actually works on the first try, here it is:
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 curl in one sentence."}
]
}'
That single request hits Anthropic's Messages endpoint, authenticates with your API key, and returns a JSON response containing Claude's reply. The rest of this article breaks down every part of that command, shows variations (streaming, system prompts, multi-turn conversations), and covers the most common errors people hit when testing the Claude API from the command line.
Why curl is the right first step
Before wiring Claude into a frontend, a backend service, or a CI pipeline, it's worth proving the request works with nothing but curl. It removes SDK version issues, framework quirks, and language-specific serialization bugs from the equation. If curl works, you know the problem is in your code, not your credentials or payload. If curl fails, you know it's a request or auth issue, not an app issue.
Breaking down the request
Endpoint: https://api.anthropic.com/v1/messages is the core endpoint for chat-style completions. There's no separate "completions" vs "chat" endpoint — Messages handles both single-turn and multi-turn conversations.
Headers: Three headers matter here:
x-api-key— your Anthropic API key, not a Bearer token. This trips up people used to OpenAI's auth scheme.anthropic-version— a required date-versioned string that pins the API shape you're coding against. Omitting it causes requests to default to the latest version, which can silently change behavior over time.content-type: application/json— standard, but easy to forget when copy-pasting from memory.
Body fields:
model— which Claude model to use (e.g.claude-sonnet-4-5,claude-opus-4, or a specific dated snapshot).max_tokens— the maximum number of tokens Claude can generate in the response. This is required, not optional.messages— an array of{role, content}objects. Roles alternate betweenuserandassistant.
Adding a system prompt
System prompts are a top-level field, not a message with role: "system":
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,
"system": "You are a terse technical writer. Answer in one short paragraph.",
"messages": [
{"role": "user", "content": "What is a REST API?"}
]
}'
Multi-turn conversations
To continue a conversation, append the assistant's previous reply and the new user message to the messages array. Anthropic's API is stateless — it doesn't remember prior calls, so your client has to resend the full history each time:
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": "What is the capital of France?"},
{"role": "assistant", "content": "Paris."},
{"role": "user", "content": "What is its population?"}
]
}'
Streaming responses with curl
Add "stream": true and -N to disable curl's output buffering so you see tokens as they arrive:
curl -N 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,
"stream": true,
"messages": [
{"role": "user", "content": "Write a haiku about terminals."}
]
}'
You'll get a stream of server-sent events (message_start, content_block_delta, message_stop, etc.) rather than one JSON blob.
Common curl errors and fixes
- 401 Unauthorized — usually means a missing or malformed
x-api-keyheader, or the key has been revoked. - 400 invalid_request_error: max_tokens — this field is required; curl requests without it fail immediately.
- 400 overloaded_error / 529 — Anthropic's infrastructure is under heavy load; retry with backoff.
- Trailing comma or unescaped quotes in JSON — the most common cause of silent curl failures. Validate your JSON body with
jqbefore sending:echo "$BODY" | jq .
Turning your curl request into a production API
A raw curl command is fine for testing, but production apps usually need more: per-application API keys instead of one shared secret, usage tracking per key, team seat management, and a stable HTTPS surface you don't have to re-document every time Anthropic ships a new API version.
That's the gap SubToAPI fills. It takes your existing Claude access and exposes it as a clean HTTPS API with sub_live_... keys you can issue per app or per customer, full streaming support, tool use, and usage metadata in one dashboard — so the curl command above becomes:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Explain curl in one sentence."}
]
}'
Same shape, same mental model, but with scoped keys and a dashboard instead of one shared secret buried in an environment variable. Start with a free trial at /signup, check the /docs/quickstart for the full setup, and see /docs/streaming and /docs/tools for the streaming and tool-use equivalents of the examples above.
FAQ
Do I need the Anthropic SDK to call the Claude API, or is curl enough? curl is enough for testing, scripting, and even production use if your stack doesn't need SDK conveniences like automatic retries or typed responses. Many teams use curl for quick checks and an SDK or a wrapper like SubToAPI for actual app code.
Why does my curl request return 401 even though I copied my key correctly? Check that you're using x-api-key, not Authorization: Bearer, for direct Anthropic API calls — mixing up auth schemes from other APIs is the most common cause. Also confirm the key hasn't been rotated or revoked in your account.
Can I test streaming with curl, or do I need a special tool? Plain curl works for streaming — just add "stream": true to the request body and pass -N to curl so it doesn't buffer the output before printing it.