Your First Claude API Call in TypeScript and Node.js

A
Author
22 Oct, 2026 4 min read 0 views

In Node.js you call Claude with the @anthropic-ai/sdk package. Create a client, call client.messages.create() with a model, max_tokens and messages, and read the text from the content blocks. The SDK is fully typed, so your editor will guide you.

Set up the project

mkdir claude-ts && cd claude-ts
npm init -y
npm install @anthropic-ai/sdk
npm install -D typescript tsx @types/node
export ANTHROPIC_API_KEY="your-key-here"

Set "type": "module" in package.json so you can use ES module imports. The tsx tool lets you run TypeScript files directly without a build step.

Make the call

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

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

const response = await client.messages.create({
  model: "claude-sonnet-5-5",
  max_tokens: 1000,
  messages: [
    { role: "user", content: "Explain the JavaScript event loop in three sentences." },
  ],
});

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

console.log("stop reason:", response.stop_reason);
console.log("usage:", response.usage.input_tokens, response.usage.output_tokens);

Save this as first.ts and run npx tsx first.ts. You will see the explanation, then stop reason: end_turn, then the token counts.

Why you need the type check

response.content is a union of block types: text, thinking, tool use and others. TypeScript will not let you read .text until you narrow the type with block.type === "text". That check looks fussy at first, but it prevents runtime errors when a response contains something other than plain text.

Use the SDK types

Do not invent your own message interface. The SDK exports the types for every API object, and reusing them keeps your code in step with the API.

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

const history: Anthropic.MessageParam[] = [
  { role: "user", content: "What is a closure?" },
];

function textOf(message: Anthropic.Message): string {
  return message.content
    .filter((b): b is Anthropic.TextBlock => b.type === "text")
    .map((b) => b.text)
    .join("");
}

Handle errors by class

Failures throw subclasses of Anthropic.APIError. Check the most specific class first.

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

const client = new Anthropic();

try {
  await client.messages.create({
    model: "claude-haiku-5-5",
    max_tokens: 300,
    messages: [{ role: "user", content: "Hi" }],
  });
} catch (error) {
  if (error instanceof Anthropic.AuthenticationError) {
    console.error("Invalid API key");
  } else if (error instanceof Anthropic.RateLimitError) {
    console.error("Rate limited, retry later");
  } else if (error instanceof Anthropic.APIError) {
    console.error("API error", error.status, error.message);
  } else {
    throw error;
  }
}

Client options you will use soon

The client constructor accepts options for retries and timeouts. By default the SDK retries certain failures twice, such as rate limits, server errors and dropped connections, and it allows long requests before timing out. In TypeScript the timeout is measured in milliseconds, so a value of 30000 means thirty seconds. You can also override options for a single call with a second argument to create.

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

const client = new Anthropic({
  maxRetries: 3,
  timeout: 60_000, // milliseconds
});

const response = await client.messages.create(
  {
    model: "claude-haiku-5-5",
    max_tokens: 200,
    messages: [{ role: "user", content: "Say hello in French." }],
  },
  { timeout: 10_000 }, // this call only
);

Running this prints nothing by itself; the point is that the per-call timeout of ten seconds overrides the client default of sixty. If the API takes longer than ten seconds to answer, the SDK aborts and retries according to maxRetries, then throws a timeout error.

Module settings that trip people up

Top-level await only works in ES modules. If you see a syntax error about await, set "type": "module" in package.json, or rename the file to .mts, or wrap your code in async function main() { ... } and call main(). If you prefer CommonJS, the same package works with require, though you will need the default export, usually reached as require("@anthropic-ai/sdk"). Pick one module style per project and keep it consistent.

For TypeScript projects, a modern tsconfig.json with "strict": true gives you the full benefit of the SDK types. Strict mode will warn you about the exact mistakes that cause runtime surprises, like reading a text field on the wrong block type.

Never ship the key to the browser

Run this code on a server. If you call Claude from frontend code, anyone can open developer tools, copy your key and spend your money. Put a small backend between your UI and the API, and apply your own authentication and rate limits there.

Common mistakes

  • Forgetting max_tokens, which is required and causes a 400 error.
  • Using top-level await in a CommonJS file. Switch to ES modules or wrap the code in an async function.
  • Reading content[0].text without narrowing the type.
  • Hard-coding the key instead of using the environment.
Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.