5.1 LLM & Technical Fundamentals: Tokens, Context Windows & Sampling
Key Takeaways
- A token averages about 4 characters or 0.75 English words, but code, JSON, non-Latin scripts, and long numbers are far denser, which is why /v1/messages/count_tokens exists rather than character estimation.
- Claude 4.7 and later models use a newer tokenizer producing roughly 30% more tokens for the same text than Claude Sonnet 4.6 and earlier.
- max_tokens is a ceiling rather than a reservation and is shared between thinking and response text; hitting it produces stop_reason max_tokens, which is truncation rather than completion.
- Reproducibility comes from pinning the model snapshot and holding the prompt byte-identical, not from setting temperature to 0, and temperature and top_p should never be tuned simultaneously.
- The Claude API is REST over HTTPS authenticated with an x-api-key header and a required anthropic-version header; streaming uses Server-Sent Events rather than WebSockets.
LLM & Technical Fundamentals: Tokens, Context Windows & Sampling
Exam Blueprint Focus: LLM Fundamentals (5.2%) and Technical Fundamentals (6.1%) are the two largest sub-skills in the 16.8% Model Selection and Optimisation domain — together they outweigh model selection and cost management combined. They test the layer below the API: what a token is, what a context window actually holds, what the sampling parameters do, and how the HTTP and SDK surface is shaped.
Tokens: The Unit You Are Billed In
A token is a sub-word fragment produced by a byte-pair-encoding (BPE) tokenizer. English text averages roughly 4 characters, or 0.75 words, per token, but that ratio is an average and not a rule:
| Content | Typical density | Why |
|---|---|---|
| Ordinary English prose | ~0.75 words/token | Common words are single tokens |
| Source code | Far more tokens per character | Punctuation, indentation, and camelCase identifiers fragment |
| JSON | Expensive | Every brace, quote, colon, and comma is its own token |
| Non-Latin scripts | Often 2-3x English | Less represented in the tokenizer's merge table |
| Long numbers and UUIDs | Very expensive | Split into small digit groups |
Two consequences engineers routinely get wrong:
- You cannot estimate tokens from characters reliably, which is why
/v1/messages/count_tokensexists. A 50,000-character English document and a 50,000-character JSON payload are not the same request. - Tokenizers change between model generations. Claude 4.7 and later models use a newer tokenizer that produces roughly 30% more tokens for the same text than the one used by Claude Sonnet 4.6 and earlier. A prompt that fit a budget on one generation may not on the next, even byte-for-byte unchanged.
Where tokens are counted
Every one of these consumes context and is billed:
- The
systemprompt, on every request. - The full
messagesarray — the API is stateless, so the entire history is re-sent and re-billed each turn. - The
toolsarray: names, descriptions, and JSON schemas. - An automatic tool-use system prompt the API injects whenever
toolsis present: 354 tokens on Claude Sonnet 5 and 286 on Claude Opus 5 withtool_choiceautoornone, rising to 474 and 406 withanyortool. thinkingblocks, billed as output tokens even when the text is not returned to you.
Context Windows: Capacity, Not Budget
The context window is the maximum number of tokens a single request may contain — input plus the output it generates. Current capacities: 1,000,000 tokens on Claude Sonnet 5, Claude Opus 5, and Claude Fable 5.1; 200,000 on Claude Haiku 4.5. On the current tokenizer, 1M tokens is roughly 555,000 words.
Separately, max output caps the response: 128K tokens synchronously on Sonnet 5, Opus 5, and Fable 5.1, and 64K on Haiku 4.5. On the Message Batches API, several current models support up to 300K output tokens behind a beta header.
Three rules that follow:
max_tokensis a ceiling, not a reservation. You are billed for tokens actually generated. Setting it generously costs nothing directly — but if the model hits it, generation stops mid-sentence withstop_reason: "max_tokens", which is a truncation bug, not a completion.- Thinking and response text share the
max_tokensceiling. At higheffort, a smallmax_tokensstarves the answer. - Capacity is not free. Every token in the window is re-billed on every turn it survives.
Sampling Parameters
After each forward pass the model produces a probability distribution over the vocabulary. Sampling parameters shape how the next token is drawn from it.
| Parameter | Range | Effect |
|---|---|---|
temperature | 0.0-1.0 | Flattens or sharpens the distribution. Low values concentrate probability on the top candidates; high values spread it |
top_p (nucleus) | 0.0-1.0 | Considers only the smallest set of tokens whose cumulative probability reaches p |
top_k | integer | Considers only the k highest-probability tokens |
stop_sequences | array of strings | Halts generation when one is emitted; stop_reason becomes "stop_sequence" |
Practical guidance the exam rewards:
- Change one at a time.
temperatureandtop_pboth truncate the same distribution; tuning both together makes the effect uninterpretable. - Low temperature is not determinism. It biases strongly toward the highest-probability path, but identical output across runs is not guaranteed by the API. Reproducibility comes from pinning the model snapshot and holding the prompt byte-identical — not from
temperature: 0alone. - Structure beats sampling for structured output. If you need valid JSON, force a tool call with
tool_choicerather than lowering temperature and hoping. Sampling shapes style; a schema enforces shape. - Thinking constrains sampling. On manual extended-thinking models,
temperaturemust be left at its default when thinking is enabled, and alteringtop_p/top_krestricts the reasoning exploration the mode exists to enable.
Technical Fundamentals: The HTTP and SDK Surface
Claude is a REST API over HTTPS. There is no WebSocket protocol and no session object on the server; streaming uses Server-Sent Events over ordinary HTTP, which matters for infrastructure design because SSE traverses standard HTTP proxies while WebSockets often do not.
POST https://api.anthropic.com/v1/messages
x-api-key: $ANTHROPIC_API_KEY
anthropic-version: 2023-06-01
content-type: application/json
Three headers, three facts:
x-api-key, notAuthorization: Bearer. This trips up developers arriving from other providers.anthropic-versionis required and pins the API contract independently of the model. Version pinning and model pinning are two different decisions.anthropic-betaopts into pre-GA features; a request that needs a beta header and omits it fails rather than silently degrading.
The core endpoints:
| Endpoint | Purpose |
|---|---|
POST /v1/messages | Synchronous or streaming inference |
POST /v1/messages/count_tokens | Exact token count, no inference, no generation cost |
POST /v1/messages/batches | Asynchronous batch submission (50% discount) |
POST /v1/files | Upload a document once, reference it by ID |
GET /v1/models | Capabilities, max_input_tokens, max_tokens per model |
What the SDKs add
The official SDKs (anthropic for Python, @anthropic-ai/sdk for TypeScript, plus Go, Java, C#, Ruby, and PHP) are thin wrappers over that REST surface, and they are worth using because of what they handle for you:
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from the environment
resp = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system="You are a precise technical assistant.",
messages=[{"role": "user", "content": "Summarise this incident report."}],
)
- Automatic retries with exponential backoff on 429, 500, and 529 (2 retries by default), respecting
retry-after. - Streaming helpers that reassemble SSE deltas — including partial JSON in
tool_useblocks — into whole objects. - Typed response models, so a
thinkingblock or atool_useblock is a typed object rather than a dict you must guess at. - Credential handling from the environment, so the key never has to appear in source.
Deployment surfaces
The same Messages API is reachable through Amazon Bedrock, Google Cloud (Vertex AI), Microsoft Foundry, and Claude Platform on AWS. The model IDs differ per platform, and partner-operated platforms set their own lifecycle dates and their own pricing — a fact that matters when you are tracking retirement across a multi-cloud deployment.
A team budgets prompts by dividing character count by 4. Their document pipeline works fine on English prose but starts overflowing budgets when they add support for minified JSON payloads and Japanese documents. What is the correct explanation and fix?
An engineer wants byte-identical output from Claude across repeated runs for a regression test. They set temperature to 0 and are surprised when outputs occasionally differ. What actually delivers reproducibility?
A developer migrating from another LLM provider gets 401 errors from every Claude API call. Their request sets Authorization: Bearer $KEY and content-type: application/json. What is wrong?