Skip to content
AI Full-Stack Wiki

Claude API Cheat Sheet for TypeScript Developers

A one-page reference for the Messages API in TypeScript: request parameters, response fields, stop reasons, streaming events, errors, and the SDK helpers you use daily.

Difficulty: Beginner10 min readUpdated
On this page· One request

Why this matters

You will look these things up constantly: which parameter controls what, what each stop_reason means, which errors to retry. This page collects them, with links to the articles that explain each one. Model names, limits, and features change; check the official documentation for current details before relying on a value.

One request#

One request to the Messages APIYour server sends a POST to the Messages API with the model, an output limit, a system prompt, and messages; the model generates; the response carries content blocks, the stop reason, and usage.Your serverMessages APIbuild request: model,max_tokens, system,messagesPOST /v1/messagesgenerate tokens until astop condition{ content: [blocks],stop_reason, usage }check stop_reason, readtext blocks, log usage

Step 1 of 5

build request: model, max_tokens, system, messages

Everything the model needs for this task goes into this one request. The API key is added by the SDK from the environment.

Anatomy of one Messages API call: your Node.js code builds a request with model, max_tokens, system, and messages; the API returns content blocks, a stop_reason, and token usage.
Show text description

The life of a single request, step by step. Participants, left to right: Your server, Messages API.

  1. Your server → Your server: build request: model, max_tokens, system, messages · Everything the model needs for this task goes into this one request. The API key is added by the SDK from the environment.
  2. Your server → Messages API: POST /v1/messages · The SDK sends the request over HTTPS, retrying automatically on rate limits and transient errors.
  3. Messages API → Messages API: generate tokens until a stop condition · The model writes (and possibly reasons) until it finishes, hits max_tokens, or wants a tool.
  4. Messages API → Your server: { content: [blocks], stop_reason, usage } · content is a list of typed blocks (text, thinking, tool_use); stop_reason says why generation ended; usage reports input and output tokens.
  5. Your server → Your server: check stop_reason, read text blocks, log usage · Branch on stop_reason before trusting the text, and record usage for cost tracking.
minimal.ts
import Anthropic from "@anthropic-ai/sdk";
 
const client = new Anthropic(); // reads ANTHROPIC_API_KEY
 
const message = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 4096,
  system: "You are a concise assistant.",
  messages: [{ role: "user", content: "Explain idempotency in one paragraph." }],
});
console.log(message.content.flatMap((b) => (b.type === "text" ? [b.text] : [])).join(""));

Request parameters#

ParameterPurposeNotes
modelWhich modelclaude-opus-5-5, claude-sonnet-5-5, claude-haiku-4-5 at the time of writing
max_tokensOutput limitRequired; reaching it gives stop_reason: "max_tokens"
messagesThe conversationAlternating user and assistant, starting with user; system messages allowed mid-conversation on current models
systemInstructionsString or text blocks (blocks can carry cache_control)
tools, tool_choiceTool useauto (default) or none; forced tool choice is not supported on the newest models
output_config.formatStructured outputsUse messages.parse with zodOutputFormat
output_config.effortThoroughness vs. speed and tokenslow, medium, high (and higher on some models)
thinkingExtended reasoning{ type: "adaptive" } on current models
cache_controlPrompt cachingTop-level for automatic caching, or on blocks as breakpoints
streamStreamingPrefer client.messages.stream() in the SDK

temperature, top_p, and top_k are not accepted by the newest models; steer behavior with instructions, examples, and effort.

Response fields#

FieldMeaning
contentArray of blocks: text, tool_use, thinking, server tool results
stop_reasonWhy generation stopped (below)
usageinput_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens
id, modelMessage ID and the model that answered
_request_idRequest ID for support (also error.requestID on errors)

Stop reasons#

Handling every stop_reasonEach response ends with a stop reason. end_turn means show the answer; max_tokens means the output was cut off; tool_use means run the requested tools; refusal means show a safe fallback; pause_turn means continue the turn.Responsestop_reasonend_turnuse the answermax_tokenscut off: retry or continuetool_userun tools, send resultsrefusalsafe fallback messagepause_turnsend it back to continue

Hover or tap a component to see what it does.

Handling stop_reason in the Messages API: end_turn, max_tokens, tool_use, refusal, and pause_turn each need different code paths.
Show text description

Code that only reads the text of a response treats a truncated answer, a refusal, and a request for a tool as if they were complete answers. Branch on stop_reason first.

  • Response: Every Messages API response says why generation stopped.
  • end_turn: The model finished naturally. Read the text blocks.
  • max_tokens: The answer hit your output limit and is incomplete. Raise max_tokens, ask for shorter output, or continue the response.
  • tool_use: The model is waiting for your code to execute one or more tools and return tool_result blocks.
  • refusal: The request was declined. Do not show partial output as an answer; show a clear message instead.
  • pause_turn: A long server-side tool turn paused. Append the response and call again to continue.
