Your First Claude API Call in TypeScript and Node.js
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
awaitin a CommonJS file. Switch to ES modules or wrap the code in an async function. - Reading
content[0].textwithout narrowing the type. - Hard-coding the key instead of using the environment.
Comments
No comments yet. Have a question or a tip? Be the first to share it.