M
MeshWorld.
AI Claude API Anthropic Tutorial Node.js 5 min read

How to Add Claude to Your App Using the Anthropic API

By Vishnu Damwala

The Anthropic API gives you direct access to Claude’s models. Whether you want to add a chat interface, automate document processing, or build a fully agentic workflow, this is where you start.

This guide uses Node.js/TypeScript, but the SDK is available for Python too — the patterns are identical.

Install the SDK

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

Set your API key as an environment variable:

export ANTHROPIC_API_KEY="your-key-here"

Get a key from the Anthropic Console.

Your first call

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 REST APIs in two sentences.",
    },
  ],
});

console.log(message.content[0].text);

That is the whole thing. client.messages.create sends a message and returns Claude’s response.

Key parameters

  • model — which Claude model to use. Current options: claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5-20251001. Default to Sonnet for most tasks.
  • max_tokens — the maximum tokens in Claude’s response. Set this intentionally — lower values reduce cost, higher values allow longer outputs.
  • messages — the conversation history as an array of {role, content} objects.

Streaming responses

For chat UIs or anything where the user should see output as it generates, use streaming:

const stream = await client.messages.stream({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Write a short poem about debugging." }],
});

for await (const chunk of stream) {
  if (
    chunk.type === "content_block_delta" &&
    chunk.delta.type === "text_delta"
  ) {
    process.stdout.write(chunk.delta.text);
  }
}

In a web app, you would pipe these chunks to a Server-Sent Events (SSE) endpoint and push them to the client in real time.

Managing conversation history

Claude does not store conversation state between API calls. You manage the history yourself by passing the full message array each time:

const history: { role: "user" | "assistant"; content: string }[] = [];

async function chat(userMessage: string): Promise<string> {
  history.push({ role: "user", content: userMessage });

  const response = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: history,
  });

  const assistantMessage = response.content[0].text;
  history.push({ role: "assistant", content: assistantMessage });

  return assistantMessage;
}

// Usage
await chat("What is a closure in JavaScript?");
await chat("Show me a practical example."); // Claude remembers the previous Q&A

For production apps, store history in a database (keyed by session/user) instead of in-memory.

System prompts

The system parameter lets you set Claude’s behavior, persona, and constraints before the conversation starts:

const response = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  system:
    "You are a customer support agent for Meshworld. Only answer questions about our products. If asked about unrelated topics, politely redirect.",
  messages: [{ role: "user", content: "How do I reset my password?" }],
});

Keep system prompts specific. Vague instructions produce inconsistent behavior.

Common patterns

Summarization

async function summarize(text: string): Promise<string> {
  const response = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 512,
    messages: [
      {
        role: "user",
        content: `Summarize the following in 3 bullet points:\n\n${text}`,
      },
    ],
  });
  return response.content[0].text;
}

Structured data extraction

async function extractFields(
  email: string
): Promise<{ name: string; issue: string; urgency: string }> {
  const response = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 256,
    messages: [
      {
        role: "user",
        content: `Extract these fields from the email as JSON: name, issue, urgency (low/medium/high).\n\nEmail:\n${email}`,
      },
    ],
  });

  return JSON.parse(response.content[0].text);
}

For reliable JSON output, use the json_object response format or ask Claude to wrap its output in a code block and parse it.

Classification

async function classifyTicket(
  description: string
): Promise<"billing" | "technical" | "general"> {
  const response = await client.messages.create({
    model: "claude-haiku-4-5-20251001", // Use Haiku for fast, cheap classification
    max_tokens: 10,
    messages: [
      {
        role: "user",
        content: `Classify this support ticket as one word: billing, technical, or general.\n\n${description}`,
      },
    ],
  });

  return response.content[0].text.trim().toLowerCase() as any;
}

Use Haiku for high-volume, low-complexity tasks like classification. Use Sonnet for reasoning and generation. Use Opus for complex multi-step work.

Error handling

import Anthropic from "@anthropic-ai/sdk";

try {
  const response = await client.messages.create({ ... });
} catch (error) {
  if (error instanceof Anthropic.APIStatusError) {
    if (error.status === 429) {
      // Rate limited — back off and retry
    } else if (error.status === 400) {
      // Bad request — check your parameters
    }
  }
}

For production, implement exponential backoff for rate limit errors. The SDK does not retry automatically by default.

Token and cost management

Every call costs tokens. Check usage in the response:

const response = await client.messages.create({ ... });
console.log(response.usage); // { input_tokens: 150, output_tokens: 320 }

Tips to manage cost:

  • Use max_tokens conservatively — you pay for what Claude generates
  • Use Haiku for repetitive, simple tasks
  • Cache responses for identical inputs (the API supports prompt caching)
  • Keep conversation history trim — old messages still cost tokens

Next steps