Use Extended Thinking

Su Suriya Ravichandran Updated 10 Nov 2026
7 min read ·Lesson 148 of 200

Giving Claude Room to Reason Before It Answers

Most of the answers in this course came straight from Claude's first pass at a problem. For many tasks that is enough. For harder ones, such as multi-step arithmetic, subtle code changes, or questions that require weighing several constraints, the quality of the answer improves when Claude reasons through the problem before writing its reply. Extended thinking is the feature that makes this reasoning available to your application.

When thinking is on, Claude produces a separate reasoning phase before its answer. That reasoning is returned as thinking blocks in the response content, ahead of the text blocks (Lesson 16). Your code can read those blocks, pass them back in later turns, and decide whether to show them to users. The reasoning is also billed as output tokens, which is the main cost to plan for.

This lesson explains the current configuration, the difference between adaptive and manual thinking, which models accept which setting, and how to validate a thinking configuration before sending it. Lesson 152 covers how to control the depth of thinking, and Lesson 153 covers how to read and preserve the blocks.


Two Modes: Adaptive and Manual

The API offers two ways to turn thinking on, and they differ in who decides how much to think:

ModeConfigurationWho decides the depth
Adaptivethinking: {"type": "adaptive"} with output_config: {"effort": ...}Claude decides when and how much to think, guided by the effort level
Manualthinking: {"type": "enabled", "budget_tokens": N}You set a token budget for reasoning

The documentation describes adaptive thinking as the current mechanism. Claude determines how much reasoning a request needs, and the effort parameter steers that decision. Manual thinking is the earlier mechanism, in which a fixed budget is the ceiling for reasoning. Adaptive thinking is generally the better fit for current models, because the model allocates reasoning to the problem rather than to a number chosen in advance.

A minimal adaptive request looks like this:

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    thinking={"type": "adaptive"},
    output_config={"effort": "medium"},
    messages=[{"role": "user", "content": "Is 1,001 prime? Show your reasoning briefly."}],
)

thinking decides whether reasoning happens. output_config.effort decides how much work the whole response receives, which in adaptive mode includes how deeply Claude thinks. The two are separate parameters, and they live in different places in the request.


Which Models Accept Which Mode

Not every model accepts every configuration, and the rules have changed across generations. Sending a configuration a model does not accept produces a 400 error, which is easy to avoid by checking before the request:

Model groupAdaptiveManual budget_tokensNotes
Older models, such as Claude Sonnet 4.5, Claude Opus 4.5, and Claude Haiku 4.5Not availableRequired formAdaptive returns an error
Claude Opus 4.6 and Claude Sonnet 4.6AvailableDeprecatedMigrate to adaptive
Claude Opus 4.7 and later, including Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5AvailableNot acceptedenabled returns an error
Claude Opus 5.5Always onNot acceptedThinking cannot be turned off

Two details in this table catch many teams. First, Claude Opus 5.5 thinks on every request, so a disabled setting is not valid there. Second, Claude Haiku 5.5 does not accept manual thinking at all, so code that worked on an earlier Haiku model will fail on it.

The documentation's advice for migration is direct: remove budget_tokens, set thinking to adaptive, and control depth with output_config.effort.


Validating the Configuration

Because the rules depend on the model, a small validator that checks the configuration before sending catches mistakes early. It does not replace the API's own checks, but it makes the rules visible and testable. The validator below encodes the groups from the table:

"""thinking_config.py: check a thinking configuration against the model's rules."""

LEGACY_MANUAL_ONLY = {"claude-sonnet-4-5", "claude-opus-4-5", "claude-haiku-4-5"}
TRANSITIONAL = {"claude-opus-4-6", "claude-sonnet-4-6"}
ADAPTIVE_ONLY = {"claude-opus-4-7", "claude-opus-4-8", "claude-opus-5", "claude-sonnet-5",
                 "claude-opus-5-5", "claude-sonnet-5-5", "claude-haiku-5-5", "claude-fable-5-1"}
ALWAYS_ON = {"claude-opus-5-5"}


