Complete Claude API Guide 2026: Models, Features, Patterns
# The definitive Claude API guide for 2026: model selection, prompt caching, tool use, extended thinking, streaming, batch processing, and the patterns that make Claude-powered apps reliable in production.
Claude is Anthropic's production-grade LLM family. In 2026, the Claude 4 series (Haiku, Sonnet, Opus) handles everything from fast classification to frontier-level reasoning. This guide covers the API features that matter for production — not the hello-world examples in the official docs, but the patterns that make real apps work.
Model Selection Guide
// Claude 4 model IDs (2026)
const MODELS = {
HAIKU: "claude-haiku-4-5-20251001", // Fast, cheap, great for routing/classification
SONNET: "claude-sonnet-4-6", // Best quality/cost for most production work
OPUS: "claude-opus-4-8", // Frontier quality for complex reasoning
} as const;
| Model | Best use | Input price/M | Output price/M | Context | |-------|---------|--------------|----------------|---------| | Haiku 4.5 | Classification, quick tasks, routing | $0.80 | $4.00 | 200K | | Sonnet 4.6 | Chat, code, analysis, RAG | $3.00 | $15.00 | 200K | | Opus 4.8 | Complex reasoning, multi-step agents | $15.00 | $75.00 | 200K |
Default recommendation: Claude Sonnet for 90% of production workloads. Switch to Haiku when volume is high or latency is critical. Use Opus only when Sonnet demonstrably fails on your task.
Installation and Setup
npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
// Optional: custom retry configuration
maxRetries: 3,
timeout: 120_000, // 2 minutes
});
Core Patterns
Basic Message
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: "What is RAG?" }],
});
console.log(response.content[0].text);
console.log(`Tokens: ${response.usage.input_tokens} in, ${response.usage.output_tokens} out`);
System Prompt + Message
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: "You are a concise technical writer. Answer in 2-3 sentences.",
messages: [{ role: "user", content: "Explain vector embeddings." }],
});
Multi-Turn Conversation
const messages: Anthropic.MessageParam[] = [];
async function chat(userMessage: string): Promise<string> {
messages.push({ role: "user", content: userMessage });
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
messages,
});
const assistantMessage = response.content[0].text;
messages.push({ role: "assistant", content: assistantMessage });
return assistantMessage;
}
Prompt Caching
Cache large, stable context (system prompts, documents) to cut costs 80–90%:
const LARGE_SYSTEM = `[Your 2000+ token system prompt here]`;
const DOCUMENT = `[Large document or knowledge base]`;
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: [
{ type: "text", text: LARGE_SYSTEM, cache_control: { type: "ephemeral" } },
{ type: "text", text: DOCUMENT, cache_control: { type: "ephemeral" } },
],
messages: [{ role: "user", content: userQuery }],
});
// Check if cache was used
console.log({
cache_hits: response.usage.cache_read_input_tokens,
cache_misses: response.usage.cache_write_input_tokens,
});
Minimum 1,024 tokens required to create a cache entry. TTL: 5 minutes. See Prompt Caching Mastery.
Tool Use (Function Calling)
import { z } from "zod";
const tools: Anthropic.Tool[] = [
{
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"], default: "celsius" },
},
required: ["city"],
},
},
];
async function agentLoop(userMessage: string): Promise<string> {
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: userMessage },
];
while (true) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
tools,
messages,
});
messages.push({ role: "assistant", content: response.content });
if (response.stop_reason === "end_turn") {
return response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
}
if (response.stop_reason === "tool_use") {
const toolResults: Anthropic.ToolResultBlockParam[] = await Promise.all(
response.content
.filter((b): b is Anthropic.ToolUseBlock => b.type === "tool_use")
.map(async (block) => ({
type: "tool_result" as const,
tool_use_id: block.id,
content: JSON.stringify(await callTool(block.name, block.input)),
}))
);
messages.push({ role: "user", content: toolResults });
}
}
}
Streaming
// Stream tokens as they're generated
const stream = await client.messages.stream({
model: "claude-sonnet-4-6",
max_tokens: 2048,
messages: [{ role: "user", content: "Write a technical blog post about RAG" }],
});
for await (const event of stream) {
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
process.stdout.write(event.delta.text);
}
}
const finalMessage = await stream.finalMessage();
console.log("\nTotal tokens:", finalMessage.usage);
Structured Output (via Tool Use)
Force structured JSON output using tool_choice:
async function extractStructured<T>(
content: string,
schema: z.ZodSchema<T>,
toolName: string,
description: string
): Promise<T> {
const jsonSchema = zodToJsonSchema(schema) as Anthropic.Tool["input_schema"];
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
tools: [{ name: toolName, description, input_schema: jsonSchema }],
tool_choice: { type: "tool", name: toolName },
messages: [{ role: "user", content }],
});
const toolUse = response.content.find((b) => b.type === "tool_use");
if (!toolUse || toolUse.type !== "tool_use") throw new Error("No tool use in response");
return schema.parse(toolUse.input);
}
Extended Thinking
For complex reasoning tasks:
const response = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 16_000,
thinking: {
type: "enabled",
budget_tokens: 10_000, // how much to "think" before responding
},
messages: [{ role: "user", content: complexMathProblem }],
});
const thinking = response.content.filter((b) => b.type === "thinking");
const answer = response.content.filter((b) => b.type === "text");
console.log("Model reasoning:", thinking.map((b) => b.thinking).join("\n"));
console.log("Answer:", answer.map((b) => b.text).join("\n"));
See Token Budget Optimization for when extended thinking pays off.
Batch Processing (50% Cost Reduction)
For non-time-sensitive processing:
const batch = await client.messages.batches.create({
requests: documents.map((doc, i) => ({
custom_id: `doc-${i}`,
params: {
model: "claude-sonnet-4-6",
max_tokens: 500,
messages: [{ role: "user", content: `Summarize: ${doc}` }],
},
})),
});
console.log(`Batch ID: ${batch.id}`);
// Poll for completion (results within 24 hours)
let batchStatus = batch;
while (batchStatus.processing_status !== "ended") {
await sleep(30_000);
batchStatus = await client.messages.batches.retrieve(batch.id);
console.log(`Status: ${batchStatus.processing_status}, completed: ${batchStatus.request_counts.succeeded}`);
}
// Get results
for await (const result of await client.messages.batches.results(batch.id)) {
if (result.result.type === "succeeded") {
console.log(`${result.custom_id}: ${result.result.message.content[0].text}`);
}
}
Error Handling
async function robustComplete(messages: Anthropic.MessageParam[]): Promise<string> {
for (let attempt = 0; attempt < 4; attempt++) {
try {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
messages,
});
return response.content[0].text;
} catch (err) {
if (err instanceof Anthropic.RateLimitError) {
const waitMs = parseInt(err.headers?.["retry-after"] ?? "60") * 1000;
await sleep(waitMs);
continue;
}
if (err instanceof Anthropic.APIConnectionError) {
await sleep(2 ** attempt * 1000 + Math.random() * 1000);
continue;
}
throw err; // non-retriable (400, auth errors, etc.)
}
}
throw new Error("Max retries exceeded");
}
Production Checklist
// Every production Claude integration should have:
// 1. Pinned model version (never use floating aliases)
const model = "claude-sonnet-4-6-20251120"; // pin the date suffix
// 2. Prompt caching for large stable context
system: [{ type: "text", text: SYSTEM, cache_control: { type: "ephemeral" } }]
// 3. Cost tracking
logLLMCall(model, response.usage);
// 4. Error handling with retry
// (use the robustComplete pattern above)
// 5. Structured output for machine-readable responses
// (use tool_choice force pattern above)
// 6. Streaming for user-facing responses
// (never make users wait for the full response)
FAQ
Which Claude version should I use in 2026? Claude Sonnet 4.6 for most production workloads. Haiku 4.5 for high-volume/low-complexity. Opus 4.8 for tasks where quality justifies 5× cost.
How do I migrate from OpenAI to Claude?
The Anthropic SDK has a different interface (not OpenAI-compatible). Use the AI SDK (@ai-sdk/anthropic) for framework-agnostic code, or write a thin adapter. The message format is similar; the main difference is system prompts are top-level, not a system role in messages.
Is there a rate limit tier above the default? Yes — Anthropic offers increased limits for growing teams. Contact [email protected] with your use case and expected volume.
How do I handle context length overflow?
Check response.stop_reason === "max_tokens" and implement context compaction. See Context Compaction for Agents and Context Window Management.
What's the best way to reduce Claude API costs? Priority order: (1) Model routing (use Haiku for simple tasks), (2) Prompt caching (large stable contexts), (3) Semantic caching (skip LLM for similar queries), (4) Batch API (50% off for async work). See Prompt Caching Mastery and Model Routing.