← Blog

Claude API Request Response Format Explained

2026-09-23 · 5 min read · SubToAPI Team

What the Claude API request and response format looks like

The Claude API uses a JSON-based request/response format built around a messages array, similar in shape to other modern LLM APIs but with a few Claude-specific fields. A request is a POST with a model, a messages array, and a max_tokens value; the response is a JSON object containing a content array, a stop_reason, and a usage object with token counts.

If you're integrating Claude for the first time, the fastest way to understand the format is to look at a minimal request and its corresponding response side by side, then walk through what each field actually means and when it changes shape (tool use, streaming, multi-turn conversations).

The request format

A standard request to Claude's Messages API looks like this:

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-20250929",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Explain what a race condition is."}
    ]
  }'

Key fields:

One detail that trips people up: content is not always a string. For multi-modal input or when you're passing tool results back, content becomes an array of typed blocks, e.g. {"type": "text", "text": "..."} or {"type": "image", "source": {...}}.

The response format

A non-streaming response has this general shape:

{
  "id": "msg_01XYZ...",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-4-5-20250929",
  "content": [
    {
      "type": "text",
      "text": "A race condition occurs when..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 14,
    "output_tokens": 187
  }
}

Fields worth knowing:

How the format changes with tool use

When Claude decides to call a tool, stop_reason becomes "tool_use" and content includes a block like:

{
  "type": "tool_use",
  "id": "toolu_01A...",
  "name": "get_weather",
  "input": {"location": "Berlin"}
}

Your application executes the tool, then sends a follow-up request where the messages array includes a tool_result content block referencing that id. This request/response round-trip is the core pattern for function calling with Claude — see the tools documentation for the full schema.

How the format changes with streaming

With "stream": true, instead of one JSON object you get a sequence of SSE events: message_start, content_block_start, content_block_delta (repeated), content_block_stop, message_delta, and message_stop. Each content_block_delta carries a small text fragment, and you concatenate them to reconstruct the full response. The final message_delta event carries the cumulative usage and stop_reason.

Simplifying the format with SubToAPI

If you're already paying for Claude through a subscription and want an HTTP API without managing separate API billing, SubToAPI turns your existing Claude access into a standard HTTPS endpoint. The request and response format is intentionally close to what's described above — messages, max_tokens, content blocks, usage, stop_reason — so anything you build against the standard Messages format works with minimal changes. You authenticate with an application key (sub_live_...) instead of managing raw provider credentials:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5-20250929",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Explain what a race condition is."}
    ]
  }'

The response comes back in the same shape — content array, stop_reason, usage — so you can point existing code at it. Full field-by-field reference is in the messages docs, streaming details are in the streaming docs, and a working end-to-end example is in the quickstart. Plans start at €9/month with a free trial at signup; seat pricing for teams is on the pricing page.

Practical tips for working with the format

Questions

Is the Claude API request format the same for every model version? Yes, the messages/max_tokens/content structure is stable across model versions — you generally only change the model string, not the request shape.

Why is content an array instead of a plain string in the response? Because a single response can contain multiple typed blocks — text, tool calls, or (in some cases) multiple text segments — so the array format keeps the schema consistent whether there's one block or several.

Do I need to parse SSE manually for streaming responses? You need to handle the event stream (accumulating content_block_delta events), but most official and third-party SDKs, including standard HTTP clients against SubToAPI's streaming endpoint, handle the parsing for you.

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 →