mrkeyoor.com_
Wed 05 Aug 05:07 UTC
npmAI / MLupdated 05 Aug 2026

@anthropic-ai/sdk

Official TypeScript/JavaScript client for the Claude API. Wraps the Messages endpoint with typed requests and responses, streaming helpers, automatic retries with backoff, and typed error classes per HTTP status. Runs on Node 20+, Deno, Bun, Cloudflare Workers, and Vercel Edge; browser use is disabled by default so you cannot accidentally ship your API key to clients. It is a 0.x package that tracks a fast-moving API, so minor releases can carry breaking changes.

Verdict

The correct way to call Claude from TypeScript: well built, typed end to end, and released in lockstep with the API. Treat it like the 0.x package it is: pin versions and expect steady churn.

API stability3/5Well-designed surface, but pre-1.0 at 0.115.0: breaking changes land in minor releases and deprecated parameters get removed on short cycles
Docs4/5The repo README is deliberately thin; the real docs on platform.claude.com are thorough, with per-feature examples, streaming and tool-use guides, and migration notes
Maintenance5/5Generated from the API spec and released alongside API changes; multiple releases per month and pushes on the day of writing
Ecosystem4/530M weekly downloads and first-party status; a smaller third-party plugin scene than the openai client, but multi-provider layers like the ai package support it

Use it if

  • You call the Claude API from server-side TypeScript or JavaScript and want typed params instead of hand-rolled fetch calls
  • You need streaming with real helpers: client.messages.stream() plus finalMessage() beats parsing SSE yourself
  • You want tool use, structured outputs validated against Zod schemas, and token counting handled by the library
  • You deploy to edge runtimes (Cloudflare Workers, Vercel Edge) where many HTTP clients break
Skip it if

Setup reality

npm install @anthropic-ai/sdk, set ANTHROPIC_API_KEY, and new Anthropic() picks it up automatically. No peer dependencies; TypeScript 4.9+ and Node 20+ required. The friction is churn, not setup: the package is 0.x and tracks the API closely, so model ids, beta feature flags, and parameter shapes move between minor versions. Pin your version. Also note Jest only works with the node test environment (jsdom is unsupported), and the client timeout option is in milliseconds, unlike the Python SDK's seconds.

Patterns

Send a basic messagecreate-message

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

const client = new Anthropic(); // reads ANTHROPIC_API_KEY

const msg = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Hello, Claude' }],
});

for (const block of msg.content) {
  if (block.type === 'text') console.log(block.text);
}

content is an array of typed blocks; narrow on block.type before reading .text or TypeScript will error.

Set a system promptsystem-prompt

const msg = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 1024,
  system: 'You are a terse SQL expert. Answer with queries only.',
  messages: [{ role: 'user', content: 'Top 5 customers by revenue' }],
});

system is a top-level parameter, not a message with role system at index 0.

Stream a response token by tokenstream-response

const stream = client.messages.stream({
  model: 'claude-opus-5',
  max_tokens: 64000,
  messages: [{ role: 'user', content: 'Write a long report' }],
});

stream.on('text', (delta) => process.stdout.write(delta));

const final = await stream.finalMessage();
console.log('\ntokens:', final.usage.output_tokens);

Use finalMessage() for the complete message; large max_tokens values require streaming to avoid HTTP timeouts.

Let Claude call your function (tool runner)tool-use

import { betaZodTool } from '@anthropic-ai/sdk/helpers/beta/zod';
import { z } from 'zod';

const getWeather = betaZodTool({
  name: 'get_weather',
  description: 'Get current weather for a location',
  inputSchema: z.object({ location: z.string() }),
  run: async ({ location }) => `22C and clear in ${location}`,
});

const final = await client.beta.messages.toolRunner({
  model: 'claude-opus-5',
  max_tokens: 1024,
  tools: [getWeather],
  messages: [{ role: 'user', content: 'Weather in Paris?' }],
});

The tool runner (beta) drives the call-execute-loop cycle for you; drop to a manual stop_reason === 'tool_use' loop only when you need to own the whole loop.

Handle rate limits and API errors by typehandle-errors

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

try {
  await client.messages.create({ /* ... */ });
} catch (err) {
  if (err instanceof Anthropic.RateLimitError) {
    // 429: back off and retry
  } else if (err instanceof Anthropic.APIConnectionError) {
    // network failure before a response
  } else if (err instanceof Anthropic.APIError) {
    console.error(err.status, err.message);
  } else {
    throw err;
  }
}

Check APIConnectionError before APIError; in this SDK it is a subclass of APIError. The client already retries 429/5xx twice by default.

Count tokens before sendingcount-tokens

const count = await client.messages.countTokens({
  model: 'claude-opus-5',
  messages: [{ role: 'user', content: longDocument }],
});
console.log(count.input_tokens);

Counts are model-specific; never estimate Claude tokens with tiktoken, it is OpenAI's tokenizer and undercounts.

Get schema-validated JSON outputstructured-output

import { z } from 'zod';
import { zodOutputFormat } from '@anthropic-ai/sdk/helpers/zod';

const Contact = z.object({ name: z.string(), email: z.string() });

const res = await client.messages.parse({
  model: 'claude-opus-5',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Extract: Jane Doe, jane@co.com' }],
  output_config: { format: zodOutputFormat(Contact) },
});

console.log(res.parsed_output?.name);

parsed_output is null when parsing failed; also note the old top-level output_format parameter is deprecated in favor of output_config.format.

Send an image for analysisimage-input

import fs from 'fs';

const data = fs.readFileSync('chart.png').toString('base64');

const msg = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 1024,
  messages: [{
    role: 'user',
    content: [
      { type: 'image', source: { type: 'base64', media_type: 'image/png', data } },
      { type: 'text', text: 'What trend does this chart show?' },
    ],
  }],
});

URL sources also work: { type: 'url', url: '...' }. Put images before the text block that asks about them.

Cache a large stable prompt prefixprompt-caching

const msg = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 1024,
  system: [{
    type: 'text',
    text: bigStableInstructions,
    cache_control: { type: 'ephemeral' },
  }],
  messages: [{ role: 'user', content: userQuestion }],
});
console.log(msg.usage.cache_read_input_tokens);

Caching is a byte-exact prefix match: a timestamp or random id anywhere in the prefix silently kills every cache hit.

Tune timeout and retriesconfigure-client

const client = new Anthropic({
  timeout: 60_000, // milliseconds in the TS SDK
  maxRetries: 3,   // default is 2 (retries 408/429/5xx)
});

// per-request override
await client.messages.create({ /* ... */ }, { timeout: 5_000 });

Timeouts are retried too, so worst-case wall clock is timeout times (maxRetries + 1).

Alternatives

PackageRegistryPick it when
openainpmYour app targets OpenAI models; the client design is nearly identical
ainpmYou want one interface over several model providers plus framework streaming hooks
anthropicPyPISame official client for the same API, from Python