← Blog

Claude API JavaScript Example: Requests, Streaming & Tools

2026-08-31 · 4 min read · SubToAPI Team

If you're looking for a Claude API JavaScript example, you want working code you can copy, adapt, and run — not another conceptual overview. This article gives you complete, runnable examples: a basic chat request, streaming output, tool use, and error handling, all in plain JavaScript using fetch.

Every example below works in Node.js 18+ (native fetch) or in a browser context where CORS allows it. We'll show the direct Anthropic API pattern first, then the equivalent using SubToAPI, which wraps Claude behind a standard HTTPS API with application keys — useful if you're building a product on top of Claude rather than just calling it from a script.

Basic Chat Request

The simplest Claude API call in JavaScript sends a messages array and reads back the response text.

const response = await fetch("https://api.anthropic.com/v1/messages", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.ANTHROPIC_API_KEY,
    "anthropic-version": "2023-06-01"
  },
  body: JSON.stringify({
    model: "claude-3-5-sonnet-20241022",
    max_tokens: 1024,
    messages: [
      { role: "user", content: "Explain event loops in one paragraph." }
    ]
  })
});

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

Key things to note:

If you're calling Claude through SubToAPI instead, the shape is nearly identical — you swap the endpoint and auth header:

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

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

Full request/response schema is in the docs.

Streaming Responses in JavaScript

For chat UIs or CLI tools, you usually don't want to wait for the full response — you want tokens as they're generated. Claude supports server-sent events for this, and Node's fetch gives you a readable stream you can iterate over.

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

const reader = response.body.getReader();
const decoder = new TextDecoder();

let buffer = "";
while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split("\n");
  buffer = lines.pop();

  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;
    const payload = line.slice(6);
    if (payload === "[DONE]") continue;

    const event = JSON.parse(payload);
    if (event.type === "content_block_delta") {
      process.stdout.write(event.delta.text);
    }
  }
}

This manual SSE parsing pattern applies whether you're calling Anthropic directly or through SubToAPI — the event format is the same. See /docs/streaming for the full list of event types (message_start, content_block_delta, message_stop, etc.) if you need to handle more than plain text deltas.

Tool Use (Function Calling) Example

Claude can call functions you define, returning structured input for you to execute. Here's a minimal weather-lookup example.

const tools = [
  {
    name: "get_weather",
    description: "Get current weather for a city",
    input_schema: {
      type: "object",
      properties: {
        city: { type: "string", description: "City name" }
      },
      required: ["city"]
    }
  }
];

const response = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`
  },
  body: JSON.stringify({
    model: "claude-3-5-sonnet-20241022",
    max_tokens: 1024,
    tools,
    messages: [
      { role: "user", content: "What's the weather in Lisbon?" }
    ]
  })
});

const data = await response.json();
const toolCall = data.content.find(block => block.type === "tool_use");

if (toolCall) {
  console.log(toolCall.name, toolCall.input);
  // { city: "Lisbon" } — now call your actual weather API here
}

After you run the tool, you send the result back in a follow-up message with a tool_result content block so Claude can produce a final answer. Details on multi-turn tool loops are in /docs/tools.

Error Handling You Actually Need

Production code needs to handle rate limits, invalid requests, and network failures distinctly:

async function callClaude(payload) {
  const response = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`
    },
    body: JSON.stringify(payload)
  });

  if (response.status === 429) {
    throw new Error("Rate limited — back off and retry");
  }
  if (!response.ok) {
    const err = await response.json();
    throw new Error(`API error ${response.status}: ${err.error?.message}`);
  }

  return response.json();
}

Wrap calls in retry logic with exponential backoff for 429s and 5xx responses — Claude's API (and SubToAPI's) can return transient errors under load.

Why Route JavaScript Calls Through SubToAPI

If you're calling Claude from a single script, the direct Anthropic SDK or raw fetch is fine. Where it gets harder is when multiple services, team members, or environments need access: rotating a raw API key touches every deployment, and there's no per-app usage breakdown.

SubToAPI issues scoped sub_live_... keys per application, so you can revoke one integration without breaking others, see usage per key, and manage team seats from one dashboard. The request/response format matches what's shown above — no rewrite needed. Plans start at €9/month (Solo), with Team at €19/seat and Scale at €49/seat, and there's a free trial at /signup. Full setup is in /docs/quickstart, and /pricing has plan details.

questions

Do I need Anthropic's official SDK to call Claude from JavaScript? No. The SDK is convenient but the API is plain HTTPS/JSON — fetch works fine in Node.js or the browser, as shown above.

Why does my streaming response show garbled or partial JSON? You're likely not buffering across chunk boundaries. SSE events can split across fetch reads, so accumulate text and only parse complete lines ending in \n.

Can I use these same examples with SubToAPI instead of Anthropic directly? Yes — the request and response bodies are identical. You only change the base URL to api.subtoapi.app/v1 and the auth header to your sub_live_... key.

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 →