def check_thinking(model: str, thinking: dict, max_tokens: int) -> list[str]:
    problems: list[str] = []
    kind = thinking.get("type")

    if model in ALWAYS_ON and kind == "disabled":
        problems.append(f"{model} always thinks; thinking cannot be disabled")
    if model in LEGACY_MANUAL_ONLY and kind == "adaptive":
        problems.append(f"adaptive thinking is not available on {model}; use enabled with budget_tokens")
    if model in ADAPTIVE_ONLY and kind == "enabled":
        problems.append(f"manual budget_tokens is not accepted on {model}; "
                        "use adaptive thinking with output_config.effort")
    if model in TRANSITIONAL and kind == "enabled":
        problems.append(f"budget_tokens is deprecated on {model}")

    if kind == "enabled":
        budget = thinking.get("budget_tokens")
        if budget is None:
            problems.append("enabled thinking needs budget_tokens")
        else:
            if budget < 1024:
                problems.append("budget_tokens must be at least 1024")
            if budget >= max_tokens:
                problems.append("budget_tokens must be less than max_tokens")
    return problems


if __name__ == "__main__":
    cases = [
        ("claude-opus-5-5", {"type": "adaptive"}, 4096),
        ("claude-opus-5-5", {"type": "enabled", "budget_tokens": 4096}, 8192),
        ("claude-sonnet-4-5", {"type": "enabled", "budget_tokens": 2048}, 4096),
        ("claude-sonnet-4-5", {"type": "adaptive"}, 4096),
        ("claude-sonnet-4-5", {"type": "enabled", "budget_tokens": 512}, 4096),
        ("claude-sonnet-4-6", {"type": "enabled", "budget_tokens": 4096}, 4096),
        ("claude-opus-5-5", {"type": "disabled"}, 4096),
    ]
    for number, (model, thinking, max_tokens) in enumerate(cases, start=1):
        print(f"case {number}:", check_thinking(model, thinking, max_tokens) or "ok")

Output:

case 1: ok
case 2: ['manual budget_tokens is not accepted on claude-opus-5-5; use adaptive thinking with output_config.effort']
case 3: ok
case 4: ['adaptive thinking is not available on claude-sonnet-4-5; use enabled with budget_tokens']
case 5: ['budget_tokens must be at least 1024']
case 6: ['budget_tokens is deprecated on claude-sonnet-4-6', 'budget_tokens must be less than max_tokens']
case 7: ['claude-opus-5-5 always thinks; thinking cannot be disabled']

How It Works

  1. The model sets are the rules. Each set corresponds to a row of the table, so a new model is added in one place, and the checks follow.
  2. The always-on check comes first for disabled, because that is the only configuration that can be rejected for every thinking-capable model in the ALWAYS_ON set.
  3. Adaptive is rejected on legacy models, and manual is rejected on the adaptive-only models. The messages name the correct alternative, so the fix is obvious.
  4. Manual budgets have their own rules. The budget must be at least 1024 and below max_tokens, because the reasoning and the answer share the output space. Case 6 shows both the deprecation and the size rule at once.
  5. Problems are collected, not raised one at a time, so a configuration with several faults is fixed in one pass.

What Thinking Costs

Thinking tokens count toward max_tokens, so a limit set too low can leave no room for the answer. The reasoning is billed as output tokens, which is usually the more expensive side of the bill. A request with a small visible answer can still be costly if Claude reasoned at length, and that is why effort is worth tuning (Lesson 152). Budgeting for thinking means budgeting for output, not only for the text users see.

Thinking also affects caching. Changing the thinking configuration, or the effort level, invalidates cached message prefixes, as Lesson 127 described. A feature that switches between thinking settings on each request will not benefit from prompt caching on those requests.


Using Thinking in an Application

Enabling thinking changes the shape of the response. The content list now contains thinking blocks before the text. Code that reads content[0].text breaks, because the first block is the reasoning. The correct habit, from Lesson 42, is to select text blocks by type. Lesson 153 covers the thinking blocks themselves, including how to keep them intact across tool calls.

Thinking is most useful for tasks where a wrong answer is costly and the reasoning can be checked: analysis, planning, code review, and problems with several steps. For simple lookups, classification, and formatting, it adds cost and latency without improving the result, which is why the effort level should match the task.


Summary

Extended thinking gives Claude a reasoning phase before its answer. Current models use adaptive thinking, steered by effort, and older models use a manual token budget. Each model accepts only some configurations, so validation prevents avoidable errors. Thinking is billed as output, counts toward max_tokens, and invalidates cache prefixes when its settings change. The next lesson shows how to choose the effort level and, where it still applies, the manual budget.

0 Comments

Reviewed before they appear

No comments yet.

