ASK KNOX
beta
LESSON 566

Meet the Claude API

One endpoint, three models, and the mental model that makes the rest of this track click.

6 min read·Building with Claude

The Track You Were Missing

You have been prompting Claude in Claude.ai, in Claude Code, maybe through the academy's own AI Tutor. You have built intuitions about what Claude can do. Now you build ON it. This track is the shift from user to engineer: instead of sending prompts through someone else's product, you call the API directly and own the integration.

The payoff is control. When you call the Messages API yourself, you decide when to send, what history to include, which model to route to, whether to use tools, how to handle errors, and how to track cost. That control is what separates a polished production integration from a weekend demo.

This first lesson covers the mental model that makes the rest of the track load correctly. Everything else — tool use, structured outputs, caching, streaming — is a feature of one endpoint. Get the foundation right and the rest follows naturally.

One Endpoint

Here is the mental model: there is one endpoint.

POST /v1/messages

Tools? A feature of it. Structured output? A feature of it. Prompt caching? A feature of it. Adaptive thinking? A feature of it. Streaming? A mode of it. This is not like some APIs where each capability lives at a different URL with its own conventions. Everything in this track happens on /v1/messages, and everything you learn compounds on the same base.

The academy's AI Tutor is built on this endpoint in production. When you ask it a question, it calls client.messages.create with a system prompt that carries the lesson context, sends your question as a user message, and streams back the response. Same endpoint, same pattern — just with more context.

The Three-Model Family

Three models. One deliberate capability/cost ladder. The model IDs in the matrix are aliases — the stable identifiers you use in production. Notice what is NOT shown: date suffixes. There is no claude-sonnet-4.6 (dot) or claude-sonnet-4-6-20250514. Those either 404 or are deprecated. Use the alias IDs exactly as shown.

Two things about the matrix worth internalizing beyond the numbers:

First, Opus 4.8 has no sampling parameters. Temperature, top_p, top_k — all return 400 on Opus 4.8 and 4.7. This is intentional. Anthropic removed them in favor of steering via prompting. If your integration sends temperature: 0.7 to Opus 4.8, it fails. This catches developers who copy a config from an older integration.

Second, the Models API is the live source of truth. You can query GET /v1/models or client.models.retrieve("claude-sonnet-4-6") to get the current context window, max output tokens, and supported feature flags for any model. Don't hardcode capability beliefs — models change. The Foresight trading system queries model capabilities at startup so its routing logic never lags a model update.

Your First Request

Install the SDK and initialize the client:

import Anthropic from "@anthropic-ai/sdk"

const client = new Anthropic()
// reads ANTHROPIC_API_KEY from process.env automatically

The client reads process.env.ANTHROPIC_API_KEY with no configuration needed. Never pass the key as a string literal — that is how keys end up in git history.

A minimal request:

const response = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Explain the Messages API in one sentence." }
  ],
})

Three required fields: model, max_tokens, messages. That is the floor. Everything else — system, tools, tool_choice, stream — is optional.

Reading the Response

The anatomy diagram is the reading contract. Two things require special attention.

response.content is an array of typed blocks, not a string. A typical response contains one text block, but tool-use responses contain tool_use blocks, and future model features may add other types. Always narrow before reading:

const textContent = response.content
  .filter(block => block.type === "text")
  .map(block => block.text)
  .join("")

response.stop_reason is your control flow signal, not an afterthought. Before you read content, check why the model stopped:

if (response.stop_reason === "max_tokens") {
  // Response was truncated — raise max_tokens or stream
  throw new Error(`Response truncated at max_tokens: ${response.usage.output_tokens}`)
}
if (response.stop_reason === "tool_use") {
  // Tool calls to handle — don't read content as text
  handleToolCalls(response.content)
  return
}
// stop_reason === "end_turn" — normal completion
const text = response.content
  .filter(b => b.type === "text")
  .map(b => b.text)
  .join("")

A production integration switches on every stop reason. We will cover all seven in the next lesson.

The system Param

One common confusion: system is a top-level param, not a message with role: "system".

// CORRECT
await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  system: "You are a concise technical writer. Always use TypeScript examples.",
  messages: [{ role: "user", content: "Explain tool use." }],
})

// INCORRECT — system as a message role does not exist on this API
await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    { role: "system", content: "..." },  // ← this will error
    { role: "user", content: "Explain tool use." },
  ],
})

This distinction matters for caching (the prompt-caching lesson), where placing a cache_control breakpoint on the system param caches your entire system prompt. The system prompt is the constitution for the conversation — it applies to every turn and stays outside the messages array by design.

Tracking Cost

Every response carries response.usage:

console.log(response.usage)
// { input_tokens: 124, output_tokens: 87 }

At $3.00 / 1M input tokens and $15.00 / 1M output tokens for Sonnet 4.6, that request cost about $0.0004 + $0.0013 = $0.0017. For a production feature doing 10,000 requests per day, that math determines whether the feature is viable. Log response.usage from the start — retrofitting cost tracking is always harder than building it in.

The usage object gains more fields as you add features: cache_read_input_tokens appears when you add prompt caching, letting you see exactly how many tokens came from cache versus fresh input. Every field tells a story about your system's economics.

The Mental Model, Complete

One endpoint. Three models on a deliberate cost-capability ladder. Required fields: model, max_tokens, messages. System prompt is top-level. Response content is a typed array — narrow before reading. Stop reason is your control flow. Everything else in this track — tools, structured outputs, caching, thinking, streaming — is a feature of this foundation.

The next lesson covers the conversation model: how statelessness works, why your code owns history, and what every stop reason means for your application logic.