← Blog

Claude API HTTP Request Example with curl

2026-10-25 · 4 min read · SubToAPI Team

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

Breaking down the request body

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

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.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →