[SL_CORE]
< CD ..||7 min read

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][anthropic][api][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.