Claude's Context Window: 200K, 1M, and What Fills It Fastest
9 min read · updated August 11, 2026
There is no single answer to “how big is Claude’s context window”, and the two numbers in circulation are both correct for different models. What matters more than either is the accounting: what is counted, and in what order it grows.
The documented numbers
Anthropic publishes the per-model limits on its models overview page. At the time of writing, the current frontier and mid-tier models — the Opus and Sonnet families from the 4.6 generation onward — document a context window of one million tokens, while the small fast model, Claude Haiku 4.5, documents 200,000. The 200K figure is not wrong; it is the limit that the older Claude 3, 3.5, 4 and 4.5 families were built with, and it is still the limit on the current Haiku.
The primary source is Anthropic’s model overview documentation, which lists the window per model ID. Anthropic also publishes it per-model through the Models API, which is the version that cannot go stale in a copy: a GET /v1/models/{id} response carries max_input_tokens (the context window) and max_tokens (the output ceiling) as fields.
What counts against the window
The window is a limit on the whole request plus what the model generates in response to it, not on the user message. Five things consume it, and they are not equally visible:
- The system prompt. Sent on every request, because the API is stateless. A 2,000-token system prompt is 2,000 tokens on turn one and on turn forty.
- Tool definitions. Every tool in the
toolsarray is serialised into the prompt — name, description and the full JSON Schema of its inputs. This is the cost people forget, because nothing in the request body looks like prose. - The full message history. Every prior user turn, every prior assistant turn, and every
tool_resultblock you sent back. Tool results are usually the largest single contributor in an agent loop, because they carry raw output. - Attachments. Images and PDF pages are converted to tokens and counted like anything else.
- The generated response, including thinking. The output shares the same window. On a thinking model, the reasoning tokens are part of that output.
A worked example of an agent loop
The following arithmetic is a derivation, not a measurement, and its assumptions are labelled so you can substitute your own. Measure the real numbers for your prompts with the token counting endpoint before relying on any of it.
Assumptions. A 200,000-token window. A system prompt of 1,500 tokens. Twelve tools, each with a description and a schema that serialises to roughly 250 tokens, so 3,000 tokens of definitions. A file-reading tool whose results average 4,000 tokens. An assistant turn averaging 400 tokens.
fixed cost, resent every turn:
system prompt 1,500
tool definitions (12 x 250) 3,000
------
4,500
marginal cost per tool-calling turn:
assistant turn (text + tool_use) 400
tool_result 4,000
------
4,400
turns until the window is full:
(200,000 - 4,500) / 4,400 = 44 turnsForty-four turns sounds generous until you notice what the number is made of. The fixed cost is 2 per cent of the window and the tool results are 91 per cent of the growth. Halving the system prompt buys you nothing measurable. Truncating tool results to the 500 tokens the model actually needs takes the marginal cost from 4,400 to 900 and the same window to roughly 217 turns — a five-fold improvement from changing one thing.
Run the same arithmetic against a one-million-token window and the untruncated loop reaches about 226 turns. That is a real improvement, and it is also the reason long windows change the failure mode rather than removing it: the loop that fills 200K in forty turns fills 1M in two hundred, and the cost of every one of those turns is the whole prefix re-read.
What happens when you go over
Two different failures, and they are worth telling apart because they arrive through different channels and only one of them raises.
If the request itself is too large, the API rejects it before generating anything — a 400 with an invalid_request_error naming the token count and the limit. Your SDK throws, your error handler fires, nothing is billed for output. This is the pleasant version: it is loud, it is immediate, and it happens before you have shown the user anything.
If the request fits but the response runs into the ceiling mid-flight, you get an HTTP 200 with a well-formed message body and no exception at all. The only signal is stop_reason. The value max_tokens means the generation hit the output cap you asked for; model_context_window_exceeded means it hit the window itself. They call for different fixes — raise max_tokens for the first, shorten the conversation for the second — which is why the API distinguishes them, and why collapsing both into a generic “response truncated” branch sends you to the wrong remedy half the time.
On a streamed request neither value is visible while text is flowing. Both arrive at the end, in delta.stop_reason on the message_delta event, by which point you have already rendered the partial answer to the user. A streaming UI therefore needs a way to mark a rendered response as incomplete after the fact — and it is much easier to build that before the first time a long answer stops mid-sentence in front of a customer.
Why trimming the middle does not work
The obvious response to a full window is a sliding window: drop the oldest turns, keep the recent ones, carry on. It is the first thing everyone builds and it has three problems, in ascending order of how long they take to diagnose.
The first is a hard error. A tool_use block and its matching tool_result are a pair, validated by the API. Trim a window boundary between them — drop the assistant turn that made the call while keeping the user turn that answered it — and the next request is a 400 complaining about a tool_use_id that refers to nothing. Any trimming logic has to treat a call and its result as one indivisible unit, which means the boundary can only fall in certain places.
The second is silent and expensive. Prompt caching is a prefix match, so a cached conversation is only reusable while its opening bytes are unchanged. Trimming the oldest turns changes the very front of the prompt, which invalidates the cached prefix for everything after it. The request that trims is therefore a full-price re-read of the entire remaining conversation, and if you trim on a rolling basis you never hit cache again. Trimming saves you tokens against the window and costs you the discount that was making the window affordable.
The third is behavioural. The instruction the user gave in turn three is usually still binding in turn forty, and a sliding window deletes it. What the user experiences is a model that quietly stops obeying a constraint it was following ten minutes ago, which reads as the model degrading rather than as a truncation you performed.
What works instead is trimming the parts that are genuinely dead rather than the parts that are merely old. Tool results are the candidate: a directory listing from twenty turns ago describes a state that no longer exists, and replacing its content with a short placeholder while leaving the block itself in place preserves the pairing the API validates. Summarising a run of old turns into one synthetic message works too, at the cost of the same cache invalidation — which is why it belongs at deliberate checkpoints rather than on every request.
Checking the limit yourself
The /v1/messages/count_tokens endpoint takes the same request body as /v1/messages — system, messages, tools — and returns the input token count without generating anything. It is the only accurate way to size a prompt for Claude; a tokenizer built for another vendor’s models will not agree with it, sometimes by a wide margin.
curl https://api.anthropic.com/v1/messages/count_tokens \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-4-6",
"system": "You are a build engineer.",
"tools": [ ... your real tool array ... ],
"messages": [{"role": "user", "content": "Why did the release job fail?"}]
}'Send your real tool array rather than a placeholder. The whole point of the measurement is the part of the prompt you did not write by hand. See the token counting endpoint for what it does and does not include, and the context window exceeded error for the recovery path once you are over.