<map version="1.0.1">
<node ID="ID_979" TEXT="Chapter 2 — Tool use, prompt caching, batches and production plumbing"><richcontent TYPE="NOTE"><html><head></head><body><p>2026-09-14 → 2026-09-20</p><p>Applications &amp; Integration · Tools &amp; MCPs</p></body></html></richcontent><node ID="ID_715" TEXT="🎯 Goal"><richcontent TYPE="NOTE"><html><head></head><body><p>Run the tool-use loop from memory, cut cost with caching and batches, and make a client that survives 429s and 529s.</p></body></html></richcontent></node><node ID="ID_718" TEXT="Exam domains"><node ID="ID_716" TEXT="Applications &amp; Integration — 33.1%"></node><node ID="ID_717" TEXT="Tools &amp; MCPs — 10.6%"></node></node><node ID="ID_792" TEXT="📅 Daily plan"><node ID="ID_729" TEXT="09-14 · Tool use: tools schema (name, description, input_schema), tool_use …"><node ID="ID_719" TEXT="Learn: Tool use: tools schema (name, description, input_schema), tool_use block, tool_result block, tool_choice"></node><node ID="ID_724" TEXT="Build: tools.py — a get_time / get_weather tool the model calls"><node ID="ID_720" TEXT="Define tools=[{name:&quot;get_time&quot;, description:&quot;Current time for an IANA timezone&quot;, input_schema:{type:&quot;object&quot;, properties:{tz:{type:&quot;string&quot;}}, required:[&quot;tz&quot;]}}]."></node><node ID="ID_721" TEXT="Call the API; when stop_reason==&quot;tool_use&quot;, find the tool_use block, run your Python function with block.input, and append two messages: the assistant message (as returned) and a user message containing {type:&quot;tool_result&quot;, tool_use_id:block.id, content:&quot;...&quot;}."></node><node ID="ID_722" TEXT="Loop until stop_reason==&quot;end_turn&quot;; print the final text."></node><node ID="ID_723" TEXT="Break it on purpose: omit the tool_result and read the error; send a wrong tool_use_id and read that error."></node></node><node ID="ID_725" TEXT="✔ Done when: Claude asks for get_time, you return the result, Claude answers in natural language; you have seen both error messages."></node><node ID="ID_728" TEXT="Read"><node ID="ID_726" TEXT="Tool use overview" LINK="https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview</p></body></html></richcontent></node><node ID="ID_727" TEXT="How to implement tool use" LINK="https://docs.claude.com/en/docs/agents-and-tools/tool-use/implement-tool-use"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/docs/agents-and-tools/tool-use/implement-tool-use</p></body></html></richcontent></node></node></node><node ID="ID_740" TEXT="09-15 · Multi-tool and parallel tool calls; missing or invalid arguments; i…"><node ID="ID_730" TEXT="Learn: Multi-tool and parallel tool calls; missing or invalid arguments; is_error on tool_result"></node><node ID="ID_735" TEXT="Build: Add a second tool; log every tool round-trip"><node ID="ID_731" TEXT="Add get_weather(city). Ask &quot;What time is it in Pune and what's the weather?&quot; — observe two tool_use blocks in one response; answer both in ONE user message."></node><node ID="ID_732" TEXT="Make get_weather raise for an unknown city; return tool_result with is_error:true and the message; watch Claude recover."></node><node ID="ID_733" TEXT="Try tool_choice={type:&quot;any&quot;} and {type:&quot;tool&quot;, name:&quot;get_time&quot;} and note how behaviour changes."></node><node ID="ID_734" TEXT="Write a generic run_tools(messages, tools, registry) loop with logging of every round-trip and a max_iterations guard."></node></node><node ID="ID_736" TEXT="✔ Done when: Parallel calls handled in one message; is_error recovery observed; generic loop with logging committed."></node><node ID="ID_739" TEXT="Read"><node ID="ID_737" TEXT="Tool use: parallel calls and tool_choice" LINK="https://docs.claude.com/en/docs/agents-and-tools/tool-use/implement-tool-use"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/docs/agents-and-tools/tool-use/implement-tool-use</p></body></html></richcontent></node><node ID="ID_738" TEXT="Tool use: handling errors" LINK="https://docs.claude.com/en/docs/agents-and-tools/tool-use/implement-tool-use#handling-tool-use-and-tool-result-content-blocks"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/docs/agents-and-tools/tool-use/implement-tool-use#handling-tool-use-and-tool-result-content-blocks</p></body></html></richcontent></node></node></node><node ID="ID_751" TEXT="09-16 · Prompt caching: cache_control breakpoints, minimum cacheable length…"><node ID="ID_741" TEXT="Learn: Prompt caching: cache_control breakpoints, minimum cacheable length, read vs write pricing, TTL"></node><node ID="ID_746" TEXT="Build: Cache a long system prompt; compare usage before and after"><node ID="ID_742" TEXT="Build a system prompt of ~3,000 tokens (paste your project's README + conventions). Send it as a list of text blocks with cache_control:{type:&quot;ephemeral&quot;} on the last block."></node><node ID="ID_743" TEXT="Call twice within 5 minutes; print usage: first call shows cache_creation_input_tokens, second shows cache_read_input_tokens."></node><node ID="ID_744" TEXT="Change one character before the breakpoint and call again — observe a new cache write."></node><node ID="ID_745" TEXT="Compute cost for 100 calls with and without caching using the pricing page; put the table in notes."></node></node><node ID="ID_747" TEXT="✔ Done when: cache_read_input_tokens &gt; 0 on the second call; cost comparison table written."></node><node ID="ID_750" TEXT="Read"><node ID="ID_748" TEXT="Prompt caching" LINK="https://docs.claude.com/en/docs/build-with-claude/prompt-caching"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/docs/build-with-claude/prompt-caching</p></body></html></richcontent></node><node ID="ID_749" TEXT="Pricing (cache multipliers)" LINK="https://docs.claude.com/en/docs/about-claude/pricing"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/docs/about-claude/pricing</p></body></html></richcontent></node></node></node><node ID="ID_761" TEXT="09-17 · Message Batches API: create, poll, retrieve results, 50% discount, …"><node ID="ID_752" TEXT="Learn: Message Batches API: create, poll, retrieve results, 50% discount, up to 24 h"></node><node ID="ID_757" TEXT="Build: Submit 20 prompts as a batch; fetch results"><node ID="ID_753" TEXT="Create 20 requests each with a custom_id (e.g. ticket-001) and a full Messages params object."></node><node ID="ID_754" TEXT="client.messages.batches.create(requests=[...]); print the batch id and processing_status."></node><node ID="ID_755" TEXT="Poll batches.retrieve(id) every 30 s until processing_status==&quot;ended&quot;; then iterate batches.results(id) and match custom_id to your inputs."></node><node ID="ID_756" TEXT="Note the result types: succeeded, errored, canceled, expired. Save results as JSONL."></node></node><node ID="ID_758" TEXT="✔ Done when: 20 results retrieved and matched by custom_id; you can name the four result types."></node><node ID="ID_760" TEXT="Read"><node ID="ID_759" TEXT="Batch processing" LINK="https://docs.claude.com/en/docs/build-with-claude/batch-processing"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/docs/build-with-claude/batch-processing</p></body></html></richcontent></node></node></node><node ID="ID_772" TEXT="09-18 · Errors and limits: 400/401/429/500/529, retry-after, SDK retries an…"><node ID="ID_762" TEXT="Learn: Errors and limits: 400/401/429/500/529, retry-after, SDK retries and timeouts, rate-limit tiers"></node><node ID="ID_767" TEXT="Build: Wrap client with backoff; simulate a 429"><node ID="ID_763" TEXT="Read the error table; write it into notes with 'whose fault' column (client vs server)."></node><node ID="ID_764" TEXT="Construct Anthropic(max_retries=5, timeout=60) and read what the SDK retries by default (408/409/429/5xx, connection errors)."></node><node ID="ID_765" TEXT="Simulate a 429 by firing 50 concurrent requests with asyncio; catch anthropic.RateLimitError and read the retry-after header from e.response.headers."></node><node ID="ID_766" TEXT="Write a decorator with exponential backoff + jitter for cases the SDK does not cover (e.g. your own tool failures)."></node></node><node ID="ID_768" TEXT="✔ Done when: You triggered and handled a real 429; the notes error table has all codes and who is responsible."></node><node ID="ID_771" TEXT="Read"><node ID="ID_769" TEXT="Errors" LINK="https://docs.claude.com/en/api/errors"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/api/errors</p></body></html></richcontent></node><node ID="ID_770" TEXT="Rate limits" LINK="https://docs.claude.com/en/api/rate-limits"><richcontent TYPE="NOTE"><html><head></head><body><p>https://docs.claude.com/en/api/rate-limits</p></body></html></richcontent></node></node></node><node ID="ID_782" TEXT="09-19 · Academy course modules on tools and caching; Console usage dashboar…"><node ID="ID_773" TEXT="Learn: Academy course modules on tools and caching; Console usage dashboard walk-through"></node><node ID="ID_777" TEXT="Build: Reconcile your usage dashboard with your own token log"><node ID="ID_774" TEXT="Complete the tool-use and caching modules with quizzes."></node><node ID="ID_775" TEXT="Sum the usage objects you have logged this week; compare to the Console usage page by model — explain any gap (cache tokens, retries)."></node><node ID="ID_776" TEXT="Add a tiny cost() helper to your lab that prices a usage object per model; you will reuse it in Chapter 3."></node></node><node ID="ID_778" TEXT="✔ Done when: Module quizzes passed; your logged totals match the Console within a few percent."></node><node ID="ID_781" TEXT="Read"><node ID="ID_779" TEXT="Course: Building with the Claude API (tools &amp; caching modules)" LINK="https://academy.claude.com/courses/building-with-the-claude-api"><richcontent TYPE="NOTE"><html><head></head><body><p>https://academy.claude.com/courses/building-with-the-claude-api</p></body></html></richcontent></node><node ID="ID_780" TEXT="Console usage" LINK="https://platform.claude.com/usage"><richcontent TYPE="NOTE"><html><head></head><body><p>https://platform.claude.com/usage</p></body></html></richcontent></node></node></node><node ID="ID_791" TEXT="09-20 · Review day"><node ID="ID_783" TEXT="Learn: Review day"></node><node ID="ID_787" TEXT="Build: Write notes/tools-caching-batch.md"><node ID="ID_784" TEXT="Write notes/tools-caching-batch.md with: the tool loop diagram, tool_choice table, cache rules (order, minimum, TTL, pricing), batch lifecycle, error table."></node><node ID="ID_785" TEXT="Self-check below; then the Applications &amp; Integration and Tools &amp; MCPs quizzes in the Question bank."></node><node ID="ID_786" TEXT="Weekly retrospective."></node></node><node ID="ID_788" TEXT="✔ Done when: Notes committed; both quizzes recorded; retrospective filled."></node><node ID="ID_790" TEXT="Read"><node ID="ID_789" TEXT="Study material below; your logs from the week"></node></node></node></node><node ID="ID_838" TEXT="📖 Study material"><node ID="ID_804" TEXT="Tool use — the loop"><node ID="ID_793" TEXT="You send tools (name, description, JSON input_schema) with the messages."></node><node ID="ID_794" TEXT="Claude replies with stop_reason: tool_use and one or more tool_use blocks: {id, name,…"><richcontent TYPE="NOTE"><html><head></head><body><p>Claude replies with stop_reason: tool_use and one or more tool_use blocks: {id, name, input}.</p></body></html></richcontent></node><node ID="ID_795" TEXT="Your code runs the function. Claude never executes anything for client tools."></node><node ID="ID_796" TEXT="You append the assistant message unchanged, then a user message whose content is…"><richcontent TYPE="NOTE"><html><head></head><body><p>You append the assistant message unchanged, then a user message whose content is tool_result blocks: {type:&quot;tool_result&quot;, tool_use_id, content, is_error?} — one per tool_use, same ids, all in one message, and the tool_result blocks must come first in that message before any text. strict: true on a tool definition makes arguments conform exactly to the schema.</p></body></html></richcontent></node><node ID="ID_797" TEXT="Repeat until stop_reason: end_turn."></node><node ID="ID_798" TEXT="Descriptions are the most important part of a tool definition — say what it does, when…"><richcontent TYPE="NOTE"><html><head></head><body><p>Descriptions are the most important part of a tool definition — say what it does, when to use it, what it returns, and what the arguments mean. Anthropic recommends 3–4+ sentences.</p></body></html></richcontent></node><node ID="ID_799" TEXT="tool_choice: {&quot;type&quot;:&quot;auto&quot;} (default, may answer without tools), {&quot;type&quot;:&quot;any&quot;} (must…"><richcontent TYPE="NOTE"><html><head></head><body><p>tool_choice: {&quot;type&quot;:&quot;auto&quot;} (default, may answer without tools), {&quot;type&quot;:&quot;any&quot;} (must call some tool), {&quot;type&quot;:&quot;tool&quot;,&quot;name&quot;:&quot;x&quot;} (must call that one — the JSON-schema trick), {&quot;type&quot;:&quot;none&quot;} (no tools this turn). disable_parallel_tool_use: true forces one call at a time.</p></body></html></richcontent></node><node ID="ID_800" TEXT="Parallel tool use: several tool_use blocks in one response — return all results in a…"><richcontent TYPE="NOTE"><html><head></head><body><p>Parallel tool use: several tool_use blocks in one response — return all results in a single user message, in the same order.</p></body></html></richcontent></node><node ID="ID_801" TEXT="Mark failures with is_error: true so Claude retries or explains instead of…"><richcontent TYPE="NOTE"><html><head></head><body><p>Mark failures with is_error: true so Claude retries or explains instead of hallucinating a result.</p></body></html></richcontent></node><node ID="ID_802" TEXT="Tool results can contain text and image blocks; keep them small — they are input…"><richcontent TYPE="NOTE"><html><head></head><body><p>Tool results can contain text and image blocks; keep them small — they are input tokens on every later turn.</p></body></html></richcontent></node><node ID="ID_803" TEXT="Server tools (web search, web fetch, code execution, MCP connector) run on Anthropic's…"><richcontent TYPE="NOTE"><html><head></head><body><p>Server tools (web search, web fetch, code execution, MCP connector) run on Anthropic's side; their results come back directly — you never send a tool_result for them. Text editor, bash, computer use and memory are Anthropic-defined client tools you still execute.</p></body></html></richcontent></node></node><node ID="ID_811" TEXT="Prompt caching"><node ID="ID_805" TEXT="Add cache_control: {&quot;type&quot;:&quot;ephemeral&quot;} to a block; the cached prefix is everything up…"><richcontent TYPE="NOTE"><html><head></head><body><p>Add cache_control: {&quot;type&quot;:&quot;ephemeral&quot;} to a block; the cached prefix is everything up to and including that block, in this order: tools → system → messages. Up to 4 breakpoints.</p></body></html></richcontent></node><node ID="ID_806" TEXT="Pricing: cache write ≈ 1.25× base input (5-min TTL) or 2× (1-hour TTL); cache read ≈…"><richcontent TYPE="NOTE"><html><head></head><body><p>Pricing: cache write ≈ 1.25× base input (5-min TTL) or 2× (1-hour TTL); cache read ≈ 0.1× base input. Break-even after roughly two reads.</p></body></html></richcontent></node><node ID="ID_807" TEXT="Minimum cacheable prefix depends on the model: 512 tokens (Opus 5, Fable 5.x), 1,024…"><richcontent TYPE="NOTE"><html><head></head><body><p>Minimum cacheable prefix depends on the model: 512 tokens (Opus 5, Fable 5.x), 1,024 (Sonnet 5, Sonnet 4.5/4.6, Opus 4.8), 2,048 (Opus 4.7), 4,096 (Opus 4.5/4.6, Haiku 4.5). Shorter prefixes are silently not cached.</p></body></html></richcontent></node><node ID="ID_808" TEXT="TTL 5 minutes by default, refreshed on every hit; optional ttl: &quot;1h&quot;."></node><node ID="ID_809" TEXT="Any change before the breakpoint (including tool definitions, images, or the model)…"><richcontent TYPE="NOTE"><html><head></head><body><p>Any change before the breakpoint (including tool definitions, images, or the model) invalidates the cache — stable content first, volatile content last. For chat, put the breakpoint on the latest message so the growing history is cached incrementally.</p></body></html></richcontent></node><node ID="ID_810" TEXT="You confirm hits via usage.cache_read_input_tokens. Caching cuts cost and…"><richcontent TYPE="NOTE"><html><head></head><body><p>You confirm hits via usage.cache_read_input_tokens. Caching cuts cost and time-to-first-token; it never reduces output tokens.</p></body></html></richcontent></node></node><node ID="ID_815" TEXT="Message Batches"><node ID="ID_812" TEXT="Asynchronous: submit up to 100,000 requests (or ~256 MB), each with a custom_id;…"><richcontent TYPE="NOTE"><html><head></head><body><p>Asynchronous: submit up to 100,000 requests (or ~256 MB), each with a custom_id; results within 24 h, usually much faster; 50% off input and output. Works with caching and tools.</p></body></html></richcontent></node><node ID="ID_813" TEXT="Lifecycle: in_progress → ended; then per-request result type succeeded / errored /…"><richcontent TYPE="NOTE"><html><head></head><body><p>Lifecycle: in_progress → ended; then per-request result type succeeded / errored / canceled / expired. Results are available for 29 days. Order is not guaranteed — always join on custom_id.</p></body></html></richcontent></node><node ID="ID_814" TEXT="Use for evals, backfills, bulk classification, nightly summaries. Never for anything…"><richcontent TYPE="NOTE"><html><head></head><body><p>Use for evals, backfills, bulk classification, nightly summaries. Never for anything interactive.</p></body></html></richcontent></node></node><node ID="ID_833" TEXT="Errors, limits and resilience"><node ID="ID_816" TEXT="400"><richcontent TYPE="NOTE"><html><head></head><body><p>invalid_request_error · you · fix the request (roles, max_tokens, schema)</p></body></html></richcontent></node><node ID="ID_817" TEXT="401"><richcontent TYPE="NOTE"><html><head></head><body><p>authentication_error · you · check key/env</p></body></html></richcontent></node><node ID="ID_818" TEXT="403"><richcontent TYPE="NOTE"><html><head></head><body><p>permission_error · you · key lacks access</p></body></html></richcontent></node><node ID="ID_819" TEXT="404"><richcontent TYPE="NOTE"><html><head></head><body><p>not_found_error · you · wrong model id / resource</p></body></html></richcontent></node><node ID="ID_820" TEXT="413"><richcontent TYPE="NOTE"><html><head></head><body><p>request_too_large · you · shrink input</p></body></html></richcontent></node><node ID="ID_821" TEXT="429"><richcontent TYPE="NOTE"><html><head></head><body><p>rate_limit_error · volume · honour retry-after; backoff; batch</p></body></html></richcontent></node><node ID="ID_822" TEXT="500"><richcontent TYPE="NOTE"><html><head></head><body><p>api_error · Anthropic · retry with backoff</p></body></html></richcontent></node><node ID="ID_823" TEXT="529"><richcontent TYPE="NOTE"><html><head></head><body><p>overloaded_error · Anthropic · retry with backoff + jitter, degrade gracefully</p></body></html></richcontent></node><node ID="ID_824" TEXT="Rate limits are per organisation per model: requests/min, input tokens/min, output…"><richcontent TYPE="NOTE"><html><head></head><body><p>Rate limits are per organisation per model: requests/min, input tokens/min, output tokens/min; tiers rise with spend. Headers report remaining quota.</p></body></html></richcontent></node><node ID="ID_825" TEXT="The SDK retries 408/409/429/5xx and connection errors twice by default with backoff;…"><richcontent TYPE="NOTE"><html><head></head><body><p>The SDK retries 408/409/429/5xx and connection errors twice by default with backoff; tune max_retries and timeout. Make writes idempotent; log request ids (request-id header / _request_id) for support.</p></body></html></richcontent></node><node ID="ID_826" TEXT="Long requests: stream, or the SDK may reject non-streaming calls expected to exceed…"><richcontent TYPE="NOTE"><html><head></head><body><p>Long requests: stream, or the SDK may reject non-streaming calls expected to exceed ~10 minutes.</p></body></html></richcontent></node><node ID="ID_832" TEXT="⚠ Exam traps"><node ID="ID_827" TEXT="Who executes a client tool? Always your code (server tools need no tool_result). tool_result goes in a user message, first in the content list, and must match every tool_use id."></node><node ID="ID_828" TEXT="Caching reduces cost and latency but not output tokens."></node><node ID="ID_829" TEXT="Batch cannot power a live chat UI. 429 is not a bug in your code — back off or move to batch."></node><node ID="ID_830" TEXT="Cache prefix order is tools → system → messages."></node><node ID="ID_831" TEXT="Cache reads don't count against ITPM rate limits."></node></node></node><node ID="ID_835" TEXT="Tool loop skeleton"><node ID="ID_834" TEXT="{ } code sample"><richcontent TYPE="NOTE"><html><head></head><body><p>REGISTRY = {&quot;get_time&quot;: get_time, &quot;get_weather&quot;: get_weather}</p><p>def run(messages, tools, max_iter=8):</p><p>    for _ in range(max_iter):</p><p>        r = client.messages.create(model=MODEL, max_tokens=1024, tools=tools, messages=messages)</p><p>        messages.append({&quot;role&quot;:&quot;assistant&quot;,&quot;content&quot;: r.content})</p><p>        if r.stop_reason != &quot;tool_use&quot;:</p><p>            return r</p><p>        results = []</p><p>        for b in r.content:</p><p>            if b.type == &quot;tool_use&quot;:</p><p>                try:</p><p>                    out = REGISTRY[b.name](**b.input)</p><p>                    results.append({&quot;type&quot;:&quot;tool_result&quot;,&quot;tool_use_id&quot;:b.id,&quot;content&quot;:str(out)})</p><p>                except Exception as e:</p><p>                    results.append({&quot;type&quot;:&quot;tool_result&quot;,&quot;tool_use_id&quot;:b.id,&quot;content&quot;:str(e),&quot;is_error&quot;:True})</p><p>        messages.append({&quot;role&quot;:&quot;user&quot;,&quot;content&quot;: results})</p><p>    raise RuntimeError(&quot;tool loop exceeded max_iter&quot;)</p></body></html></richcontent></node></node><node ID="ID_837" TEXT="Caching a system prompt"><node ID="ID_836" TEXT="{ } code sample"><richcontent TYPE="NOTE"><html><head></head><body><p>r = client.messages.create(</p><p>    model=MODEL, max_tokens=300,</p><p>    system=[{&quot;type&quot;:&quot;text&quot;,&quot;text&quot;: BIG_STABLE_CONTEXT,</p><p>             &quot;cache_control&quot;:{&quot;type&quot;:&quot;ephemeral&quot;}}],</p><p>    messages=[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;Question about the context…&quot;}])</p><p>print(r.usage.cache_creation_input_tokens, r.usage.cache_read_input_tokens)</p></body></html></richcontent></node></node></node><node ID="ID_923" TEXT="📚 Library digests"><node ID="ID_850" TEXT="API errors" LINK="https://platform.claude.com/docs/en/api/errors"><richcontent TYPE="NOTE"><html><head></head><body><p>https://platform.claude.com/docs/en/api/errors</p></body></html></richcontent><node ID="ID_845" TEXT="Key facts"><node ID="ID_839" TEXT="400 invalid_request_error · 401 authentication_error · 402 billing_error · 403 permission_error…"><richcontent TYPE="NOTE"><html><head></head><body><p>400 invalid_request_error · 401 authentication_error · 402 billing_error · 403 permission_error · 404 not_found_error · 409 conflict_error · 413 request_too_large · 429 rate_limit_error · 500 api_error · 504 timeout_error · 529 overloaded_error.</p></body></html></richcontent></node><node ID="ID_840" TEXT="Error body: {type:'error', error:{type, message}, request_id:'req_…'}. Header request-id; Python…"><richcontent TYPE="NOTE"><html><head></head><body><p>Error body: {type:'error', error:{type, message}, request_id:'req_…'}. Header request-id; Python message._request_id.</p></body></html></richcontent></node><node ID="ID_841" TEXT="Request-size limits: Messages 32 MB, Token Counting 32 MB, Batches 256 MB, Files 500 MB."></node><node ID="ID_842" TEXT="Non-streaming requests can time out around 10 minutes — stream or use Batches for long jobs;…"><richcontent TYPE="NOTE"><html><head></head><body><p>Non-streaming requests can time out around 10 minutes — stream or use Batches for long jobs; don't set huge max_tokens without streaming.</p></body></html></richcontent></node><node ID="ID_843" TEXT="SDKs retry transient errors (connection, 429, 5xx) with backoff, 2 retries by default, honouring…"><richcontent TYPE="NOTE"><html><head></head><body><p>SDKs retry transient errors (connection, 429, 5xx) with backoff, 2 retries by default, honouring retry-after.</p></body></html></richcontent></node><node ID="ID_844" TEXT="SSE error events can arrive after a 200 response."></node></node><node ID="ID_849" TEXT="⚠ Exam traps"><node ID="ID_846" TEXT="529 overloaded_error is Anthropic-side; 429 rate_limit_error is your quota."></node><node ID="ID_847" TEXT="413: Messages cap is 32 MB, Batch 256 MB."></node><node ID="ID_848" TEXT="A 400 can also mean an organisation spend limit was hit, not only malformed input."></node></node></node><node ID="ID_864" TEXT="Rate limits" LINK="https://platform.claude.com/docs/en/api/rate-limits"><richcontent TYPE="NOTE"><html><head></head><body><p>https://platform.claude.com/docs/en/api/rate-limits</p></body></html></richcontent><node ID="ID_858" TEXT="Key facts"><node ID="ID_851" TEXT="Usage tiers by monthly spend cap: Start $500, Build $1,000, Scale $200,000, Custom; new orgs may…"><richcontent TYPE="NOTE"><html><head></head><body><p>Usage tiers by monthly spend cap: Start $500, Build $1,000, Scale $200,000, Custom; new orgs may start in an Evaluation tier.</p></body></html></richcontent></node><node ID="ID_852" TEXT="Three limits per model per organisation: requests/min (RPM), input tokens/min (ITPM), output…"><richcontent TYPE="NOTE"><html><head></head><body><p>Three limits per model per organisation: requests/min (RPM), input tokens/min (ITPM), output tokens/min (OTPM). Limits are per model — hitting Sonnet's limit doesn't block Opus.</p></body></html></richcontent></node><node ID="ID_853" TEXT="Cache-aware ITPM: input_tokens + cache_creation_input_tokens count; cache_read_input_tokens do…"><richcontent TYPE="NOTE"><html><head></head><body><p>Cache-aware ITPM: input_tokens + cache_creation_input_tokens count; cache_read_input_tokens do NOT (except Haiku 3.5). At 80% cache hit, 2M ITPM ≈ 10M effective tokens/min.</p></body></html></richcontent></node><node ID="ID_854" TEXT="Token-bucket enforcement with continuous replenishment — bursts get 429 even under the…"><richcontent TYPE="NOTE"><html><head></head><body><p>Token-bucket enforcement with continuous replenishment — bursts get 429 even under the per-minute number. max_tokens does not count toward OTPM.</p></body></html></richcontent></node><node ID="ID_855" TEXT="429 returns retry-after (seconds). A spend-cap 429 has details.error_code…"><richcontent TYPE="NOTE"><html><head></head><body><p>429 returns retry-after (seconds). A spend-cap 429 has details.error_code 'enforced_spend_limit_reached', no retry-after, and resets on the 1st at 00:00 UTC.</p></body></html></richcontent></node><node ID="ID_856" TEXT="Headers: anthropic-ratelimit-{requests,input-tokens,output-tokens}-{limit,remaining,reset};…"><richcontent TYPE="NOTE"><html><head></head><body><p>Headers: anthropic-ratelimit-{requests,input-tokens,output-tokens}-{limit,remaining,reset}; retry-after.</p></body></html></richcontent></node><node ID="ID_857" TEXT="Batches: 1,000 RPM shared across models; 200,000 requests in the processing queue; 100,000…"><richcontent TYPE="NOTE"><html><head></head><body><p>Batches: 1,000 RPM shared across models; 200,000 requests in the processing queue; 100,000 requests per batch.</p></body></html></richcontent></node></node><node ID="ID_862" TEXT="⚠ Exam traps"><node ID="ID_859" TEXT="Cache reads are free against ITPM — caching raises effective throughput, not just cost."></node><node ID="ID_860" TEXT="A spend-limit 429 has no retry-after and won't clear until next month."></node><node ID="ID_861" TEXT="Limits are per model per org/workspace."></node></node><node ID="ID_863" TEXT="{ } code"><richcontent TYPE="NOTE"><html><head></head><body><p>raw = client.messages.with_raw_response.create(model=&quot;claude-sonnet-5&quot;, max_tokens=64,</p><p>    messages=[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;ping&quot;}])</p><p>print(raw.headers[&quot;anthropic-ratelimit-input-tokens-remaining&quot;])</p></body></html></richcontent></node></node><node ID="ID_879" TEXT="Tool use overview" LINK="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview"><richcontent TYPE="NOTE"><html><head></head><body><p>https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview</p></body></html></richcontent><node ID="ID_873" TEXT="Key facts"><node ID="ID_865" TEXT="A tool = name, description, input_schema (JSON Schema object with properties and required)."></node><node ID="ID_866" TEXT="tool_choice: {type:'auto'} default — Claude decides; {type:'any'} — must call some tool;…"><richcontent TYPE="NOTE"><html><head></head><body><p>tool_choice: {type:'auto'} default — Claude decides; {type:'any'} — must call some tool; {type:'tool', name} — must call that tool; {type:'none'} — no tools. disable_parallel_tool_use: true inside tool_choice limits Claude to one call per turn; parallel calls are on by default and appear as several tool_use blocks in one response.</p></body></html></richcontent></node><node ID="ID_867" TEXT="When Claude calls a tool: stop_reason 'tool_use', block {type:'tool_use', id, name, input}. You…"><richcontent TYPE="NOTE"><html><head></head><body><p>When Claude calls a tool: stop_reason 'tool_use', block {type:'tool_use', id, name, input}. You reply in a USER message with {type:'tool_result', tool_use_id, content, is_error?}.</p></body></html></richcontent></node><node ID="ID_868" TEXT="Client tools run in your code (custom tools plus Anthropic-defined computer use, text editor,…"><richcontent TYPE="NOTE"><html><head></head><body><p>Client tools run in your code (custom tools plus Anthropic-defined computer use, text editor, bash, memory). Server tools run on Anthropic's side (web search, web fetch, code execution, MCP connector, tool search) — results come back directly; you do not send tool_result for them.</p></body></html></richcontent></node><node ID="ID_869" TEXT="strict: true on a custom tool guarantees the call's arguments match the schema exactly (no…"><richcontent TYPE="NOTE"><html><head></head><body><p>strict: true on a custom tool guarantees the call's arguments match the schema exactly (no hallucinated parameters).</p></body></html></richcontent></node><node ID="ID_870" TEXT="Tool use injects an automatic system prompt of ~286–800 tokens depending on model and…"><richcontent TYPE="NOTE"><html><head></head><body><p>Tool use injects an automatic system prompt of ~286–800 tokens depending on model and tool_choice (Opus 5 286/406; Sonnet 5 354/474; Haiku 4.5 496/588).</p></body></html></richcontent></node><node ID="ID_871" TEXT="Billing: tools, tool_use and tool_result blocks are input tokens; client tools have no extra…"><richcontent TYPE="NOTE"><html><head></head><body><p>Billing: tools, tool_use and tool_result blocks are input tokens; client tools have no extra charge; web search / code execution have per-use charges.</p></body></html></richcontent></node><node ID="ID_872" TEXT="Claude won't reliably invent missing required parameters — ask or make them optional; steer…"><richcontent TYPE="NOTE"><html><head></head><body><p>Claude won't reliably invent missing required parameters — ask or make them optional; steer eagerness in the system prompt.</p></body></html></richcontent></node></node><node ID="ID_877" TEXT="⚠ Exam traps"><node ID="ID_874" TEXT="Server tools (web search, code execution) need no tool_result from you; client tools do."></node><node ID="ID_875" TEXT="any = some tool; tool = a named tool; none = disabled; auto = default."></node><node ID="ID_876" TEXT="disable_parallel_tool_use lives inside tool_choice, not at the top level."></node></node><node ID="ID_878" TEXT="{ } code"><richcontent TYPE="NOTE"><html><head></head><body><p>tools = [{&quot;name&quot;:&quot;get_weather&quot;,</p><p>  &quot;description&quot;:&quot;Get the current weather for a city. Use whenever the user asks about weather or temperature. Returns a short text summary.&quot;,</p><p>  &quot;input_schema&quot;:{&quot;type&quot;:&quot;object&quot;,&quot;properties&quot;:{&quot;location&quot;:{&quot;type&quot;:&quot;string&quot;}},&quot;required&quot;:[&quot;location&quot;]}}]</p><p>r = client.messages.create(model=&quot;claude-sonnet-5&quot;, max_tokens=1024, tools=tools,</p><p>    messages=[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;Weather in Pune?&quot;}])</p><p>tu = next(b for b in r.content if b.type == &quot;tool_use&quot;)</p><p>r2 = client.messages.create(model=&quot;claude-sonnet-5&quot;, max_tokens=1024, tools=tools, messages=[</p><p>    {&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;Weather in Pune?&quot;},</p><p>    {&quot;role&quot;:&quot;assistant&quot;,&quot;content&quot;: r.content},</p><p>    {&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:[{&quot;type&quot;:&quot;tool_result&quot;,&quot;tool_use_id&quot;:tu.id,&quot;content&quot;:&quot;31°C, humid&quot;}]}])</p></body></html></richcontent></node></node><node ID="ID_894" TEXT="Define tools and handle tool calls" LINK="https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools"><richcontent TYPE="NOTE"><html><head></head><body><p>https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools</p></body></html></richcontent><node ID="ID_888" TEXT="Key facts"><node ID="ID_880" TEXT="name must match ^[a-zA-Z0-9_-]{1,64}$; description and input_schema are required; optional:…"><richcontent TYPE="NOTE"><html><head></head><body><p>name must match ^[a-zA-Z0-9_-]{1,64}$; description and input_schema are required; optional: input_examples, cache_control, strict, defer_loading, allowed_callers.</p></body></html></richcontent></node><node ID="ID_881" TEXT="The description is the single most important factor — 3–4+ sentences: what it does, when to use…"><richcontent TYPE="NOTE"><html><head></head><body><p>The description is the single most important factor — 3–4+ sentences: what it does, when to use it, what each parameter means, what it returns. Prefer fewer, more capable tools; namespace names (github_list_prs).</p></body></html></richcontent></node><node ID="ID_882" TEXT="tool_choice any/tool are NOT supported with manual extended thinking, nor on Fable/Mythos 5.1 —…"><richcontent TYPE="NOTE"><html><head></head><body><p>tool_choice any/tool are NOT supported with manual extended thinking, nor on Fable/Mythos 5.1 — use auto + strict:true. With any/tool Claude emits no text before the tool_use block. Changing tool_choice invalidates cached message blocks (tools/system cache stays).</p></body></html></richcontent></node><node ID="ID_883" TEXT="tool_result content may be a string or a list of text/image/document/search_result blocks; empty…"><richcontent TYPE="NOTE"><html><head></head><body><p>tool_result content may be a string or a list of text/image/document/search_result blocks; empty content is allowed; is_error defaults to false.</p></body></html></richcontent></node><node ID="ID_884" TEXT="tool_result blocks must immediately follow the tool_use turn and must come FIRST in the user…"><richcontent TYPE="NOTE"><html><head></head><body><p>tool_result blocks must immediately follow the tool_use turn and must come FIRST in the user content array (text after them). Every tool_use id needs a matching tool_result or the API errors: 'tool_use ids were found without tool_result blocks'.</p></body></html></richcontent></node><node ID="ID_885" TEXT="Parallel calls: return all results in one user message, one block per call."></node><node ID="ID_886" TEXT="Write instructive error messages ('Rate limit exceeded. Retry after 60 seconds.'); Claude…"><richcontent TYPE="NOTE"><html><head></head><body><p>Write instructive error messages ('Rate limit exceeded. Retry after 60 seconds.'); Claude typically retries 2–3 times on invalid-parameter errors.</p></body></html></richcontent></node><node ID="ID_887" TEXT="Keep untrusted tool output inside tool_result blocks (never in system or plain text) to limit…"><richcontent TYPE="NOTE"><html><head></head><body><p>Keep untrusted tool output inside tool_result blocks (never in system or plain text) to limit prompt injection.</p></body></html></richcontent></node></node><node ID="ID_892" TEXT="⚠ Exam traps"><node ID="ID_889" TEXT="tool_result goes in a user message and must precede any text in that message."></node><node ID="ID_890" TEXT="is_error:true tells Claude the execution failed; it does not end the conversation."></node><node ID="ID_891" TEXT="Forced tool use (any/tool) is incompatible with manual extended thinking."></node></node><node ID="ID_893" TEXT="{ } code"><richcontent TYPE="NOTE"><html><head></head><body><p>results = []</p><p>for b in r.content:</p><p>    if b.type == &quot;tool_use&quot;:</p><p>        try:</p><p>            out = REGISTRY[b.name](**b.input)</p><p>            results.append({&quot;type&quot;:&quot;tool_result&quot;,&quot;tool_use_id&quot;:b.id,&quot;content&quot;:str(out)})</p><p>        except Exception as e:</p><p>            results.append({&quot;type&quot;:&quot;tool_result&quot;,&quot;tool_use_id&quot;:b.id,</p><p>                            &quot;content&quot;:f&quot;Error: {e}. Check the arguments and retry.&quot;,&quot;is_error&quot;:True})</p><p>messages.append({&quot;role&quot;:&quot;user&quot;,&quot;content&quot;: results})   # results first, then any text</p></body></html></richcontent></node></node><node ID="ID_909" TEXT="Prompt caching" LINK="https://platform.claude.com/docs/en/build-with-claude/prompt-caching"><richcontent TYPE="NOTE"><html><head></head><body><p>https://platform.claude.com/docs/en/build-with-claude/prompt-caching</p></body></html></richcontent><node ID="ID_903" TEXT="Key facts"><node ID="ID_895" TEXT="Mark a breakpoint with cache_control: {type:'ephemeral'}; optional ttl '5m' (default) or '1h'.…"><richcontent TYPE="NOTE"><html><head></head><body><p>Mark a breakpoint with cache_control: {type:'ephemeral'}; optional ttl '5m' (default) or '1h'. Up to 4 breakpoints per request.</p></body></html></richcontent></node><node ID="ID_896" TEXT="Pricing: 5-min write 1.25× base input; 1-hour write 2×; read 0.1× (0.025× on Fable/Mythos 5.1)."></node><node ID="ID_897" TEXT="Minimum cacheable prefix by model: 512 tokens (Fable 5.1, Opus 5, Fable 5); 1,024 (Opus 4.8,…"><richcontent TYPE="NOTE"><html><head></head><body><p>Minimum cacheable prefix by model: 512 tokens (Fable 5.1, Opus 5, Fable 5); 1,024 (Opus 4.8, Sonnet 5, Sonnet 4.6, Sonnet 4.5); 2,048 (Opus 4.7, Haiku 3.5); 4,096 (Opus 4.6, Opus 4.5, Haiku 4.5). Shorter prefixes are processed but silently not cached.</p></body></html></richcontent></node><node ID="ID_898" TEXT="Prefix order: tools → system → messages. The system looks ~20 blocks back from each breakpoint…"><richcontent TYPE="NOTE"><html><head></head><body><p>Prefix order: tools → system → messages. The system looks ~20 blocks back from each breakpoint for an earlier cache hit.</p></body></html></richcontent></node><node ID="ID_899" TEXT="usage: cache_creation_input_tokens, cache_read_input_tokens, input_tokens (tokens after the last…"><richcontent TYPE="NOTE"><html><head></head><body><p>usage: cache_creation_input_tokens, cache_read_input_tokens, input_tokens (tokens after the last breakpoint); total input = the sum.</p></body></html></richcontent></node><node ID="ID_900" TEXT="Invalidation: changing tool definitions invalidates everything; toggling web search/citations…"><richcontent TYPE="NOTE"><html><head></head><body><p>Invalidation: changing tool definitions invalidates everything; toggling web search/citations invalidates system+messages; changing tool_choice or adding/removing images invalidates the messages cache; thinking config changes usually invalidate; changing top-level effort invalidates the message cache.</p></body></html></richcontent></node><node ID="ID_901" TEXT="Cannot cache thinking blocks directly, citation sub-blocks or empty text blocks; cached tokens…"><richcontent TYPE="NOTE"><html><head></head><body><p>Cannot cache thinking blocks directly, citation sub-blocks or empty text blocks; cached tokens still count toward the context window.</p></body></html></richcontent></node><node ID="ID_902" TEXT="Place the breakpoint on the last block identical across requests; never on content with…"><richcontent TYPE="NOTE"><html><head></head><body><p>Place the breakpoint on the last block identical across requests; never on content with timestamps or per-request data. For chat, a moving breakpoint on the newest message caches history incrementally.</p></body></html></richcontent></node></node><node ID="ID_907" TEXT="⚠ Exam traps"><node ID="ID_904" TEXT="Reads are 10% of base price; writes cost MORE than base — caching only pays with reuse."></node><node ID="ID_905" TEXT="Order is tools → system → messages: a changed tool definition busts every downstream cache."></node><node ID="ID_906" TEXT="A 1,000-token system prompt on Haiku 4.5 or Opus 4.6 (4,096 minimum) will silently not be cached."></node></node><node ID="ID_908" TEXT="{ } code"><richcontent TYPE="NOTE"><html><head></head><body><p>r = client.messages.create(model=&quot;claude-sonnet-5&quot;, max_tokens=300,</p><p>    system=[{&quot;type&quot;:&quot;text&quot;,&quot;text&quot;: BIG_STATIC_CONTEXT,</p><p>             &quot;cache_control&quot;:{&quot;type&quot;:&quot;ephemeral&quot;,&quot;ttl&quot;:&quot;1h&quot;}}],</p><p>    messages=[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;Summarise section 3.&quot;}])</p><p>u = r.usage</p><p>print(u.cache_creation_input_tokens, u.cache_read_input_tokens, u.input_tokens)</p></body></html></richcontent></node></node><node ID="ID_922" TEXT="Message Batches API" LINK="https://platform.claude.com/docs/en/build-with-claude/batch-processing"><richcontent TYPE="NOTE"><html><head></head><body><p>https://platform.claude.com/docs/en/build-with-claude/batch-processing</p></body></html></richcontent><node ID="ID_916" TEXT="Key facts"><node ID="ID_910" TEXT="50% off standard prices on input and output; stacks with prompt caching."></node><node ID="ID_911" TEXT="Limits: 100,000 requests or 256 MB per batch. Most batches finish within 1 hour; hard limit 24…"><richcontent TYPE="NOTE"><html><head></head><body><p>Limits: 100,000 requests or 256 MB per batch. Most batches finish within 1 hour; hard limit 24 hours, after which unfinished requests are 'expired' (not billed). Results retrievable for 29 days.</p></body></html></richcontent></node><node ID="ID_912" TEXT="Each request: custom_id (1–64 chars, unique) + params (normal Messages params).…"><richcontent TYPE="NOTE"><html><head></head><body><p>Each request: custom_id (1–64 chars, unique) + params (normal Messages params). processing_status: in_progress → ended (or canceling → ended). Per-request result.type: succeeded, errored, canceled, expired — only succeeded is billed.</p></body></html></richcontent></node><node ID="ID_913" TEXT="Results are NOT returned in input order — match on custom_id; delivered as a .jsonl stream."></node><node ID="ID_914" TEXT="Not supported: stream:true, fast mode. Supported: vision, tool use incl. server tools, system…"><richcontent TYPE="NOTE"><html><head></head><body><p>Not supported: stream:true, fast mode. Supported: vision, tool use incl. server tools, system prompts, multi-turn, extended thinking, caching, all active models.</p></body></html></richcontent></node><node ID="ID_915" TEXT="Endpoints: POST /v1/messages/batches; GET …/{id}; GET …; POST …/{id}/cancel; GET …/{id}/results;…"><richcontent TYPE="NOTE"><html><head></head><body><p>Endpoints: POST /v1/messages/batches; GET …/{id}; GET …; POST …/{id}/cancel; GET …/{id}/results; DELETE …/{id}. Validation errors surface asynchronously in results.</p></body></html></richcontent></node></node><node ID="ID_920" TEXT="⚠ Exam traps"><node ID="ID_917" TEXT="Discount is 50% on input AND output, and combines with caching."></node><node ID="ID_918" TEXT="'Guaranteed within 24 hours' is wrong — requests may expire at 24 h; typical is under an hour."></node><node ID="ID_919" TEXT="Results are unordered; custom_id is the only reliable join key."></node></node><node ID="ID_921" TEXT="{ } code"><richcontent TYPE="NOTE"><html><head></head><body><p>from anthropic.types.message_create_params import MessageCreateParamsNonStreaming</p><p>from anthropic.types.messages.batch_create_params import Request</p><p>batch = client.messages.batches.create(requests=[</p><p>    Request(custom_id=f&quot;ticket-{i}&quot;, params=MessageCreateParamsNonStreaming(</p><p>        model=&quot;claude-haiku-4-5-20251001&quot;, max_tokens=200,</p><p>        messages=[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:t}])) for i, t in enumerate(tickets)])</p><p>while client.messages.batches.retrieve(batch.id).processing_status != &quot;ended&quot;:</p><p>    time.sleep(30)</p><p>for res in client.messages.batches.results(batch.id):</p><p>    print(res.custom_id, res.result.type)</p></body></html></richcontent></node></node></node><node ID="ID_946" TEXT="❓ Self-check"><node ID="ID_925" TEXT="Claude returns two tool_use blocks in one response. How do you reply?"><node ID="ID_924" TEXT="→ One user message with two tool_result blocks, each matching a tool_use_id."></node></node><node ID="ID_927" TEXT="Your cache never hits. Most likely cause?"><node ID="ID_926" TEXT="→ Content before the breakpoint changes each call, or the prefix is under the minimum cacheable length (~1,024 tokens)."></node></node><node ID="ID_929" TEXT="Cheapest way to classify 50,000 support tickets overnight?"><node ID="ID_928" TEXT="→ Message Batches API with a small model (Haiku), a cached system prompt, and low max_tokens."></node></node><node ID="ID_931" TEXT="What must be true of a tool description?"><node ID="ID_930" TEXT="→ Detailed: what it does, when to use it, what each parameter means, what it returns — it drives tool selection more than the prompt."></node></node><node ID="ID_933" TEXT="How do you guarantee a response follows a JSON schema using tools?"><node ID="ID_932" TEXT="→ Define one tool with that input_schema and set tool_choice={type:'tool', name:...}; read the tool_use input as your structured output."></node></node><node ID="ID_935" TEXT="A tool call fails with a stack trace. What do you send back?"><node ID="ID_934" TEXT="→ A tool_result with is_error:true and a short error message so Claude can retry or explain."></node></node><node ID="ID_937" TEXT="Order of the cacheable prefix?"><node ID="ID_936" TEXT="→ tools → system → messages; a breakpoint caches everything up to and including its block."></node></node><node ID="ID_939" TEXT="Cache read vs write pricing?"><node ID="ID_938" TEXT="→ Write ≈ 1.25× base input (2× for 1-hour TTL); read ≈ 0.1× base input."></node></node><node ID="ID_941" TEXT="What does retry-after on a 429 mean?"><node ID="ID_940" TEXT="→ Wait at least that many seconds before retrying; the SDK honours it automatically."></node></node><node ID="ID_943" TEXT="Batch results arrive out of order. How do you match them?"><node ID="ID_942" TEXT="→ By custom_id you set on each request."></node></node><node ID="ID_945" TEXT="Which errors does the SDK retry by default?"><node ID="ID_944" TEXT="→ 408, 409, 429, 5xx and connection errors — 2 retries with backoff; 400/401 are not retried."></node></node></node><node ID="ID_963" TEXT="⚠ Traps to drill"><node ID="ID_948" TEXT="Who runs a client-side tool?"><node ID="ID_947" TEXT="→ Your code. Claude only returns a tool_use block with name and input."></node></node><node ID="ID_950" TEXT="Which role carries tool_result blocks?"><node ID="ID_949" TEXT="→ user. One message, one tool_result per tool_use, ids must match."></node></node><node ID="ID_952" TEXT="Order of content in the cached prefix?"><node ID="ID_951" TEXT="→ tools → system → messages. Change anything before the breakpoint and the cache is invalidated."></node></node><node ID="ID_954" TEXT="Does caching cut output tokens?"><node ID="ID_953" TEXT="→ No — only input cost and time-to-first-token. Output is priced the same."></node></node><node ID="ID_956" TEXT="Batch API for a chatbot?"><node ID="ID_955" TEXT="→ Never — asynchronous, up to 24 h. Batch = bulk, offline, 50% cheaper."></node></node><node ID="ID_958" TEXT="Minimum cacheable prefix length?"><node ID="ID_957" TEXT="→ Model-dependent: 512 (Opus 5) · 1,024 (Sonnet 5/4.5) · 2,048 (Opus 4.7) · 4,096 (Haiku 4.5, Opus 4.5/4.6); shorter is silently not cached."></node></node><node ID="ID_960" TEXT="tool_choice value that forces one specific tool?"><node ID="ID_959" TEXT="→ {type:'tool', name:'...'}; 'any' forces some tool; 'auto' is default; 'none' disables."></node></node><node ID="ID_962" TEXT="429 vs 529?"><node ID="ID_961" TEXT="→ 429 rate limit (your volume, honour retry-after); 529 overloaded (Anthropic side, back off with jitter)."></node></node></node><node ID="ID_971" TEXT="🗒 Cheat sheet"><node ID="ID_964" TEXT="Tool loop: tools + messages → stop_reason tool_use → run it yourself → user msg with tool_result(tool_use_id, content, is_error?) → repeat until end_turn"></node><node ID="ID_965" TEXT="tool_choice: auto (default) · any (must use some) · tool:{name} (must use that) · none; disable_parallel_tool_use for one-at-a-time"></node><node ID="ID_966" TEXT="Parallel calls: several tool_use blocks → all results in ONE user message"></node><node ID="ID_967" TEXT="Cache: cache_control ephemeral on a block; prefix = tools→system→messages up to it; min 512–4,096 tokens by model (Sonnet 5 = 1,024, Haiku 4.5 = 4,096); TTL 5 min (refreshes) or 1 h; write 1.25×/2×, read 0.1×; ≤4 breakpoints; reads free vs ITPM"></node><node ID="ID_968" TEXT="Batches: custom_id per request, up to 100k reqs, ≤24 h, 50% off, results 29 days, join on custom_id, types succeeded/errored/canceled/expired"></node><node ID="ID_969" TEXT="Errors: 400 yours · 401 key · 413 too large · 429 rate limit (retry-after) · 500 api · 529 overloaded; SDK retries 408/409/429/5xx ×2"></node><node ID="ID_970" TEXT="Rate limits per org per model: RPM, ITPM, OTPM; tiers grow with spend"></node></node><node ID="ID_978" TEXT="☑ Chapter checklist"><node ID="ID_972" TEXT="Can draw the tool-use loop (request → tool_use → run tool → tool_result → final answer) from memory"></node><node ID="ID_973" TEXT="Know what tool_choice auto / any / tool / none do"></node><node ID="ID_974" TEXT="Cache hit visible in usage: cache_read_input_tokens &gt; 0"></node><node ID="ID_975" TEXT="Batch job submitted, polled, results parsed by custom_id"></node><node ID="ID_976" TEXT="Retry/backoff wrapper handles 429 and 529"></node><node ID="ID_977" TEXT="notes/tools-caching-batch.md written"></node></node></node>
</map>
