← Blog

Claude AI for Developers: A Practical Guide to the Anthropic API

·5 min read·Claude AIAILLMAnthropicPythonTypeScript

Claude is Anthropic's family of large language models, designed with safety and reliability as first-class constraints. If you've been building with OpenAI, Claude will feel familiar — but there are meaningful differences in how it handles context, tool use, and long-form reasoning that are worth understanding before you ship something to production.

This post covers the practical side: setting up the SDK, prompt design, tool use, caching, and patterns that actually hold up under real workloads.


Model Family Overview

ModelContext WindowBest For
Opus 4.7200k tokensComplex reasoning, agentic workflows
Sonnet 4.6200k tokensBalanced cost/performance, most use cases
Haiku 4.5200k tokensHigh-throughput, latency-sensitive tasks

Start with Sonnet for most things. Move to Opus for tasks that need multi-step reasoning or you're getting unsatisfying results. Use Haiku when you need to run inference at scale and latency matters.


Getting Started

pip install anthropic
# or
pnpm add @anthropic-ai/sdk

Basic request in Python:

import anthropic
 
client = anthropic.Anthropic()
 
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Explain prompt caching in two sentences."}
    ]
)
 
print(message.content[0].text)

And in TypeScript:

import Anthropic from "@anthropic-ai/sdk";
 
const client = new Anthropic();
 
const message = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: [
        { role: "user", content: "Explain prompt caching in two sentences." },
    ],
});
 
console.log(message.content[0].text);

System Prompts That Actually Work

Claude responds well to direct, specific system prompts. Avoid vague instructions like "be helpful." Instead, define the persona, constraints, and output format explicitly.

system = """You are a senior TypeScript engineer.
- Answer questions about TypeScript, React, and Node.js
- Provide working code examples
- If unsure, say so instead of guessing
- Format code in markdown code blocks with language tags
- Keep explanations concise — developers prefer examples over prose"""

Claude follows multi-point instructions reliably. You can also use XML tags to structure complex prompts:

system = """
<persona>
You are a code reviewer focused on correctness and security.
</persona>
 
<rules>
- Flag any SQL injection, XSS, or SSRF vulnerabilities
- Note missing error handling
- Suggest specific fixes, not just "handle this error"
</rules>
 
<output_format>
Return a JSON array of findings:
[{"severity": "high|medium|low", "line": N, "issue": "...", "fix": "..."}]
</output_format>
"""

Tool Use (Function Calling)

Tool use lets Claude call functions you define. The model decides when a tool is needed, you execute it, and return the result.

import anthropic
import json
 
client = anthropic.Anthropic()
 
tools = [
    {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name"},
                "units": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    }
]
 
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "What's the weather in Lima?"}]
)
 
# Claude returns a tool_use block
if response.stop_reason == "tool_use":
    tool_call = next(b for b in response.content if b.type == "tool_use")
    tool_input = tool_call.input
 
    # Execute your function
    result = get_weather(tool_input["city"], tool_input.get("units", "celsius"))
 
    # Send result back
    followup = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "What's the weather in Lima?"},
            {"role": "assistant", "content": response.content},
            {
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_call.id,
                    "content": json.dumps(result)
                }]
            }
        ]
    )
    print(followup.content[0].text)

Prompt Caching

Prompt caching is Claude's most impactful cost optimization. When you mark part of your prompt with cache_control, Anthropic caches that prefix and reuses it across requests — reducing cost by up to 90% and latency by ~85% on cache hits.

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": LARGE_SYSTEM_PROMPT,  # e.g., 10k token context doc
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[
        {"role": "user", "content": user_question}
    ]
)

Cache TTL is 5 minutes by default. Extend it by including the cached block in each request within that window.

When to use it:

  • Long system prompts (docs, code context, personas)
  • RAG pipelines where context is reused across queries
  • Multi-turn conversations with a fixed instruction block

Streaming

For UIs, streaming dramatically improves perceived responsiveness:

const stream = await client.messages.stream({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: [{ role: "user", content: prompt }],
});
 
for await (const chunk of stream) {
    if (
        chunk.type === "content_block_delta" &&
        chunk.delta.type === "text_delta"
    ) {
        process.stdout.write(chunk.delta.text);
    }
}
 
const final = await stream.finalMessage();
console.log(
    "Total tokens:",
    final.usage.input_tokens + final.usage.output_tokens,
);

Extended Thinking

For hard problems — logic puzzles, math, complex code generation — enable extended thinking:

response = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000  # how much Claude can "think"
    },
    messages=[{"role": "user", "content": "Design a distributed rate limiter..."}]
)
 
for block in response.content:
    if block.type == "thinking":
        print("Reasoning:", block.thinking)
    elif block.type == "text":
        print("Answer:", block.text)

Thinking tokens are billed but not cached. Reserve this for tasks where quality matters more than cost.


Multi-turn Conversations

Claude has no built-in memory — you maintain the conversation history yourself:

history = []
 
def chat(user_message: str) -> str:
    history.append({"role": "user", "content": user_message})
 
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system="You are a helpful coding assistant.",
        messages=history
    )
 
    assistant_message = response.content[0].text
    history.append({"role": "assistant", "content": assistant_message})
    return assistant_message
 
chat("How do I debounce a function in TypeScript?")
chat("Now show me how to test that with Jest.")

For production, persist history in Redis or Postgres and load it per session.


Rate Limits and Error Handling

from anthropic import RateLimitError, APIStatusError
import time
 
def call_with_retry(messages, retries=3):
    for attempt in range(retries):
        try:
            return client.messages.create(
                model="claude-sonnet-4-6",
                max_tokens=1024,
                messages=messages
            )
        except RateLimitError:
            wait = 2 ** attempt
            print(f"Rate limited, waiting {wait}s...")
            time.sleep(wait)
        except APIStatusError as e:
            if e.status_code >= 500:
                time.sleep(2 ** attempt)
            else:
                raise
    raise Exception("Max retries exceeded")

Key Takeaways

  • Use Sonnet for most workloads, Opus for complex reasoning, Haiku for volume
  • Prompt caching is the single biggest lever for cost reduction on repeated context
  • Tool use is reliable and well-suited for agentic workflows
  • Extended thinking is worth the cost for hard, single-shot tasks
  • Maintain conversation history yourself — Claude has no session state

The Anthropic SDK is well-documented and actively maintained. Start with the official docs for the latest model capabilities.