// LOADING
// LOADING
// LOADING_ARTICLE
Adding an AI feature to a web application used to mean training a model or stitching together a pile of ML infrastructure. Today it means making an API call. The Claude API exposes capable language models behind a clean HTTP interface, and the official Anthropic SDK makes integration from a Next.js app straightforward.
This post takes you from zero to a working AI feature: installed SDK, first API call, streaming response to the browser, and a few production considerations.
The official package is @anthropic-ai/sdk.
bashnpm install @anthropic-ai/sdk
Store your API key as a server-side environment variable. For Next.js, that means a name without the NEXT_PUBLIC_ prefix so it is never shipped to the browser. See Environment Variables and Secrets Management in Next.js for the full pattern.
# .env.local
ANTHROPIC_API_KEY=sk-ant-...
Create a Route Handler in Next.js (app/api/summarize/route.ts) to keep your API key server-side.
tsimport Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic(); // reads ANTHROPIC_API_KEY from env export async function POST(req: Request) { const { text } = await req.json(); const message = await client.messages.create({ model: "claude-opus-4-8", max_tokens: 1024, messages: [ { role: "user", content: `Summarize the following text in three bullet points:\n\n${text}`, }, ], }); const summary = message.content[0]; if (summary.type !== "text") { return Response.json({ error: "Unexpected response type" }, { status: 500 }); } return Response.json({ summary: summary.text }); }
The messages array is the core of the API. Each entry has a role ("user" or "assistant") and content. The model sees this array as the conversation so far and appends its response.
Claude comes in a tiered family. At the time of writing:
claude-opus-4-8): The default workhorse. Strong reasoning, long context, suited for complex generation tasks.claude-sonnet-4-6): A balanced middle tier—faster and cheaper than Opus while retaining strong capability.claude-haiku-4-5): The fastest and most economical tier, ideal for classification, short generations, and latency-sensitive paths.claude-fable-5): The highest-capability model, suited for the most demanding reasoning and generation tasks.For most user-facing features, start with claude-opus-4-8. Downgrade to Sonnet or Haiku for paths where throughput and cost matter more than maximum quality—autocomplete suggestions, labeling, quick classifications.
A system prompt sets the model's persona, constraints, and output format before the user's message. It is the primary lever for making outputs consistent.
tsconst message = await client.messages.create({ model: "claude-opus-4-8", max_tokens: 1024, system: "You are a concise technical writing assistant. " + "Respond only with the requested output—no preamble, no sign-off.", messages: [ { role: "user", content: `Summarize:\n\n${text}` }, ], });
For more on structuring prompts reliably, see Prompt Engineering for Developers.
For longer generations, streaming lets you display tokens as they arrive instead of waiting for the full response. This dramatically improves perceived latency.
tsexport async function POST(req: Request) { const { text } = await req.json(); const stream = client.messages.stream({ model: "claude-opus-4-8", max_tokens: 1024, messages: [{ role: "user", content: `Summarize:\n\n${text}` }], }); // Return a ReadableStream that forwards SSE chunks const readable = new ReadableStream({ async start(controller) { for await (const event of stream) { if ( event.type === "content_block_delta" && event.delta.type === "text_delta" ) { controller.enqueue( new TextEncoder().encode(`data: ${JSON.stringify({ text: event.delta.text })}\n\n`) ); } } controller.close(); }, }); return new Response(readable, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", }, }); }
For a full streaming walkthrough including the React client side, see Streaming LLM Responses in a Next.js App.
The SDK throws typed errors you can catch and handle cleanly.
tsimport Anthropic from "@anthropic-ai/sdk"; try { const message = await client.messages.create({ ... }); } catch (err) { if (err instanceof Anthropic.APIError) { console.error("Claude API error", err.status, err.message); // err.status is the HTTP status code (429, 500, etc.) } throw err; }
Common errors to handle explicitly: 429 (rate limit—implement exponential backoff), 400 (invalid request—usually a prompt or parameter issue), 529 (overloaded—retry with backoff).
Because your server proxies calls to Claude on behalf of users, you should rate-limit your own API endpoint to prevent a single user from exhausting your API quota. See Rate Limiting Your Next.js APIs for the implementation.
Once you have a basic call working, the natural extensions are:
The Claude API reduces integrating a capable LLM to a few dozen lines of TypeScript. The SDK handles auth, retries, and streaming; your job is structuring prompts clearly, handling errors gracefully, and rate-limiting your own endpoints. Start with a single, narrow feature—a summarizer, a classifier, an autocomplete—validate that it works well, then expand. Check out the projects page to see AI features built on this foundation.