CertSafari
    Anthropic Claude Certified Developer – Foundations (CCDV-F)· Lessons

    Domain 2 · Lesson 6/25

    Claude Messages API mechanics: requests, state, tools, streaming and vision

    Claude API Mechanics

    19 min read
    5.52% of exam
    10 sources
    Published 12 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Read a Messages API response: its content blocks, stop_reason and usage
    • Keep conversation state in your own code and resend it on every call without breaking the prompt cache
    • Run a client-side tool loop that passes thinking blocks back unmodified
    • Consume a streamed response event by event, or let the SDK accumulate it
    • Send images inline, by URL, or by Files API file_id
    • Say what changes in a request when Claude is called through Google Cloud or Amazon Bedrock

    Key concept

    Stateless Messages API — The Messages API keeps no memory between calls. Your application holds the conversation and sends all of it, every earlier turn, tool result and image, on every request.

    1.How a Messages request and response are shaped

    The Messages API gives you direct access to the model, and it is the surface you use when you want to write your own agent loop. Each call is one request with three required parts: a model, a max_tokens ceiling on the reply, and a messages list in which every entry has a role (user or assistant) and some content. Instructions that should hold from the first turn go in the top-level system field, outside the list.

    The reply is one assistant message. Its content is an array of typed blocks, not a string. A plain answer is a single text block. The same array can also hold tool_use blocks and thinking blocks, so code that reads content[0].text will break once you turn those features on. Two other fields carry the facts your code branches on. stop_reason says why generation stopped: end_turn, max_tokens, stop_sequence, tool_use or refusal. usage reports input_tokens and output_tokens, which is where your billing and context-size numbers come from.

    A complete response to a one-line user message: one text block, a stop reason and token usagejson
    {
      "id": "msg_01XFDUDYJgAACzvnptvVoYEL",
      "type": "message",
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": "Hello!"
        }
      ],
      "model": "claude-opus-5-5",
      "stop_reason": "end_turn",
      "stop_sequence": null,
      "usage": {
        "input_tokens": 12,
        "output_tokens": 6
      }
    }

    A stop_reason of max_tokens means the reply was cut off at your ceiling. It is not a finished answer. With refusal, the response also includes a stop_details object that names the policy category behind the refusal, so you can log it or tell the user why.

    Checkpoint 1 of 8· Check yourself

    Your moderation dashboard needs to record why Claude declined a request. Which part of the response gives you that?

    Sources12

    2.Conversation state lives in your code

    The API has no session, so your application is the conversation store. After each reply, append the assistant message to your list, append the next user turn, and send the whole list. Data access works the same way: anything Claude should be able to see in turn 7 (a document, a screenshot, a tool result from turn 2) has to be in the turn 7 request.

    Turn two of a conversation: the earlier user and assistant turns are sent again with the new questionpython
    message = anthropic.Anthropic().messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {"role": "user", "content": "Hello, Claude"},
            {"role": "assistant", "content": "Hello!"},
            {"role": "user", "content": "Can you describe LLMs to me?"},
        ],
    )

    Since you write the history yourself, you can put things in it. Assistant turns don't have to come from Claude: a synthetic few-shot exchange is valid input. On older models such as Claude Sonnet 4.5, an assistant message in the last position works as a prefill, and Claude continues from where it ends. Pair a prefill of The answer is ( with max_tokens: 1 and you get back a single multiple-choice letter. Don't build new code on that pattern: Claude Opus 4.6 already rejects prefilled assistant turns, and the Opus 5.5 migration checklist says to remove them. On recent models such as Claude Opus 5.5 and Sonnet 5.5, you can instead add a role: "system" message after a user turn to bring in an instruction partway through. It cannot be the first entry in messages.

    Prompt caching reuses work across those resent histories. A cache_control marker on a content block sets a cache breakpoint: everything up to and including that block is the reusable prefix. In the response's usage, cache_read_input_tokens counts tokens served from that cached prefix, and cache_creation_input_tokens counts tokens newly written to it. A read count stuck at zero means the prefix never repeats.

    Resending the full history on every call is what makes [prompt caching](lesson:5.4) worth using, and it puts one requirement on how you build requests: the prefix has to stay byte-identical. The cache key is a hash of the prompt up to the breakpoint, so anything that changes early in the request, like a timestamp in the system prompt, a reordered tool list or an edited earlier turn, produces a new key and a cache miss. Put changing content at the end. A mid-conversation system message is appended to the end of the history, so it leaves the cached prefix before it intact.

    How long an entry survives is set on the marker. The default lifetime is 5 minutes, refreshed at no extra cost each time the entry is used, and it is measured from the start of the request, so a slow streamed response eats into it. Adding "ttl": "1h" to cache_control requests a one-hour entry instead. Writes cost more than ordinary input: 25% extra for the 5-minute entry and 2x the base input price for the 1-hour one. The longer entry pays for itself when requests arrive more than five minutes apart or responses run long enough to outlast the window.

    Checkpoint 2 of 8· Exam question

    A compliance-document assistant sends the same 40,000-token reference manual as cached context, but users only ask questions roughly every 20-30 minutes, well beyond the default cache lifetime. Which caching configuration change best fits this access pattern?

    Checkpoint 3 of 8· Check yourself

    A team puts a static 6,000-token manual in one system block and the "Current time" line in a second system block after it. The only cache_control marker sits on the time block. Across thousands of calls, cache_read_input_tokens stays at zero. What is the fix?

    Sources134

    3.Tool calls and thinking blocks in the request loop

    Tool use runs on the same stateless request cycle. Claude never runs anything itself. When it wants a tool, the response has stop_reason: "tool_use" and one or more tool_use blocks, each with a tool name and a JSON object of arguments. Your code runs the operation and sends the output back as tool_result blocks in a new user message. That message goes at the end of the full history, after the assistant turn that asked for the tool. Keep looping while stop_reason is tool_use. Any other stop reason ends the loop. Writing the tool schemas themselves is covered in [Tool implementation](lesson:8.1).

    Server tools are different. For web_search, web_fetch, code_execution and tool_search, Anthropic runs the code, and you never build a tool_result. The response shows server_tool_use blocks for what ran. That server-side loop has an iteration cap. When it hits the cap, the response comes back with stop_reason: "pause_turn", and you continue by sending the conversation again with the paused response included.

    [Extended and adaptive thinking](lesson:5.3) adds thinking blocks to the assistant's content, and each one carries a signature. In a tool loop, send those blocks back exactly as you received them, including blocks whose thinking text is empty. The safe way is to append the assistant message as you received it. Don't filter its blocks by type and don't rebuild it from parts. The API rejects thinking blocks that have been edited, reordered or partly dropped with a 400 error.

    Checkpoint 4 of 8· Check yourself

    To save tokens, an agent rebuilds each assistant turn with only its tool_use blocks before appending the tool results. Thinking is enabled. What happens on the next request?

    On Claude Opus 5.5, thinking is not optional: every request runs with adaptive thinking, so any response can open with thinking blocks. Thinking tokens are billed as output tokens even when the thinking text is not returned, and they count against max_tokens, which stays a hard limit on thinking plus reply text. A ceiling tuned for a model that ran without thinking can now cut the answer short, and at xhigh or max effort the migration guide suggests starting at 64k.

    Depth is controlled by the effort parameter rather than by a thinking budget. Set output_config={"effort": "medium"} (the Opus 5.5 default) and lower it to spend fewer tokens on thinking. The old thinking: {"type": "enabled", "budget_tokens": N} form is replaced by thinking: {"type": "adaptive"} plus effort, or by no thinking field at all. On models that still use manual extended thinking, the budget counts toward max_tokens too. What you see is also a setting: thinking.display defaults to "omitted", so thinking blocks arrive with an empty thinking field and a signature. Set "summarized" to receive readable summaries, and treat the field as display text only.

    Checkpoint 5 of 8· Exam question

    A developer configures `thinking={"type": "enabled", "budget_tokens": 32000}` alongside `max_tokens=16000` and the request fails validation. What is the correct fix?

    Sources235

    4.Streaming responses as server-sent events

    Set "stream": true and the response arrives as server-sent events, so a UI can show text as it is generated. Streaming also helps when nobody is watching. With a large max_tokens, the SDK requires streaming to avoid HTTP timeouts. Its accumulation helper streams under the hood and hands you the same complete Message object that messages.create() would.

    The event sequence of one streamed message
    EventWhat it carries
    message_startA Message object with empty content
    content_block_start / content_block_delta / content_block_stopOne block, built up by deltas; its index matches its position in the final content array
    message_deltaTop-level changes such as stop_reason and output usage
    message_stopEnd of the stream
    ping / errorKeep-alives at any point; errors such as overloaded_error mid-stream

    Each kind of block streams a different delta. A text_delta carries a piece of text. A tool_use block streams input_json_delta events made of partial JSON strings, so collect them and parse once content_block_stop arrives, or use the SDK helpers that parse as the input streams. Thinking streams as thinking_delta, followed by one signature_delta just before the block closes. That signature is what the tool loop above must send back. Errors can also arrive mid-stream: overloaded_error is the streaming version of an HTTP 529. And because new event types may be added, a parser should skip unknown events rather than crash on them.

    Checkpoint 6 of 8· Fill the gap

    This request uses a 128,000-token max_tokens and only needs the finished message. Which helper completes it?

    client = anthropic.Anthropic()
    
    with client.messages.stream(
        max_tokens=128000,
        messages=[{"role": "user", "content": "Write a detailed analysis..."}],
        model="claude-opus-5-5",
    ) as stream:
        message = stream. ? ()

    Sources6

    5.Sending images and files: inline, by URL, or by file ID

    An image is just another content block in a user turn. Its source takes one of three types. base64 embeds the bytes and needs a media_type. url points at a hosted image. file refers to something already uploaded to the Files API. The supported formats are image/jpeg, image/png, image/gif and image/webp. Put the image block before the text that asks about it, as in this example.

    A base64 image block followed by the question about it, in a single user turnpython
    message = anthropic.Anthropic().messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": image_media_type,
                            "data": image_data,
                        },
                    },
                    {"type": "text", "text": "What is in the above image?"},
                ],
            }
        ],
    )

    Statelessness is what makes inline images expensive. Suppose a support tool sends two screenshots in turn 1 and asks about "the first image" in turn 3. Those screenshots have to be in the turn 3 request, so a base64 image is re-encoded and re-sent every turn. The Files API avoids that: upload once, get back a file_id, and from then on each request refers to the file by ID. The history still has to include the image, but now as a short reference. The same upload works for documents such as PDFs, and files can be listed, retrieved and deleted.

    Uploading a PDF once to the Files API to get a reusable file_idpython
    uploaded = client.files.upload(
        file=("document.pdf", open("/path/to/document.pdf", "rb"), "application/pdf"),
    )
    file_id = uploaded.id
    print(file_id)

    Checkpoint 7 of 8· Check yourself

    A review assistant attaches the same 2 MB product photo to every turn of a long conversation. What change stops the photo bytes from being sent on every request?

    Sources17

    6.What changes when you call Claude through a cloud vendor

    Which platform you buy Claude through is a decision about [data residency and platform choice](lesson:2.1). Once that is decided, your request code changes in specific ways. On Google Cloud's Agent Platform the body looks almost the same as a Messages API request, with two changes. The model moves out of the body and into the endpoint URL. The API version moves into the body as anthropic_version.

    Messages API request versus Claude on Google Cloud's Agent Platform
    AspectClaude APIAgent Platform
    Where the model goesmodel field in the request bodyIn the Google Cloud endpoint URL
    API versionSent as a headeranthropic_version in the body, set to vertex-2023-10-16
    SDKAnthropic client SDKSame SDK with the vertex extra: pip install -U "anthropic[vertex]"

    Model IDs also differ by platform. On Amazon Bedrock, for the models covered by its Opus 4.6-and-earlier guide, you pass an inference profile instead of the base model ID: a regional prefix plus the Bedrock ID, such as us.anthropic.claude-sonnet-4-5-20250929-v1:0, or the full inference-profile ARN. Model availability varies by region, and lifecycle dates on partner platforms are set by the partner, so the retirement date on Bedrock or Google Cloud can differ from the Claude API's. Some feature behaviour changes too. Prompt caches are isolated per workspace on the Claude API but per organization on Bedrock and Google Cloud.

    The SDK client and the authentication change with the platform as well. For Bedrock models in the Opus 4.6-and-earlier guide, install anthropic[bedrock] and use AnthropicBedrock: it takes AWS keys or the default AWS credential providers, or a bearer token through the AWS_BEARER_TOKEN_BEDROCK environment variable. Opus 4.7 and later models run on Claude in Amazon Bedrock instead, through AnthropicBedrockMantle, which resolves credentials by the standard AWS precedence and uses IDs with an anthropic. prefix, such as anthropic.claude-opus-5-5. On Google Cloud, AnthropicVertex takes a project_id and a region, where global gives dynamic routing for maximum availability. Authenticate first, usually with gcloud auth application-default login.

    Checkpoint 8 of 8· Match them up

    Match each platform to the request detail that is specific to it

    Tap a term, then the definition that fits it.

    Sources89104

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.Once Claude has seen an image or document in an earlier turn, a later turn can refer to it without including it again.Why is that wrong?

      The API keeps nothing between calls. The image, or a file_id reference to it, has to be in the history of every request that relies on it.

      Covered in Conversation state lives in your code

    2. 2.Thinking blocks are just display text, so a tool loop can drop them or keep only the tool_use blocks when it sends the assistant turn back.Why is that wrong?

      The thinking blocks have to go back complete, signatures included. Edited, reordered or partly dropped thinking blocks are rejected with a 400.

      Covered in Tool calls and thinking blocks in the request loop

    3. 3.Each input_json_delta in a streamed tool_use block is a complete JSON object you can parse right away.Why is that wrong?

      The deltas are fragments of a JSON string. Collect them and parse at content_block_stop, or use the SDK's streaming helpers.

      Covered in Streaming responses as server-sent events

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Refusal responses (stop_reason: "refusal") also include a stop_details object identifying the policy category that triggered the refusal”
      ↩︎ How a Messages request and response are shaped
      “Earlier conversational turns don't necessarily need to actually originate from Claude.”
      ↩︎ Conversation state lives in your code
      “You can pre-fill part of Claude's response in the last position of the input messages list.”
      ↩︎ Conversation state lives in your code
      “A system message cannot be the first entry in messages.”
      ↩︎ Conversation state lives in your code
      “because it is appended to the end of the message history, it does not invalidate any cached prefix that came before it.”
      ↩︎ Conversation state lives in your code
      “You can supply images using the base64, url, or file source types.”
      ↩︎ Sending images and files: inline, by URL, or by file ID
      “Supported media types are image/jpeg, image/png, image/gif, and image/webp.”
      ↩︎ Sending images and files: inline, by URL, or by file ID
      “The Messages API is stateless, which means that you always send the full conversational history to the API.”
      ↩︎ Key concept
      “The Messages API is stateless, which means that you always send the full conversational history to the API.”
      ↩︎ Exam trap 1
      “The Messages API is stateless, which means that you always send the full conversational history to the API.”
      ↩︎ Prediction
    2. 2.
      “The loop exits on any other stop reason ("end_turn", "max_tokens", "stop_sequence", or "refusal")”
      ↩︎ How a Messages request and response are shaped
      “Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks.”
      ↩︎ Tool calls and thinking blocks in the request loop
      “You never construct a tool_result block for these tools.”
      ↩︎ Tool calls and thinking blocks in the request loop
      “re-send the conversation (including the paused response) to let the model continue where it left off.”
      ↩︎ Tool calls and thinking blocks in the request loop
    3. 3.
      “Remove any assistant-message prefills; Claude Opus 4.6 already rejects them.”
      ↩︎ Conversation state lives in your code
      “pass the thinking blocks from each assistant response back to the API complete and unmodified when you return tool results”
      ↩︎ Tool calls and thinking blocks in the request loop
      “Thinking runs on every Claude Opus 5.5 request, so every response can begin with thinking blocks, and max_tokens covers thinking plus text.”
      ↩︎ Tool calls and thinking blocks in the request loop
      “To spend fewer tokens on thinking, lower the effort level.”
      ↩︎ Tool calls and thinking blocks in the request loop
      “thinking.display defaults to "omitted"”
      ↩︎ Tool calls and thinking blocks in the request loop
      “pass the thinking blocks from each assistant response back to the API complete and unmodified when you return tool results”
      ↩︎ Exam trap 2
      “the API rejects edited, reordered, or partially dropped thinking blocks with a 400 error.”
      ↩︎ Checkpoint
    4. 4.
      “Cache keys are generated using a cryptographic hash of the prompts up to the cache control point.”
      ↩︎ Conversation state lives in your code
      “writes a fresh entry each time and never hits”
      ↩︎ Conversation state lives in your code
      “cache_read_input_tokens: Tokens retrieved from cache (everything before cache breakpoints that was cached)”
      ↩︎ Conversation state lives in your code
      “By default, the cache has a 5-minute lifetime. The cache is refreshed for no additional cost each time the cached content is used.”
      ↩︎ Conversation state lives in your code
      “You can specify a 1-hour TTL at 2x the base input token price”
      ↩︎ Conversation state lives in your code
      “5-minute cache writes cost 25% more than base input tokens, 1-hour cache writes cost 2x base input tokens”
      ↩︎ Conversation state lives in your code
      “On Bedrock and Google Cloud, caches are isolated per organization.”
      ↩︎ What changes when you call Claude through a cloud vendor
      “keep the breakpoint before it, on the last stable block.”
      ↩︎ Checkpoint
    5. 5.
      “Requires a minimum budget of 1,024 tokens and counts towards your max_tokens limit.”
      ↩︎ Tool calls and thinking blocks in the request loop
    6. 6.
      “This is especially useful for requests with large max_tokens values, where the SDK requires streaming to avoid HTTP timeouts.”
      ↩︎ Streaming responses as server-sent events
      “For thinking content, a special signature_delta event is sent just before the content_block_stop event.”
      ↩︎ Streaming responses as server-sent events
      “your code should handle unknown event types gracefully.”
      ↩︎ Streaming responses as server-sent events
      “the deltas are partial JSON strings, whereas the final tool_use.input is always an object.”
      ↩︎ Exam trap 3
    7. 7.
      “Upload files to Anthropic's secure storage and receive a unique file_id”
      ↩︎ Sending images and files: inline, by URL, or by file ID
      “Upload files once, reference them by file_id in Messages requests, and download outputs created by skills or the code execution tool.”
      ↩︎ Checkpoint
    8. 8.
      “anthropic_version is passed in the request body (rather than as a header), and must be set to the value vertex-2023-10-16.”
      ↩︎ What changes when you call Claude through a cloud vendor
      “Lifecycle dates on partner-operated platforms are set by the partner and can differ from the Claude API schedule.”
      ↩︎ What changes when you call Claude through a cloud vendor
      “Before running requests you might need to run gcloud auth application-default login to authenticate with Google Cloud.”
      ↩︎ What changes when you call Claude through a cloud vendor
      “On Agent Platform, model is not passed in the request body. Instead, it is specified in the Google Cloud endpoint URL.”
      ↩︎ Checkpoint
    9. 9.
      “To invoke these models, pass an inference profile instead of the base model ID.”
      ↩︎ What changes when you call Claude through a cloud vendor
      “The simplest approach is to set the AWS_BEARER_TOKEN_BEDROCK environment variable”
      ↩︎ What changes when you call Claude through a cloud vendor
    10. 10.

    Continue to page 2 of 2

    Message Batches API and choosing batch over realtime calls

    Spotted a mistake, or was something unclear? Tell us.