← Blog

Claude API Node.js Integration Tutorial (2024 Guide)

2026-09-30 · 5 min read · SubToAPI Team

Claude API Node.js Integration Tutorial

If you're building a Node.js app and want to call Claude, you need three things: an API key, the official SDK (or plain fetch), and a request shaped correctly for the Messages API. This tutorial walks through all of it — from npm install to a working chat endpoint with streaming — using code you can copy directly into a project.

By the end you'll have a small Express route that accepts a user message, sends it to Claude, and returns a response, plus the streaming variant for real-time UIs. We'll also cover the two ways to authenticate: directly with Anthropic, or through a proxy like SubToAPI if you want a single HTTPS API key shared across a team instead of managing raw provider credentials.

Step 1: Install the SDK

Anthropic publishes an official Node.js SDK. Install it in your project:

npm install @anthropic-ai/sdk

If you'd rather avoid an SDK dependency, plain fetch works fine too since the API is just JSON over HTTPS — we'll show both.

Step 2: Set up your API key

Store your key as an environment variable, never hardcoded:

# .env
ANTHROPIC_API_KEY=sk-ant-...

Load it with dotenv or your framework's config system. If you're using SubToAPI instead of a raw Anthropic key, the variable would be SUBTOAPI_KEY=sub_live_... — same pattern, different provider.

Step 3: Send your first message

Here's a minimal script using the official SDK:

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

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

async function main() {
  const message = await anthropic.messages.create({
    model: "claude-sonnet-4-5",
    max_tokens: 1024,
    messages: [
      { role: "user", content: "Explain event loops in Node.js in two sentences." },
    ],
  });

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

main();

Run it with node index.js (make sure your package.json has "type": "module" or use require syntax instead). You should get a text response printed to your console within a second or two.

Step 4: Wire it into an Express route

Most integrations aren't standalone scripts — they're API endpoints. Here's a basic Express route:

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

const app = express();
app.use(express.json());

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

app.post("/api/chat", async (req, res) => {
  const { message } = req.body;

  try {
    const response = await anthropic.messages.create({
      model: "claude-sonnet-4-5",
      max_tokens: 1024,
      messages: [{ role: "user", content: message }],
    });

    res.json({ reply: response.content[0].text });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Claude request failed" });
  }
});

app.listen(3000, () => console.log("Server running on port 3000"));

This is the pattern most SaaS backends use: front end sends a message, backend calls Claude, backend returns text to the client. Keep the API key server-side — never call Claude directly from browser JavaScript, since your key would be exposed to anyone who opens devtools.

Step 5: Add streaming for real-time output

For chat UIs, streaming tokens as they arrive feels far more responsive than waiting for the full response. The SDK supports this natively:

app.post("/api/chat/stream", async (req, res) => {
  const { message } = req.body;

  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  const stream = anthropic.messages.stream({
    model: "claude-sonnet-4-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: message }],
  });

  stream.on("text", (text) => {
    res.write(`data: ${JSON.stringify({ text })}\n\n`);
  });

  stream.on("end", () => {
    res.write("data: [DONE]\n\n");
    res.end();
  });

  stream.on("error", (err) => {
    console.error(err);
    res.end();
  });
});

On the client, consume this with EventSource or a fetch reader loop, appending each chunk to your UI as it arrives.

Step 6: Handling errors and rate limits properly

Production integrations need to handle a few predictable failure modes:

Wrap calls in a retry helper rather than letting a transient error crash a user-facing request:

async function callWithRetry(fn, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === retries - 1 || err.status < 500) throw err;
      await new Promise((r) => setTimeout(r, 500 * 2 ** i));
    }
  }
}

Alternative: skip provider management with SubToAPI

The steps above work identically whether you're calling Anthropic directly or routing through SubToAPI. The difference is what happens around the API call: SubToAPI turns your existing Claude access into a standard HTTPS API with sub_live_... application keys, so instead of juggling raw provider credentials per environment, you issue scoped keys per app or teammate from one dashboard.

The request shape is the same Messages API format, so your Node.js code barely changes:

const response = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "claude-sonnet-4-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello Claude" }],
  }),
});

const data = await response.json();
console.log(data.content[0].text);

This is useful if you have multiple developers or services that need Claude access but you don't want to distribute a single shared Anthropic key. See the quickstart and messages endpoint docs for the full request/response reference, and streaming docs if you're building the real-time variant. Plans start at €9/month with a free trial — check pricing for details.

questions

Do I need the official Anthropic SDK, or can I just use fetch? Either works. The SDK adds convenience methods for streaming and retries, but the API is plain JSON over HTTPS, so fetch or axios integrate just as easily if you want fewer dependencies.

How do I keep my Claude API key secure in a Node.js app? Store it in an environment variable, load it only on the server, and never send it to the browser. All Claude calls should go through your own backend route, not directly from client-side JavaScript.

Can I use this same code with SubToAPI instead of a direct Anthropic key? Yes — swap the base URL to https://api.subtoapi.app/v1/messages and the authorization header to your sub_live_... key. The request and response format matches the Messages API, so no other code changes are needed.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →