n4nAI

What does max_tokens actually control in an API call?

A precise technical explanation of what max_tokens controls in LLM API calls, how it interacts with context windows and stop sequences, and common misconceptions that cause production bugs.

n4n Team4 min read956 words

Audio narration

Coming soon — every post will get a voice note here.

What does max_tokens do in an LLM API call? It sets a hard ceiling on the number of tokens the model may generate in its completion. The parameter does not limit the prompt, the conversation history, or the total context window — it only caps the newly generated output tokens.

When the model hits this limit, the API truncates the response mid-token and returns a finish_reason of "length". Understanding this boundary is essential for controlling costs, preventing runaway generations, and designing reliable streaming handlers.

How max_tokens interacts with the context window

Every model has a maximum context window — for example, 128k tokens for GPT-4o or 200k for Claude 3.5 Sonnet. This window encompasses the system prompt, user messages, few-shot examples, tool definitions, and the model’s own generated output. The max_tokens parameter reserves a slice of that window exclusively for the completion.

{
  "model": "gpt-4o",
  "messages": [
    {"role": "system", "content": "You are a concise assistant."},
    {"role": "user", "content": "Summarize the attached 50-page PDF in detail."}
  ],
  "max_tokens": 4096
}

If your prompt consumes 120k tokens and you request max_tokens: 4096 on a 128k model, the call succeeds. If the prompt consumes 125k tokens, the same request fails with a context-length error before generation starts. The API validates prompt_tokens + max_tokens ≤ model_context_limit upfront.

Why max_tokens matters for production systems

Cost control

LLM providers bill by output tokens. A missing or excessively high max_tokens is the most common cause of surprise bills. A single runaway generation on a large model can cost dollars in seconds. Setting a sensible ceiling — often 1024 to 4096 for typical chat turns — bounds your worst-case spend per request.

Latency predictability

Output length correlates directly with generation time. A 200-token response typically completes in 500–1500 ms; a 16k-token response can take 30+ seconds. Fixed max_tokens lets you set meaningful request timeouts and design UI loading states that don’t hang indefinitely.

Streaming buffer management

When streaming, each chunk arrives as a server-sent event. Without a token ceiling, your client buffer can grow unbounded, triggering OOM kills in browser tabs or mobile apps. max_tokens gives you a hard upper bound for buffer allocation.

async def stream_with_cap(client, messages, max_tokens=2048):
    """Stream with a hard token cap and early exit."""
    collected = []
    token_count = 0
    
    async for chunk in client.chat.completions.create(
        model="gpt-4o",
        messages=messages,
        max_tokens=max_tokens,
        stream=True
    ):
        delta = chunk.choices[0].delta.content or ""
        collected.append(delta)
        token_count += len(delta.split())  # rough heuristic
        
        if token_count >= max_tokens:
            break  # defensive: stop processing if provider misses the cap
            
        yield delta
    
    if chunk.choices[0].finish_reason == "length":
        logger.warning("Generation truncated at max_tokens=%d", max_tokens)

Concrete example: summarization with a token budget

You need to summarize long documents but must keep each summary under 500 tokens for downstream processing. The prompt itself varies in length depending on the source document.

MAX_CONTEXT = 128_000  # gpt-4o
RESERVED_OUTPUT = 500
SAFETY_MARGIN = 100

def build_summary_request(doc_text: str) -> dict:
    prompt = f"Summarize in 3 bullet points:\n\n{doc_text}"
    prompt_tokens = count_tokens(prompt)  # use tiktoken or provider tokenizer
    
    available_for_prompt = MAX_CONTEXT - RESERVED_OUTPUT - SAFETY_MARGIN
    if prompt_tokens > available_for_prompt:
        # Truncate or chunk the document before sending
        doc_text = truncate_to_token_limit(doc_text, available_for_prompt - count_tokens("Summarize in 3 bullet points:\n\n"))
        prompt = f"Summarize in 3 bullet points:\n\n{doc_text}"
        prompt_tokens = count_tokens(prompt)
    
    return {
        "model": "gpt-4o",
        "messages": [{"role": "user", "content": prompt}],
        "max_tokens": RESERVED_OUTPUT,
        "temperature": 0.3
    }

This pattern — calculate prompt tokens, subtract from context limit, reserve output budget — prevents context-length errors and guarantees the summary fits your pipeline.

Common misconceptions

“Max_tokens limits the total conversation length”

False. It limits only the completion tokens. The prompt can consume the rest of the context window. If you need to bound total conversation length, you must implement sliding-window or summarization logic in your application layer.

“Setting max_tokens lower makes the model more concise”

Not reliably. The model still attempts to answer fully; it simply stops mid-sentence when the cap hits. For genuine conciseness, use prompt instructions (“Answer in 2 sentences”) combined with a modest max_tokens as a safety net.

“Max_tokens includes the prompt tokens”

False. prompt_tokens and completion_tokens are reported separately in the usage object. max_tokens applies only to completion_tokens.

{
  "usage": {
    "prompt_tokens": 12450,
    "completion_tokens": 500,
    "total_tokens": 12950
  }
}

“The model knows about max_tokens and plans accordingly”

The model has no awareness of this parameter. It generates token by token until the API enforces the stop. This is why you get hard cutoffs mid-word rather than graceful conclusions.

Interaction with stop sequences

stop sequences and max_tokens are independent termination conditions. Whichever triggers first wins.

{
  "max_tokens": 1000,
  "stop": ["\n\n", "###"]
}

If the model emits \n\n at token 342, generation stops with finish_reason: "stop". If it reaches token 1000 without hitting a stop sequence, it stops with finish_reason: "length". Design your stop sequences for semantic boundaries (end of JSON object, end of code block) and use max_tokens as the ultimate safety valve.

Provider-specific behaviors

OpenAI-compatible endpoints

Most OpenAI-compatible APIs honor max_tokens identically. Some add max_completion_tokens as an alias. The behavior is deterministic: hard cutoff, finish_reason: "length".

Anthropic

Anthropic uses max_tokens (required) with the same semantics. Their streaming response includes a stop_reason field with values "end_turn", "max_tokens", or "stop_sequence".

Google Vertex AI / Gemini

Uses maxOutputTokens. Same hard-cutoff behavior. Note that Gemini’s token counting differs slightly from OpenAI’s — always use the provider’s tokenizer for precise budgeting.

N4n.ai gateway

When routing through a multi-provider gateway, max_tokens passes through to the upstream provider unchanged. The gateway enforces the same validation: prompt_tokens + max_tokens ≤ selected_model_context_limit. If the primary provider returns a rate-limit error, the gateway falls back to an alternate provider with the same max_tokens value, preserving your output budget across failover.

Best practices checklist

  • Always set max_tokens explicitly. Omitting it defaults to the model’s maximum output (often 4096 or 16384), which is rarely what you want.
  • Set it per use case. Chat: 512–2048. Summarization: 256–1024. Code generation: 2048–8192. Structured extraction: 128–512.
  • Pair with stop sequences for clean semantic boundaries.
  • Monitor finish_reason distributions. A high "length" rate means your cap is too low for the task; a low rate with high latency means it’s too high.
  • Use the provider’s tokenizer for prompt token counting. Rough heuristics (word count × 1.3) fail on code, non-English text, and technical terminology.
  • Reserve headroom. Keep at least 5–10% of the context window free for prompt growth across conversation turns.

Debugging truncated responses

When users report “the answer cut off,” check three things:

  1. Finish reason"length" confirms max_tokens was hit.
  2. Completion tokens vs max_tokens — if completion_tokens == max_tokens, the cap was binding.
  3. Prompt token growth — in multi-turn conversations, earlier turns consume context, leaving less room for output. Implement conversation summarization or sliding windows before lowering max_tokens further.
def diagnose_truncation(response, requested_max):
    usage = response.usage
    finish = response.choices[0].finish_reason
    
    if finish == "length":
        if usage.completion_tokens >= requested_max:
            return "HARD_CAP: Increase max_tokens or shorten prompt"
        else:
            return "UNEXPECTED: Provider stopped early"
    elif finish == "stop":
        return "NORMAL: Stop sequence triggered"
    else:
        return f"OTHER: {finish}"

Summary

max_tokens is a hard output ceiling, not a suggestion. It reserves space in the context window for the completion, bounds cost and latency, and prevents buffer overflows in streaming clients. It does not affect the prompt, the model has no awareness of it, and it interacts orthogonally with stop sequences. Set it deliberately per use case, monitor finish_reason distributions, and pair it with semantic stop sequences for clean truncation boundaries.

Tagsmax-tokensapi-usagellm-basicsglossary

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All max tokens, stop sequences & output truncation posts →