Set Max Tokens
What max_tokens Is
max_tokens is a required parameter of every Messages API request. It sets the maximum number of tokens Claude may generate in its response:
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain recursion."}],
)
It is an upper limit, not a target. Claude does not try to fill it. When the answer is complete, Claude stops on its own, often far below the limit. max_tokens only matters when the response would otherwise be longer.
What a Token Is
A token is the unit of text a model reads and writes: a word, part of a word, a punctuation mark, or a piece of whitespace. Common English words are often one token; longer or rarer words are split into several.
As a rough guide, the tokenizer used by current Claude models fits about 555,000 English words into 1 million tokens, so one word averages a little under two tokens. Code, numbers, and non-English text usually use more tokens per word. Exact counts are available from the API (Lessons 44–46).
Why max_tokens Exists
The limit protects you in several ways:
| Purpose | Explanation |
|---|---|
| Cost control | You pay for output tokens. The limit caps the maximum cost of any single response. |
| Latency control | Generating tokens takes time. A limit caps how long a response can take. |
| Resource protection | It prevents a runaway response, for example one that repeats itself, from producing huge outputs. |
| Predictability | Your application knows the largest response it might have to store, display, or process. |
Why it is required rather than optional: the API makes you decide explicitly how much output you are willing to accept, instead of applying a hidden default that might not suit your application.
How max_tokens Affects the Response
Two outcomes are possible:
1. Claude finishes before the limit. The response is complete, and stop_reason is "end_turn":
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=2048,
messages=[{"role": "user", "content": "What is 2 + 2? Answer with just the number."}],
)
print(message.stop_reason) # end_turn
print(message.usage.output_tokens) # A small number, far below 2048
2. Claude reaches the limit. Generation stops immediately, possibly mid-sentence, and stop_reason is "max_tokens":
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=300,
messages=[{"role": "user", "content": "Write a detailed guide to Python packaging."}],
)
print(message.stop_reason) # max_tokens
The response is truncated. It is not an error and no exception is raised, so your code must check stop_reason to notice.
You Pay for Tokens Generated, Not for the Limit
Billing uses the actual number of output tokens, reported in message.usage.output_tokens. A request with max_tokens=8000 that produces a 150-token answer is billed for 150 output tokens, plus any thinking tokens.
Consequence: a generous max_tokens does not increase cost for short answers. It only allows long answers when they are needed.
Thinking Tokens Count Toward max_tokens
This is the most important detail for current models.
Claude Opus 5.5 and Claude Fable 5.1 always use adaptive thinking, and the other current models use it by default: the model may reason internally before writing its answer. Thinking tokens count toward max_tokens, and they are billed as output tokens even though the thinking text is not shown by default.
max_tokens = 1,000
┌──────────────────────────────┬──────────────────────┐
│ thinking: 700 tokens │ answer: 300 tokens │
└──────────────────────────────┴──────────────────────┘
both count toward the same 1,000-token limit
This has a surprising consequence. With a small max_tokens, the model can spend the whole budget thinking and stop before writing any text:
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=30,
messages=[{"role": "user", "content": "Compare three sorting algorithms."}],
)
print(message.stop_reason) # max_tokens
print([block.type for block in message.content]) # Possibly ['thinking'] with no text
The response contains a thinking block and no answer at all.
How to Avoid It
- Leave headroom. Set
max_tokenshigh enough for both thinking and the answer. - Lower the effort for simple tasks. The
effortsetting tells the model how much to think. Less thinking leaves more of the budget for the answer, and reduces latency and cost:
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
output_config={"effort": "low"},
messages=[{"role": "user", "content": "Classify as positive or negative: 'Great service!'"}],
)
Claude Opus 5.5's default effort is medium. Effort is covered in depth with the advanced thinking features (Lessons 151–153).
Valid Range
| Constraint | Value |
|---|---|
| Minimum | 1 |
| Maximum for current models (Messages API) | 128,000 (128K) tokens |
| Maximum with the Message Batches API | Up to 300K on supported models, with the output-300k-2026-03-24 beta header |
A value above the model's maximum is rejected with a 400 error. The maximum differs between models, especially older ones, so read it from the Models API instead of hardcoding it:
model_info = client.models.retrieve("claude-opus-5-5")
print(model_info.max_tokens) # 128000
Relationship with the Context Window
The context window (1M tokens for current models) must hold both the input and the generated output. If your input is very large, the model may run out of context space before reaching max_tokens. In that case, the response ends with:
stop_reason == "model_context_window_exceeded"
As with max_tokens, the response is valid but truncated. This situation is rare unless you send extremely large inputs, such as long document collections or very long conversations.
Very Large Values Need Streaming
Generating tens of thousands of tokens can take minutes. Holding a single HTTP connection open and silent for that long is unreliable, because some networks drop idle connections. For that reason, the SDK raises a ValueError for non-streaming requests that it expects to take longer than about 10 minutes, based on the max_tokens you request.
Two ways to handle long outputs:
- Use streaming (Lessons 51–60). This is the recommended approach. The response arrives piece by piece, and the connection stays active.
- Set a timeout explicitly on the client or request, which also disables the check (Lesson 10).
Note: For long responses, streaming also gives users a much better experience. They see text appear immediately instead of waiting for the whole response.
Choosing a Value
There is no single correct number. Choose based on the longest complete answer you expect, plus room for thinking:
| Task | Typical answer length | Suggested max_tokens |
|---|---|---|
| Classification, yes/no, a label | A few tokens | 1,024 (headroom for thinking), with low effort |
| Short answers, one or two paragraphs | 50–300 tokens | 1,024–2,048 |
| Explanations, emails, summaries | 300–1,500 tokens | 4,096 |
| Long articles, detailed reports, larger code files | 2,000–8,000 tokens | 8,192–16,384, with streaming |
| Very long generation, such as complete documents or big refactors | 10,000+ tokens | 32,000+, with streaming |
Why the small tasks still get 1,024: with thinking models, a tiny limit risks a response with no text. The generous limit costs nothing extra, because you pay only for tokens actually generated.
max_tokens Is Not a Length Instruction
max_tokens cuts off a response. It does not make Claude write shorter. If you want a short answer, ask for one in the prompt:
# Wrong way to get a short answer: produces a truncated long answer
client.messages.create(model="claude-opus-5-5", max_tokens=60, messages=[...])
# Right way: request the length, and keep a safe limit
client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "In exactly two sentences, explain what an API is."}],
)
Claude plans its answer around what you ask for. It does not plan around max_tokens, so a low limit simply interrupts it. Lesson 38 (Control Response Length) covers prompting for length in detail.
| Goal | Tool |
|---|---|
| A shorter or longer answer | Instructions in the prompt |
| A safety ceiling on cost and time | max_tokens |
| Less thinking for simple tasks | effort |
Detecting and Handling Truncation
Every application that uses the answer should check stop_reason:
from anthropic.types import Message
def get_text(message: Message) -> str:
return "".join(block.text for block in message.content if block.type == "text")
def is_truncated(message: Message) -> bool:
return message.stop_reason in {"max_tokens", "model_context_window_exceeded"}
When a response is truncated, there are three sensible options:
| Option | When to use |
|---|---|
| Retry with a higher limit | The answer should fit within the model's maximum |
| Stream with a higher limit | Long outputs, where streaming avoids timeouts |
| Ask for a shorter answer, or split the task into parts | The task genuinely produces too much output |
Never show or store a truncated answer as if it were complete. For example, truncated JSON will fail to parse, and a truncated summary may omit important conclusions.
Practical Example: Automatic Retry on Truncation
This function requests an answer with a reasonable limit. If the response is cut off, it retries once with a larger limit, capped at the model's maximum, and reports how many output tokens were used:
"""complete_answer.py: request an answer and retry once if it is truncated."""
from anthropic import Anthropic
from anthropic.types import Message
MODEL = "claude-opus-5-5"
client = Anthropic()
MODEL_MAX_TOKENS = client.models.retrieve(MODEL).max_tokens
def get_text(message: Message) -> str:
return "".join(block.text for block in message.content if block.type == "text")
def create(prompt: str, max_tokens: int) -> Message:
return client.messages.create(
model=MODEL,
max_tokens=max_tokens,
messages=[{"role": "user", "content": prompt}],
)
def ask_complete(prompt: str, max_tokens: int = 4096) -> str:
"""Return a complete answer, retrying once with a larger limit if needed."""
message = create(prompt, max_tokens)
if message.stop_reason == "max_tokens":
larger = min(max_tokens * 4, MODEL_MAX_TOKENS)
if larger > max_tokens:
print(f"[info] Truncated at {max_tokens} tokens; retrying with {larger}.")
max_tokens = larger
message = create(prompt, max_tokens)
if message.stop_reason in {"max_tokens", "model_context_window_exceeded"}:
raise RuntimeError(
f"Answer still incomplete (stop_reason={message.stop_reason}). "
"Ask for a shorter answer or split the task."
)
print(f"[info] Used {message.usage.output_tokens} of {max_tokens} allowed output tokens.")
return get_text(message)
if __name__ == "__main__":
answer = ask_complete(
"Write a step-by-step guide to setting up a Python project with a "
"virtual environment, dependencies, and tests.",
max_tokens=1024,
)
print(answer)
Example output when the first attempt is too small:
[info] Truncated at 1024 tokens; retrying with 4096.
[info] Used 2310 of 4096 allowed output tokens.
## 1. Create the project folder
...
How It Works
MODEL_MAX_TOKENSis read once from the Models API at startup, so the retry never exceeds what the model allows. That works for any model, without hardcoding 128,000.create()wraps the request so that both attempts send exactly the same prompt and differ only in the limit.- The first request uses a moderate limit, which keeps typical requests fast.
- If
stop_reasonis"max_tokens", the function retries with four times the limit, capped bymin(...)at the model maximum. Theif larger > max_tokenscheck avoids a pointless retry when the limit was already at the maximum. - The final check raises an error if the answer is still incomplete, instead of returning partial text that callers might mistake for a full answer.
- The usage line shows how much of the allowed budget was used, including thinking. Logging this over time shows whether your default limit is well chosen.
- The retry costs extra. The first attempt's tokens are billed too. For outputs you know will be long, start with a higher limit, and use streaming, instead of relying on retries.
Note: For very large limits, switch this function to streaming (Lessons 51–60). Otherwise the SDK's long-request check may raise a
ValueErrorbefore the request is sent.
Monitoring Output Tokens
Every response reports what was actually used:
print(message.usage.input_tokens) # Tokens you sent
print(message.usage.output_tokens) # Tokens generated, including thinking
Logging these values over time tells you:
- whether your
max_tokensvalues are realistic: how often you hit the limit, and how close typical responses come, - how much thinking a task uses at a given effort level, and
- what each feature of your application costs.
Common Mistakes
Using max_tokens to Make Answers Shorter
What happens: answers end mid-sentence instead of being concise.
Fix: ask for the length you want in the prompt, and keep max_tokens as a safety ceiling.
Setting max_tokens Too Low for a Thinking Model
What happens: responses intermittently contain no text, with stop_reason == "max_tokens", because thinking consumed the budget.
Fix: leave generous headroom (1,024 or more even for tiny answers), and lower effort for simple tasks.
Not Checking stop_reason
What happens: truncated answers are displayed, saved, or parsed as if complete. Truncated JSON raises parsing errors that look unrelated to token limits.
Fix: check for max_tokens and model_context_window_exceeded on every response your application depends on.
Exceeding the Model's Maximum
What happens: max_tokens=200000 returns a 400 error for models whose maximum is 128,000.
Fix: read the maximum from the Models API, or keep values within the documented limit.
Requesting Huge Outputs Without Streaming
What happens: the SDK raises ValueError for requests it expects to take more than about 10 minutes, or long requests time out on unreliable networks.
Fix: use streaming for large max_tokens values.
Estimating Cost from Visible Text
What happens: costs are higher than expected, because thinking tokens are billed as output but not shown.
Fix: calculate cost from usage.output_tokens.
Best Practices
- Treat
max_tokensas a ceiling, not a length control. Request length in the prompt. - Leave room for thinking, especially on Claude Opus 5.5 and Fable 5.1, where thinking is always on.
- Use
effortto reduce thinking on simple, high-volume tasks. - Always check
stop_reasonbefore using a response. - Use streaming for long outputs, typically when
max_tokensis above about 8,000–16,000. - Read model limits from the Models API when your code works with several models.
- Log
usage.output_tokensand tune limits from real data. - Make limits configurable (Lesson 8), so they can be adjusted without code changes.
Exercise
- Ask Claude to "Write a detailed history of the Python language" with
max_tokens=200. Printstop_reason, the block types, andusage.output_tokens. - Repeat with
max_tokens=4096. Comparestop_reasonand the length of the answer. - Ask a one-word classification question with
max_tokens=1024, first with the default effort and then withoutput_config={"effort": "low"}. Compareusage.output_tokens. - Change
ask_complete()so it returns a tuple of(text, output_tokens)instead of printing the usage line.
stop_reasonismax_tokens. The block list may be['thinking', 'text']with a cut-off text block, or only['thinking']if the budget ran out during thinking.output_tokensis 200 or very close to it.stop_reasonis usuallyend_turn. The answer is complete, andoutput_tokensis whatever was needed, typically well below 4096.- With low effort,
output_tokensis usually smaller, because the model spends fewer tokens thinking. The exact numbers vary from run to run. - A version that returns the usage:
def ask_complete(prompt: str, max_tokens: int = 4096) -> tuple[str, int]:
"""Return (answer, output_tokens), retrying once with a larger limit if needed."""
message = create(prompt, max_tokens)
if message.stop_reason == "max_tokens":
larger = min(max_tokens * 4, MODEL_MAX_TOKENS)
if larger > max_tokens:
message = create(prompt, larger)
if message.stop_reason in {"max_tokens", "model_context_window_exceeded"}:
raise RuntimeError(f"Answer still incomplete (stop_reason={message.stop_reason}).")
return get_text(message), message.usage.output_tokens
text, used = ask_complete("Explain Python generators with an example.", max_tokens=1024)
print(text)
print(f"Output tokens used: {used}")
Returning values instead of printing them lets the caller decide what to do with the usage, such as logging it, storing it, or showing it to an administrator.