stop_reasonMeaningWhat to do
end_turnFinished naturallyUse the answer
tool_useWants tools runRun them, send tool_result blocks in one user message
max_tokensHit the output limitRaise max_tokens, ask for shorter output, or continue
stop_sequenceHit a custom stop sequenceExpected if you set one
pause_turnServer tools paused a long turnSend the conversation back to continue
refusalDeclined for safety reasonsShow a helpful message; do not retry blindly
model_context_window_exceededRan out of contextTrim history, compact, or retrieve less

The same table as code, so every caller handles every case:

lib/next-action.ts
import type Anthropic from "@anthropic-ai/sdk";
 
export type NextAction =
  | { kind: "answer"; text: string }
  | { kind: "run_tools"; calls: Anthropic.ToolUseBlock[] }
  | { kind: "continue" }
  | { kind: "truncated"; partial: string }
  | { kind: "refused" }
  | { kind: "context_full" };
 
const textOf = (m: Anthropic.Message) => m.content.flatMap((b) => (b.type === "text" ? [b.text] : [])).join("");
 
export function nextAction(message: Anthropic.Message): NextAction {
  switch (message.stop_reason) {
    case "tool_use":
      return { kind: "run_tools", calls: message.content.filter((b): b is Anthropic.ToolUseBlock => b.type === "tool_use") };
    case "pause_turn":
      return { kind: "continue" };
    case "max_tokens":
      return { kind: "truncated", partial: textOf(message) };
    case "refusal":
      return { kind: "refused" };
    case "model_context_window_exceeded":
      return { kind: "context_full" };
    case "end_turn":
    case "stop_sequence":
    case null:
      return { kind: "answer", text: textOf(message) };
    default: {
      // A new stop reason added in a future API version fails the type check here.
      const unknown: never = message.stop_reason;
      throw new Error(`Unhandled stop_reason: ${String(unknown)}`);
    }
  }
}
next-action.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import type Anthropic from "@anthropic-ai/sdk";
import { nextAction } from "./lib/next-action.ts";
 
const msg = (stop_reason: Anthropic.StopReason, content: unknown[]) => ({ stop_reason, content }) as unknown as Anthropic.Message;
 
test("maps stop reasons to actions", () => {
  assert.deepEqual(nextAction(msg("end_turn", [{ type: "text", text: "Hi" }])), { kind: "answer", text: "Hi" });
  assert.equal(nextAction(msg("tool_use", [{ type: "tool_use", id: "t", name: "x", input: {} }])).kind, "run_tools");
  assert.deepEqual(nextAction(msg("max_tokens", [{ type: "text", text: "Part" }])), { kind: "truncated", partial: "Part" });
  assert.equal(nextAction(msg("pause_turn", [])).kind, "continue");
  assert.equal(nextAction(msg("refusal", [])).kind, "refused");
});

Streaming events#

EventCarries
message_startMessage ID, model, initial usage
content_block_startA new block (text, tool use, thinking)
content_block_deltatext_delta, input_json_delta (tool input), thinking_delta, citations_delta
content_block_stopBlock finished
message_deltaFinal stop_reason and output usage
message_stopDone

With the SDK: stream.on("text", …) for text, await stream.finalMessage() for the complete message. See streaming to React.

Errors#

StatusTypeRetry?
400invalid_request_errorNo: fix the request
401 / 403authentication_error / permission_errorNo: check keys and access
404not_found_errorNo: check the model name or ID
413Request too largeNo: send less
429rate_limit_errorYes, after retry-after
500api_errorYes
529overloaded_errorYes, with backoff, then fall back

The SDK retries retryable errors twice by default and throws typed errors (Anthropic.RateLimitError, Anthropic.BadRequestError, …). See reliability.

SDK helpers#

HelperUse it for
client.messages.parse + zodOutputFormatTyped structured outputs
client.messages.streamStreaming with events and a final message
client.messages.countTokensExact input token counts before sending
client.messages.batchesHalf-price asynchronous processing
client.beta.messages.toolRunner + betaZodToolThe tool loop, run for you
mcpTools (helpers/beta/mcp)MCP server tools in the tool runner
client.models.list()Models available to your key

Common mistakes#

  • Ignoring stop_reason. Truncated and refused answers look like normal text if you only read content.
  • Reading only the first content block. Responses can contain several blocks of different types.
  • Retrying 400 errors. They fail the same way every time.
  • Hard-coding model names everywhere. Keep them in one config module.

Try it

Handle every stop reason

  1. Use nextAction in one of your scripts and handle each kind explicitly.
  2. Force max_tokens with max_tokens: 20 and check the truncated path.
  3. Log _request_id for every call and find one request in your logs by its ID.

Check your understanding

The response has stop_reason 'tool_use'. What do you send next?
Which error is worth retrying?
Why use a `never` check in the stop_reason switch?

Key takeaways

  • Always branch on stop_reason, and handle every value.
  • Read all content blocks, record usage and request IDs, and keep model names in one place.
  • Retry 429, 5xx, and 529; fix 4xx.
  • Use the SDK helpers: parse, stream, countTokens, batches, and the tool runner.

Next steps#

Finished this article? Track it to see your progress on the roadmap.