using-claude-api
Use when writing or debugging code that calls Anthropic SDK. Covers Opus 4.7, prompt caching, tool use, vision, batch, thinking.
Files1
View on GitHubSKILL.mdskills/using-claude-api/SKILL.md
GitHubUsing Claude API — Practical Patterns
This skill complements the official claude-api skill with practical patterns and gotchas. Use this when you're writing or modifying code that imports anthropic (Python) / @anthropic-ai/sdk (TS).
Iron Law
Prompt caching is not optional on system prompts >1024 tokens. Adding cache_control is a 30-second change that cuts cost ~90% and latency ~85% on cache hits. If you write or modify a system prompt without it, you've left money on the table.
Model IDs (May 2026)
claude-opus-4-7— most capable, 1M context available, supports extended thinkingclaude-sonnet-4-6— balanced cost/quality, 200K context, supports thinkingclaude-haiku-4-5-20251001— fastest/cheapest, 200K context- Older
-20241022/-20250514IDs still work but use latest when starting new code.
If migrating retired models: see official claude-api skill for the mapping. Don't guess.
Prompt Caching — MUST for System Prompts
client.messages.create(
model="claude-opus-4-7",
system=[
{
"type": "text",
"text": LONG_SYSTEM_PROMPT, # >1024 tokens
"cache_control": {"type": "ephemeral"}
}
],
messages=[...]
)
- Cache lives 5 minutes (refreshes on each hit).
- Up to 4 cache breakpoints per request. Put them at stable boundaries (system / tool definitions / docs / dynamic user input).
- Order matters: cached prefix must be identical byte-for-byte to hit. Don't interpolate timestamps before the cache boundary.
- Check
usage.cache_read_input_tokensandcache_creation_input_tokensin the response to verify hits.
Tool Use Loop
response.stop_reason tells you what to do next:
"end_turn"— model finished, return to user"tool_use"— model wants to call a tool. You MUST run the tool and continue the conversation with atool_resultmessage."max_tokens"— you setmax_tokenstoo low, output truncated"stop_sequence"— hit a stop_sequence
Full loop:
messages = [{"role": "user", "content": "..."}]
while True:
resp = client.messages.create(model=..., tools=[...], messages=messages)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
break
tool_results = []
for block in resp.content:
if block.type == "tool_use":
result = run_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
messages.append({"role": "user", "content": tool_results})
Streaming
with client.messages.stream(...) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Use streaming for: long outputs, latency-sensitive UI, anywhere TTFB matters. Don't use for: batch processing, when you need full response before acting.
Vision
- Pass image as
{"type": "image", "source": {"type": "url", "url": "..."}}— prefer URL (less bandwidth, simpler). - Or base64:
{"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": ...}}. - Max image size: 5MB (base64) or larger via URL. Resize before sending to save tokens.
- Image counts as tokens (~1.6K for 1568x1568). Don't paste huge images blindly.
See [[extracting-from-images]] for how to interpret images Claude receives.
Extended Thinking (Opus 4.x)
client.messages.create(
model="claude-opus-4-7",
thinking={"type": "enabled", "budget_tokens": 8000},
max_tokens=16000, # MUST be > budget_tokens
messages=[...]
)
- Response includes
thinkingblocks before the final answer. - Use for hard reasoning: math, planning, debugging. Skip for chat/Q&A.
budget_tokensis a soft cap; model may use less.- Cost: thinking tokens count toward output billing.
Batch API
batch = client.messages.batches.create(requests=[
{"custom_id": "req-1", "params": {"model": ..., "messages": ...}},
...
])
- 50% cheaper than realtime.
- SLA: 24 hours, usually much faster.
- Use for: bulk classification, eval runs, dataset processing.
- Don't use for: interactive flows, latency-sensitive work.
Files API
For PDF/video/large docs you reference repeatedly:
file = client.files.upload(file=open("doc.pdf", "rb"))
# then reference file.id in messages
Persists across requests; uploaded once, referenced many times. Cheaper than re-sending the doc content.
Error Handling
| Code | Meaning | Action |
|---|---|---|
| 400 | Bad request (malformed) | Fix the request, don't retry |
| 401 | Auth | Check API key |
| 403 | Forbidden | Check permissions / org access |
| 429 | Rate limit | Exponential backoff (1s, 2s, 4s, 8s...) |
| 500 | Server error | Retry with backoff |
| 529 | Overloaded | Retry with longer backoff (15s, 30s...) |
Anthropic SDK has built-in retries (max_retries, default 2). Set higher (e.g. 5) for batch/background work, leave default for interactive.
Don't blind-retry 400s. They won't succeed.
Red Flags
- You wrote a system prompt >1K tokens with no
cache_control→ you just made the user's bill 10x bigger than it needs to be. - You hit a tool_use response and called
messages.createagain without appending thetool_result→ API errors. - You set
max_tokens=4096and output got truncated → bump to 8192 or 16384. For Opus 4.7, max is much higher. - You used
client.messages.createin awhile True:with no exit condition fortool_use→ infinite loop if model loops on tools. - You're sending the same big PDF on every request instead of using Files API.
- You used Batch API for interactive flow → user waits hours.
- You set
thinking.budget_tokens=20000butmax_tokens=10000→ 400 error, budget must be < max.
Not For
- Writing code against OpenAI / Cohere / Gemini / generic-LLM-wrapper SDKs.
- Pure linguistics / prompt-content questions (use
[[prompt-engineering]]). - Files named
*-openai.py/*-generic-llm.ts.
See Also
[[prompt-engineering]]— for writing the prompt content itself.[[extracting-from-images]]— for handling image inputs.- Official
claude-apiskill — for migration guides and the canonical reference. [[using-context7]]—mcp__context7__resolve-library-idwith "anthropic" pulls latest SDK docs.