Anthropic SDK Using Python
Anthropic SDK Intro Claude API Intro SDK Requirements Install Anthropic SDK Upgrade the SDK Create an API Key Set API Keys Use Environment Variables Import Anthropic Create a Client Send Your First Message Choose a Claude Model Set Max Tokens Read a Response Print Response Text Understand Message Blocks Use System Prompts Send User Messages Set Temperature Handle Stop Reasons Messages API Intro Create Messages Use Message Roles Send Conversation History Continue a Conversation Build a Chat Loop Reset Chat History Use Multiple Turns Inspect Message IDs Read Usage Data Write Clear Prompts Set a System Prompt Add Context to Prompts Use Prompt Templates Add Examples to Prompts Request JSON Output Use XML Tags Control Response Length Improve Prompt Quality Avoid Prompt Injection Read Text Blocks Read Content Blocks Check Stop Reasons Count Input Tokens Count Output Tokens Count Tokens Before Sending Limit Output Tokens Use Stop Sequences Read Request IDs Parse API Responses Streaming Intro Stream Text Output Use Stream Events Use Stream Helpers Get Final Messages Stream Async Responses Handle Stream Errors Build a Live Chat Stream Long Responses Close Streams Safely Build a Chatbot Keep Chat History Manage Chat Sessions Add Chat Memory Build a CLI Chatbot Build a Web Chatbot Use Flask with Claude Use FastAPI with Claude Return JSON from an API Add Chat Error Messages Async SDK Intro Create an Async Client Send Async Messages Use asyncio Run Parallel Requests Stream Async Text Handle Async Errors Set Async Timeouts Close Async Clients Choose Sync or Async Tool Use Intro Define a Tool Create Tool Schemas Send Tools to Claude Read Tool Calls Run Python Tools Return Tool Results Continue After Tool Use Control Tool Choice Handle Tool Errors Use the beta_tool Decorator Create a Tool Runner Add Tool Docstrings Validate Tool Inputs Use Multiple Tools Limit Tool Calls Log Tool Activity Build a Weather Tool Build a Search Tool Build a Calculator Tool Structured Output Intro Request JSON Data Validate JSON Output Use Pydantic Models Handle Invalid JSON Extract Fields from Text Generate Typed Data Use Image URLs Send Base64 Images Read PDFs with Claude Use Output Schemas Build Data Extraction Test Structured Responses Send Image Inputs Upload Files Use File IDs Process Text Files Summarize Documents Extract Table Data Handle File Errors Read Cache Usage Reduce Repeated Input Costs Debug Cache Misses Prompt Caching Intro Enable Prompt Caching Cache System Prompts Cache Long Context Test Cache Performance Use Cache Breakpoints Cache Best Practices Message Batches Intro Create a Message Batch Set Batch IDs Check Batch Status List Message Batches Get Batch Results Process Batch Outputs Handle Failed Batch Items Use Batches for Bulk Tasks Compare Batches and Requests Handle API Errors Handle Rate Limits Retry Failed Requests Set Request Timeouts Handle Network Errors Use Exponential Backoff Build Reliable API Calls Use Extended Thinking Log API Errors Track Request IDs Paginate API Results Set Thinking Budgets Read Thinking Blocks Use Tool Search Use Web Search Tools Use Computer Tools Use Citations Use Token Counting Read Raw Responses Set Custom Headers Use Claude on AWS Bedrock Use Claude on Vertex AI Use Claude on Microsoft Foundry Configure Cloud Credentials Choose a Cloud Provider Compare API Integrations Configure Provider Clients Handle Cloud Errors Use Cloud SDK Extras Deploy Cloud Apps Protect API Keys Use a .env File Ignore Secrets in Git Validate User Input Reduce Prompt Injection Limit Tool Permissions Avoid Unsafe Tool Calls Sanitize Tool Results Set Usage Limits Review Security Logs Test SDK Functions Mock API Responses Test Error Handling Test Streaming Code Measure Token Usage Track API Costs Configure Logging Deploy with Docker Deploy a FastAPI App Monitor Production Apps Build a Claude Chatbot Build a Text Summarizer Build a Code Explainer Build a JSON Extractor Build a PDF Assistant Build a Streaming Chat App Build a Tool-Calling Agent Build a Batch Processor Build a Research Assistant Build a Claude API Dashboard
Ask about this post
AI Ask about this post

Ask questions about Use Extended Thinking and get answers drawn from it.

Signed-in readers only.