Claude API Request Body Format: Full Example Guide
The Claude API Request Body Format
A Claude API request body is a JSON object sent to the /v1/messages endpoint. At minimum it needs three fields: model, max_tokens, and messages. The messages field is an array of objects, each with a role (user or assistant) and content, which can be a plain string or an array of content blocks for more advanced use cases like images or tool results.
Here's the simplest valid request body:
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Explain what a REST API is in two sentences." }
]
}
That's the core shape you'll use for 90% of requests. Everything else — system prompts, tool definitions, multi-turn history, streaming — builds on top of this same structure. Let's go through each field and the common variations you'll actually need.
Required Fields
model
A string identifying which Claude model to use, e.g. claude-sonnet-4-5 or claude-opus-4. If this is omitted or misspelled, you'll get a validation error, not a fallback.
max_tokens
An integer capping the length of the model's response. This isn't optional — Claude's API rejects requests without it. It doesn't guarantee that many tokens will be generated; it's a ceiling, not a target.
messages
An array of turn objects. Roles alternate between user and assistant. You don't send a system role inside this array — that's a separate top-level field (covered below).
Each message's content can be:
- A string, for plain text turns.
- An array of content blocks, when you need multiple pieces (text + image, or text + tool_use/tool_result).
Example with multi-turn history:
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "What's the capital of Portugal?" },
{ "role": "assistant", "content": "Lisbon." },
{ "role": "user", "content": "And its population?" }
]
}
Optional Fields You'll Use Often
system
A top-level string (or array of blocks) that sets behavior and context before the conversation starts. It is not part of messages.
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"system": "You are a concise technical writer. Answer in bullet points only.",
"messages": [
{ "role": "user", "content": "Summarize the benefits of HTTP/2." }
]
}
temperature
A float between 0 and 1 controlling randomness. Lower values (0–0.3) are better for deterministic tasks like extraction or classification; higher values (0.7–1) suit creative writing.
stop_sequences
An array of strings that, if generated, stop the response immediately.
stream
A boolean. When true, the response comes back as server-sent events instead of a single JSON object — useful for chat UIs where you want tokens to appear incrementally.
Content Blocks: Images and Tool Use
When content needs to carry more than text, it becomes an array of typed blocks.
Image input example:
{
"role": "user",
"content": [
{ "type": "text", "text": "What's in this image?" },
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "<base64-encoded-data>"
}
}
]
}
Tool definitions go in a top-level tools array, and tool results are sent back as tool_result content blocks on the next user turn:
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}
],
"messages": [
{ "role": "user", "content": "What's the weather in Porto?" }
]
}
Claude will respond with a tool_use block containing the input it wants to call the function with. Your code executes the function and sends the result back as a tool_result block in a new user message — the request body format stays consistent, you're just appending to the messages array each turn.
A Full Example Request with curl
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-5",
"max_tokens": 1024,
"system": "You are a helpful assistant.",
"temperature": 0.5,
"messages": [
{ "role": "user", "content": "List three uses for the Claude API." }
]
}'
Same Request Through SubToAPI
If you're building a product on top of Claude and want a hosted API key, streaming, and usage metadata without managing Anthropic billing directly, SubToAPI wraps the same request body format behind your own sub_live_... key:
curl https://api.subtoapi.app/v1/messages \
-H "content-type: application/json" \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "List three uses for the Claude API." }
]
}'
The body shape is identical — the only difference is the auth header and endpoint. If you're just getting started, the quickstart walks through signup and your first request, and the messages reference covers every field in detail. Streaming responses are documented separately in the streaming guide, and tool calling in the tools guide.
Common Mistakes in Request Bodies
- Forgetting
max_tokens— this is required, not optional, unlike some other LLM APIs. - Putting a system message inside
messages— it belongs in the top-levelsystemfield. - Mismatched roles — messages must alternate
user/assistant; starting with anassistantrole or sending twousermessages in a row will fail. - Sending a string when a content block array is expected — this only matters once you add images or tools; plain text requests can stay as simple strings.
Questions
Does the request body format differ between models (Sonnet, Opus, Haiku)? No. The JSON structure is identical across models — only the model string value changes.
Can I send multiple images in one request? Yes. Add multiple image content blocks inside the same message's content array, alongside a text block if needed.
What happens if I omit max_tokens? The request is rejected with a validation error. It's a required field, not a default-assumed one.