# Claude Code SDK vs API: Which Should I Use? > **Quick answer:** Use the raw Claude API (the Messages endpoint, via a Client SDK) when you want to control every model call yourself and don't need autonomous tool use. Use the Claude Agent SDK, the renamed Claude Code SDK, when you want Claude to run its own tool loop, with built-in tools, sessions, and context management, in Python or TypeScript. *Published by [TryUncle](https://tryuncle.com) — the on-screen assistant that teaches DaVinci Resolve on your own screen.* *Updated 2026-08-02 · Claude Agent SDK and Claude API documentation (August 2026) · Canonical: https://tryuncle.com/learn/claude-code/claude-code-sdk-vs-api-which-should-i-use* I get some version of this question constantly from developers just past their first week with Claude Code: do you build on the Agent SDK, or do you just hit the API yourself? Both work. They solve different problems, and picking the wrong one means either fighting a framework you don't need or reinventing one you do. As of August 2026, here's the short version. The Claude API is Anthropic's Messages endpoint: send a prompt, get a response, done. The Claude Agent SDK, which used to be called the Claude Code SDK before a September 2025 rename, is a library that wraps that same API in an autonomous loop, the same one that runs Claude Code itself. One gives you a single model turn. The other gives you an agent that keeps going until the job is finished. ## What's the difference between the Claude API and the Claude Agent SDK? The Claude API returns one response per request and then stops. It's up to your code to notice a `tool_use` block, run that tool, send the result back as a new message, and repeat that cycle for as many rounds as the task needs. If Claude wants to call five tools in sequence to finish a task, you write the logic for those five round trips yourself, including retries, timeouts, and deciding when to give up. The Claude Agent SDK does that work for you. You call `query()` with a prompt and a list of allowed tools, and the SDK runs the model, executes built-in and custom tools, tracks context, and streams messages back until it reaches a final result. It's the identical agent loop that runs the Claude Code CLI, exposed as an importable library for Python and TypeScript. **The Claude API gives you one model turn per request. Everything after that, running a tool, feeding the result back, deciding when to stop, is code you write yourself.** If you're building a plain text-in, text-out feature (summarize this ticket, classify this email, draft this reply) with no tool calls at all, that's exactly the shape the raw Client SDK is built for. If you're building something that reads a codebase, edits files, runs shell commands, or otherwise takes more than one step to finish a task, that's the Agent SDK's whole reason for existing. ## Why did Anthropic rename the Claude Code SDK to the Claude Agent SDK? Because the same harness had outgrown coding tasks. Anthropic explained the rename directly: "the agent harness that powers Claude Code (the Claude Code SDK) can power many other types of agents, too," and added, "to reflect this broader vision, we're renaming the Claude Code SDK to the Claude Agent SDK." The change happened on September 29, 2025, and it was a rename, not a rewrite. Anyone who built on the old Claude Code SDK package name is running the same underlying loop today under the new one. **A rename in September 2025 didn't change a line of the underlying harness. Claude Code SDK and Claude Agent SDK are the same product under two different names.** Anthropic's engineering team put the design philosophy behind that harness plainly: > "The key design principle behind the Claude Agent SDK is to give your agents a computer, allowing them to work like humans do." > > Thariq Shihipar, "Building agents with the Claude Agent SDK," Anthropic (September 29, 2025) That's the whole pitch in one sentence. Instead of hand-coding a narrow tool for every task, you give Claude a real environment (a file system, a terminal, a browser) and let it use the same general-purpose tools a human engineer would. If you only remember one naming fact, make it this: any tutorial, GitHub repo, or forum post that says "Claude Code SDK" from before late September 2025 is talking about today's Claude Agent SDK. The install commands changed (`claude-agent-sdk` instead of the old `claude-code-sdk` package name), but the agent loop itself is unchanged. ## Claude API vs Claude Agent SDK vs Claude Code CLI vs Claude Managed Agents Four different products answer four different needs, and Anthropic's own documentation lays out the split this way: | If you're... | Use | Why | | --- | --- | --- | | Calling the API directly and running the tool loop yourself | Client SDK (Claude API) | Direct access to the Messages endpoint. You implement retries, tool execution, and turn-taking. Available in Python, TypeScript, C#, Go, Java, PHP, and Ruby. | | Building an agent without writing the tool loop by hand | Claude Agent SDK | A library that runs the agent loop, built-in tools, and context management in your own process. Python and TypeScript only. | | Doing interactive development from a terminal | Claude Code CLI | The interactive tool built for daily hands-on use, not for embedding in another app. | | Running long or async agents without managing your own sandbox | Claude Managed Agents | A separate, hosted REST API. Anthropic runs the agent and the sandbox; you're billed on tokens plus session runtime. | Notice the language gap in that first row: the Client SDK reaches seven officially supported languages, while the Agent SDK reaches two. That single line answers a question I see asked constantly. **Seven officially supported languages beat two. The Client SDK reaches Go, Java, C#, PHP, and Ruby that the Agent SDK, Python and TypeScript only, doesn't touch.** If your stack is Ruby on Rails or a Go microservice and you want autonomous tool use anyway, your options are the raw API with a hand-rolled loop, or shelling out to the Claude Code CLI as a subprocess with `-p` and `--output-format json`. Neither is the Agent SDK itself; both are workarounds around its language limit. ## When should you use the raw Claude API? Use the raw Claude API, through a Client SDK, when you control every step of the exchange yourself and don't want a framework making decisions for you. Concretely, that's: - A single-turn feature: classify a support ticket, summarize a document, draft a reply, extract structured data from text. No tools, no multi-step reasoning, just one prompt and one response. - A language outside Python and TypeScript, where the Agent SDK simply isn't available as a native library. - A bespoke agent loop you already have working and don't want to replace, maybe because it has custom retry logic, a non-standard tool format, or integrates with an existing orchestration system. - Fine-grained cost control over exactly which tokens get sent, since you're building every request by hand rather than accepting whatever the SDK's built-in tool definitions add. The tradeoff is real: every tool-execution round trip through the raw API is logic you write, test, and maintain yourself. If Claude decides it needs to read a file, then grep for a pattern, then edit three lines, that's three separate API calls with three separate rounds of your own code parsing `tool_use` blocks and constructing `tool_result` messages. **Every extra tool-execution round trip through the raw API is a round trip your code has to write, test, and debug on its own.** ## When should you use the Claude Agent SDK? Use the Agent SDK when you want Claude to own the loop. That's the case for: - Coding agents: bug finders, refactoring tools, code review bots, anything that needs to read a codebase, make edits, and verify its own work. - Research or data agents that need to search, fetch, and synthesize across many steps without you hand-coding each step. - Anything that benefits from the built-in tool set (file read/write/edit, Bash, Glob, Grep, web search and fetch) rather than reimplementing those from scratch against the raw API. - Multi-turn work where sessions need to persist and resume across process restarts, since the SDK writes session state to disk automatically. - Projects that want subagents: specialized child agents with their own isolated context windows, useful for keeping a large research task from bloating your main conversation. We cover exactly how those work a bit further down. The install is close to trivial. `pip install claude-agent-sdk` (Python 3.10+) or `npm install @anthropic-ai/claude-agent-sdk` (Node.js 18+), set an `ANTHROPIC_API_KEY` environment variable, and `query()` handles the rest. Both packages bundle a native Claude Code binary for your platform, so you don't need Claude Code installed separately to run an agent built on the SDK. ## How do built-in tools differ between the two? The raw Claude API gives you tool definitions as JSON schemas you write and pass in the `tools` parameter; Claude decides when to call them, but you supply every tool's implementation yourself, even simple ones like "read this file." The Agent SDK ships the tools already built. Claude Code's tools reference lists more than 40 built-in tools available to the harness, spanning file operations (`Read`, `Write`, `Edit`), search (`Glob`, `Grep`), shell execution (`Bash`), web access (`WebFetch`, `WebSearch`), subagent spawning (`Agent`), and task tracking (`TaskCreate`, `TaskList`, `TaskUpdate`), among others. You don't write these; you enable them with an `allowed_tools` list and the SDK executes them for you, prompting for permission according to whatever `permission_mode` you set. We break that permission system down in detail below. That's the practical shortcut the SDK buys you: skip weeks of writing and hardening your own file-editing and shell-execution tools, and start from a set Anthropic already maintains and updates. MCP (Model Context Protocol) connections work differently too. The Agent SDK connects to MCP servers directly as part of its tool set, the same way [Claude Code's own MCP integration](https://tryuncle.com/learn/claude-code/claude-code-mcp-server-setup-tutorial) does. Against the raw API, you'd implement that MCP client logic yourself, since the Messages endpoint has no native concept of an MCP server. More on exactly how that connection works in the custom tools section below. ## What does the same task look like in both, side by side? Abstract descriptions only go so far. Here's one small task, "read `app.log` and summarize the errors," built both ways. Against the raw Claude API, you own the loop. A trimmed version looks like this in Python: ```python messages = [{"role": "user", "content": "Summarize the errors in app.log"}] while True: response = client.messages.create( model="claude-sonnet-5", max_tokens=1024, tools=[read_file_tool_schema], # a JSON schema you wrote messages=messages, ) messages.append({"role": "assistant", "content": response.content}) tool_calls = [b for b in response.content if b.type == "tool_use"] if not tool_calls: break # Claude has no more tools to call, print response.content and stop results = [] for call in tool_calls: if call.name == "read_file": output = read_file(call.input["path"]) # the implementation is on you results.append({"type": "tool_result", "tool_use_id": call.id, "content": output}) messages.append({"role": "user", "content": results}) ``` Every piece of that, the schema, the `read_file` function, the loop condition, the message bookkeeping, is yours to write and test. Add a second tool, like grepping for a pattern, and you write a second branch inside that `for` loop. Against the Agent SDK, the same task collapses to one call, because `Read` and `Grep` already exist as built-in tools: ```python async for message in query( prompt="Summarize the errors in app.log", options=ClaudeAgentOptions(allowed_tools=["Read", "Grep"]), ): if hasattr(message, "result"): print(message.result) ``` No schema, no `read_file` function, no loop condition to get wrong. The SDK runs however many turns Claude needs, whether that's one `Read` call or a `Read` followed by a `Grep` followed by another `Read`, and hands you back the final result. That difference, a hand-written `while` loop versus a single `query()` call, is the entire value proposition of the Agent SDK in miniature. Everything past this point in the article is really just detail on what that loop gives you once your task gets more complicated than reading one file. ## How do permission controls differ between the raw API and the Agent SDK? The raw Claude API has no permission system at all. When a `tool_use` block comes back, whether to actually run the function behind it, silently or after asking a human, is a decision your code makes before it ever gets to the next request. There's no equivalent of a hook, a deny rule, or an approval callback baked into the transport. You build all of it, or you build none of it and just run whatever Claude asks for. The Agent SDK has an actual permission system, and it's worth understanding because the defaults surprise people. Every tool call passes through the same six-step check, in this order: hooks first, then deny rules, then ask rules, then the active permission mode, then allow rules, and finally your `canUseTool` callback if nothing else resolved it. A hook or a deny rule can block a call even in the most permissive mode. | Mode | What happens | Use it when | | --- | --- | --- | | `default` | No auto-approvals. Anything not otherwise resolved hits your `canUseTool` callback. | You want a human or custom logic approving each call. | | `acceptEdits` | File edits (`Edit`, `Write`) and filesystem commands like `mkdir`, `rm`, `mv`, and `cp` auto-approve inside the working directory. Everything else still prompts. | You trust Claude's file edits and want faster iteration. | | `bypassPermissions` | Every tool that reaches the permission-mode step auto-approves, including `Bash`. Hooks and explicit deny or ask rules still run first. | A sandboxed environment where you accept the risk. | | `plan` | Claude explores and proposes a plan. File edits, and file-modifying shell commands, always route to `canUseTool`, even when an allow rule matches. | You want a reviewed plan before anything touches disk. | | `dontAsk` | Anything not pre-approved by an allow rule is denied outright. `canUseTool` is never called. | A locked-down, unattended agent with a fixed tool surface. | | `auto` | A model classifier approves or denies each prompt for you. | You want fewer manual approvals without going as far as bypass. | **Setting `allowed_tools` to just `Read` while `permission_mode` is `bypassPermissions` still approves every tool, including `Bash`, `Write`, and `Edit`, because `bypassPermissions` approves anything that reaches the permission-mode step regardless of what's on the allow list.** If you need bypass-style speed but want specific tools blocked anyway, use `disallowed_tools`, not a short `allowed_tools` list, since allow rules only pre-approve, they don't restrict. ## How do you extend each with custom tools and external services? Against the raw API, adding a new capability means writing a JSON schema for the `tools` parameter and a function that implements it, then wiring that function into your own tool-execution branch. There's no packaging convention beyond what you invent. The Agent SDK gives you two patterns instead of one improvised one. **In-process custom tools.** Define a tool with `@tool` in Python or `tool()` in TypeScript: a name, a description, an input schema, and an async handler. The handler returns a `content` array (usually a text block), plus optional `structuredContent` for machine-readable data and `isError` to compose the failure message Claude reads instead of a raw exception. Wrap one or more tools in `create_sdk_mcp_server` (Python) or `createSdkMcpServer` (TypeScript), pass that server through the `mcp_servers` option, and list `mcp__{server_name}__{tool_name}` in `allowed_tools`: ```python @tool("get_temperature", "Get the current temperature at a location", {"latitude": float, "longitude": float}) async def get_temperature(args): # your fetch logic here return {"content": [{"type": "text", "text": f"Temperature: {temp_f}°F"}]} weather_server = create_sdk_mcp_server(name="weather", version="1.0.0", tools=[get_temperature]) options = ClaudeAgentOptions( mcp_servers={"weather": weather_server}, allowed_tools=["mcp__weather__get_temperature"], ) ``` A handler that throws an uncaught exception doesn't crash the agent loop the way an unhandled exception in a hand-rolled raw API loop can; the SDK's in-process server catches it, turns it into an error result, and Claude sees the message and keeps going. If you want to hand Claude something more specific than a raw stack trace, catch the error yourself and return `isError: true` with your own explanation. **External MCP servers.** For a service someone else already wrapped, like a filesystem browser, a GitHub client, or a Postgres connector, you point the SDK at it instead of writing anything. Local processes use stdio (a `command` and `args`, the same shape as running the server from a terminal); remote services use HTTP or SSE with a `url` and optional auth `headers`. Both are configured through the same `mcp_servers` option, or a shared `.mcp.json` file at your project root. Authentication for a stdio server goes through an `env` map; for an HTTP or SSE server it goes through a `headers` map, typically a bearer token. OAuth is the one gap: the SDK won't run a browser flow for you, so you complete that in your own app and hand the resulting access token to the server config. The practical upshot: any external service you'd otherwise wire into a raw API tool loop by hand, an internal API, a database, a SaaS product with its own MCP server, becomes just another entry in `mcp_servers` for the Agent SDK, subject to the exact same permission rules as the built-in tools. ## How do hooks let you intercept and control an agent's behavior? A hook is a callback function the Agent SDK runs when something happens inside the agent loop: a tool is about to be called, a tool just returned a result, a subagent starts or stops, the whole session ends. You register one against an event name, and the SDK calls it at that exact point, before the operation proceeds. The raw Claude API has nothing at this layer either. We already covered its permission gap above: there's no equivalent of a hook, a deny rule, or an approval callback baked into the transport. What hooks add on top of that is control over the content of a tool call, not just whether it runs at all. A raw API integration can refuse to execute a tool. It can't cleanly rewrite the arguments Claude sent before executing it, or fire a webhook the instant any tool completes, without you building that machinery from scratch inside your own loop. The Agent SDK exposes a long list of hookable events. A handful cover most real use cases: | Hook event | Fires when | Typical use | | --- | --- | --- | | `PreToolUse` | A tool is about to run | Block or rewrite the call before it executes | | `PostToolUse` | A tool just returned a result | Log the outcome, append extra context | | `PostToolUseFailure` | A tool call failed | Handle or report the error separately from a normal result | | `SubagentStart` / `SubagentStop` | A subagent spawns or finishes | Track parallel work, aggregate results | | `Stop` | The agent finishes a turn | Save session state before the process exits | | `Notification` | The agent has a status update (permission prompt, idle, auth) | Forward status to Slack or a pager | Here's the shape of a `PreToolUse` hook that blocks any attempt to write a `.env` file, adapted from Anthropic's own docs: ```python async def protect_env_files(input_data, tool_use_id, context): file_path = input_data["tool_input"].get("file_path", "") if file_path.split("/")[-1] == ".env": return { "hookSpecificOutput": { "hookEventName": input_data["hook_event_name"], "permissionDecision": "deny", "permissionDecisionReason": "Cannot modify .env files", } } return {} options = ClaudeAgentOptions( hooks={"PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[protect_env_files])]} ) ``` A `matcher` string like `"Write|Edit"` restricts the hook to specific tool names so you're not paying the cost of running it on every single call; an empty or omitted matcher runs it on everything. Return `{}` to allow the operation unchanged, or set `permissionDecision` to `deny`, `ask`, or `allow` to make the call yourself. When it comes back with a decision, that decision plugs directly into the six-step permission chain covered above: hooks are evaluated first, ahead of deny rules, ask rules, the active permission mode, allow rules, and your `canUseTool` callback, so a hook can override even `bypassPermissions` mode. **A `PreToolUse` hook that returns `deny` blocks a tool call even when `permission_mode` is set to `bypassPermissions`, because hooks are evaluated before the permission mode is ever consulted.** Hooks that don't need to influence the outcome, like forwarding a notification to Slack, can return an async marker instead of blocking the agent loop while a webhook completes. That's a pattern with no raw API equivalent either: your hand-rolled loop either waits on every side effect or you build your own background-task plumbing around it. ## Streaming input or single message mode: which should you use? The raw Claude API is, by definition, always call-and-response. There's no transport-level notion of a "session" you keep open; anything resembling one is state you maintain in your own process. The Agent SDK gives you a genuine choice here, and picking the wrong one for your deployment target causes real friction. **Streaming input mode** is the default and the one Anthropic recommends: you feed the agent an async generator of messages instead of a single string, and the session stays alive as a long-lived process. That's what unlocks image attachments (inline base64 image blocks mixed into a message), messages queued to run in sequence, real-time interruption mid-task, and a `canUseTool` callback that can prompt a real human while the agent waits. **Single message mode** is the simpler `query()` call you've seen throughout this article: one prompt in, a stream of result messages out, and the process can exit as soon as it's done. | Capability | Streaming input mode | Single message mode | | --- | --- | --- | | Image attachments in a message | Yes, inline base64 | No | | Queued or interruptible messages | Yes | No | | Stateless / serverless friendly | No, needs a long-lived process | Yes | | Multi-turn continuation | Native, within the same generator | Via `continue: true` or `resume: sessionId` on a new call | The practical rule Anthropic's own docs give: reach for single message mode specifically when you're running in a stateless environment like a Lambda function, where nothing survives between invocations anyway, so paying for a persistent generator buys you nothing. Reach for streaming input mode for anything interactive, like a chat UI where a user might attach a screenshot mid-conversation or want to interrupt a long-running task before it finishes. ## How does pricing differ between the Claude API and the Agent SDK? They don't differ in the underlying rate. Both bill the exact same per-token price for whatever model you call. As of August 2026, Claude Sonnet 5 costs $2 per million input tokens and $10 per million output tokens through August 31, 2026, rising to $3 and $15 after that. Claude Opus models run $5 input / $25 output per million tokens, and Claude Haiku 4.5 runs $1 / $5. | Model | Input (per MTok) | Output (per MTok) | Notes | | --- | --- | --- | --- | | Claude Sonnet 5 | $2 (through Aug 31, 2026), then $3 | $10, then $15 | Current flagship model | | Claude Opus 5 / 4.8 / 4.7 / 4.6 / 4.5 | $5 | $25 | Highest-capability tier | | Claude Haiku 4.5 | $1 | $5 | Cheapest current model | Where the bill actually diverges is usage pattern, not rate card. Every tool definition you send, whether through the raw API's `tools` parameter or the Agent SDK's built-in tools, adds input tokens to the request; Anthropic's own pricing docs list per-model tool-overhead token counts that run from roughly 280 to over 800 tokens depending on the model and tool choice setting. An autonomous agent loop, by design, can also run more turns than a hand-written one that you've deliberately capped, since nothing stops it from calling a tool five times if that's what it decides the task needs. Prompt caching (cache reads run at 10% of standard input price) and the Batch API (a 50% discount on both input and output) apply identically to both, since they're properties of the underlying Messages API, not of whichever layer sits on top of it. If you're specifically trying to pin down what Claude Code itself costs on a subscription versus per-token API billing, including the real numbers Anthropic has published on typical developer spend, [our full cost breakdown](https://tryuncle.com/learn/claude-code/how-much-does-claude-code-cost) covers that in more depth than fits here. ## How do you handle errors, retries, and rate limits in each? Both hit the same wall eventually: Anthropic enforces rate limits per organization, measured in requests per minute (RPM), input tokens per minute (ITPM), and output tokens per minute (OTPM), tracked separately for each model with a token bucket algorithm rather than a hard reset every 60 seconds. Exceed any of the three and you get a 429 response with a `retry-after` header telling you exactly how many seconds to wait. On the Start tier, Claude Sonnet 5 carries a 1,000 RPM / 2,000,000 ITPM / 400,000 OTPM ceiling; the Build and Scale tiers raise all three as your organization's usage history grows. Tokens read from the prompt cache don't count toward ITPM on most models, so aggressive caching effectively raises your usable throughput without Anthropic raising your limit. Where the two products diverge is how failure reaches your code. Against the raw API, the official Client SDKs already retry the easy cases for you: connection errors, 429s, and 5xx server errors get retried automatically with exponential backoff, twice by default, honoring the `retry-after` header when the API sends one. Each SDK exposes a `max_retries` option if you want more attempts or none at all. That safety net sits below your tool loop, though, not inside it. If your `read_file` function throws on a missing path, or Claude asks for a tool name you never registered, that's an exception in your own code, and no amount of SDK-level retry configuration touches it. You catch it, or your process crashes mid-loop with half a conversation already sent. The Agent SDK fails at a different boundary, because it isn't making HTTP calls directly, it's supervising a `claude` CLI subprocess over stdio and reading JSON messages off that pipe. Its Python package defines five exception types, all inheriting from one `ClaudeSDKError` base: | Exception | Raised when | | --- | --- | | `CLIConnectionError` | The SDK can't reach the subprocess at all | | `CLINotFoundError` | The bundled Claude Code binary is missing | | `ProcessError` | The subprocess exits abnormally; carries `exit_code` and `stderr` | | `CLIJSONDecodeError` | A line of subprocess output isn't valid JSON | | `MessageParseError` | A well-formed JSON message the SDK doesn't recognize | A minimal retry wrapper around `query()` looks like this: ```python import asyncio from claude_agent_sdk import query from claude_agent_sdk import CLIConnectionError, ProcessError async def query_with_retry(prompt, max_attempts=3): delay = 2 for attempt in range(max_attempts): try: async for message in query(prompt=prompt): yield message return except (CLIConnectionError, ProcessError) as e: if attempt == max_attempts - 1: raise await asyncio.sleep(delay) delay *= 2 ``` Catching `ClaudeSDKError` alone is enough if you just want a single net for "something in the loop broke." Catching the specific subclasses matters when the response differs: a `ProcessError`'s `stderr` often contains the actual API error text (a 429, an overloaded model, an invalid request), which is worth logging even though you're not catching an HTTP exception directly. **A single autonomous task that calls five tools in sequence spends five requests against your organization's requests-per-minute limit, not one, so an agent loop can trip a rate limit that a single hand-rolled API call never would.** That's a real operational difference even though both products draw from the same rate-limit pool: the Agent SDK's whole value proposition, letting Claude keep calling tools until the task is done, is also what makes it more likely to be the thing that exhausts your RPM budget during a traffic spike. If you're chasing a specific error inside an interactive Claude Code session rather than a library integration, [our debugging walkthrough](https://tryuncle.com/learn/claude-code/how-to-debug-with-claude-code) covers the CLI side of this in more depth. ## How do sessions and context management work in each? Against the raw Claude API, you manage conversation history yourself: every message you want Claude to remember has to be included in the array you send with the next request, and it's your job to trim, summarize, or cache that history as it grows. The Agent SDK manages this for you. A session is the full conversation history the SDK accumulates while an agent works: your prompt, every tool call, every tool result, every response. Both SDKs write sessions to disk automatically. Python's `ClaudeSDKClient` tracks the session ID internally across multiple `query()` calls in the same process; TypeScript passes `continue: true` on a later call to resume the most recent session in the current directory with no ID tracking required. You can also `resume` a specific session by ID, or `fork` one to try an alternative approach while leaving the original untouched, the same session mechanics [our guide to resuming a previous Claude Code session](https://tryuncle.com/learn/claude-code/how-to-resume-a-previous-claude-code-session) covers from the CLI side. Context limits still apply either way. Most Claude models cap out at a 200,000-token context window, with Claude Sonnet 5 and Claude Opus 4.6 and later supporting a 1-million-token window instead. The Agent SDK inherits Claude Code's automatic context compaction, summarizing older conversation before the window fills; against the raw API, you'd build that summarization logic yourself, deciding what to drop and when. If you've hit this wall inside Claude Code specifically, [our guide to fixing a mid-session context blowout](https://tryuncle.com/learn/claude-code/claude-code-running-out-of-context-mid-session) walks through `/compact`, `/clear`, and subagent offloading in detail, and the same compaction concept applies whether you're driving it from the CLI or from the SDK. ## How do subagents work, and when should you use them? A subagent is a separate agent instance your main agent spawns to handle a focused subtask, called through the built-in `Agent` tool. Whatever the subagent does, however many files it reads or tool calls it makes, stays inside its own context; only its final message returns to the parent conversation. That's the whole benefit in one sentence: a research task that would otherwise flood your main conversation with dozens of file reads instead comes back as one clean summary. You can define subagents three ways: programmatically through the `agents` parameter in `query()` options (the recommended approach for SDK apps), as markdown files in a `.claude/agents/` directory, or not at all, since Claude can always spawn the built-in `general-purpose` subagent on its own. A programmatic `AgentDefinition` looks like this: | Field | Required | What it controls | | --- | --- | --- | | `description` | Yes | When Claude should delegate to this subagent, in plain language | | `prompt` | Yes | The subagent's own system prompt | | `tools` | No | Restricts the subagent to a specific tool list; omit it and the subagent inherits everything available to subagents | | `model` | No | Overrides the model for this subagent, for example a cheaper model for routine work or a stronger one for high-stakes review | | `maxTurns` | No | A hard cap on how many turns this subagent can take | | `mcpServers` | No | MCP servers available specifically to this subagent | | `permissionMode` | No | Overrides the parent's permission mode, except when the parent itself uses `bypassPermissions`, `acceptEdits`, or `auto`, which always apply to every subagent | A subagent's context starts fresh, not empty: it gets its own system prompt, the prompt string you pass through the `Agent` tool call, and your project's `CLAUDE.md`. It does not get the parent's conversation history or prior tool results, so any file path, error message, or decision the subagent needs has to be spelled out in the prompt you hand it. Subagents run in the background by default as of Claude Code v2.1.198, and by default they can spawn subagents of their own, up to three layers deep. | Use case | Tools | Why | | --- | --- | --- | | Read-only code review | `Read`, `Grep`, `Glob` | Can examine code but never accidentally modify it | | Test execution | `Bash`, `Read`, `Grep` | Can run the suite and analyze output | | Full research agent | Omit `tools` | Inherits everything, for open-ended exploration | The raw Claude API has nothing comparable. Parallelizing sub-tasks against it means writing your own context isolation (a fresh `messages` array per sub-task), your own orchestration for running them concurrently, and your own logic for folding results back into the parent conversation, which is precisely what the `agents` parameter and the `Agent` tool give you for free. ## How do you monitor an agent running in production? Against the raw API, observability is entirely your own build. You already own every request and response, so you already have everything you'd log: wrap each `client.messages.create` call with your own timer, read the `usage` field for token counts, and record whichever fields your observability stack expects. Nothing about the Messages API stops you from doing this well, but nothing does it for you either. The Agent SDK ships instrumentation for free, because the CLI subprocess it runs has OpenTelemetry built in. It records spans around every model request and tool execution, emits counters for tokens and cost, and emits structured log events for prompts and tool results, then exports all three signals (traces, metrics, logs) to any backend that accepts the OpenTelemetry Protocol: Honeycomb, Datadog, Grafana, Langfuse, or a self-hosted collector. It's off by default. Turning it on is an environment variable, not a code change: ```bash CLAUDE_CODE_ENABLE_TELEMETRY=1 OTEL_METRICS_EXPORTER=otlp OTEL_LOGS_EXPORTER=otlp OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.example.com:4318 ``` Traces are still in beta and need one more flag, `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1`, on top of `OTEL_TRACES_EXPORTER=otlp`. Once traces are on, each turn of the agent loop becomes a `claude_code.interaction` span, with child spans for every model call and every tool execution nested underneath it, and a subagent's spans nest under the parent's tool-call span too, so a whole delegation chain shows up as one trace instead of scattered fragments. The nicer detail is what happens when your own application already emits OpenTelemetry: the SDK automatically propagates your active trace context into the CLI subprocess, so the agent's spans attach as children of your own trace instead of showing up as a disconnected root. Building that same trace-context propagation by hand against the raw API, so an agent-shaped feature nests correctly inside your existing service traces, is exactly the kind of plumbing the SDK buys you for one environment variable. By default, none of this exports the actual content Claude read or wrote, only structural data like durations, model names, and token counts. Prompt text, tool arguments, and full API request bodies are each behind their own opt-in flag (`OTEL_LOG_USER_PROMPTS`, `OTEL_LOG_TOOL_DETAILS`, `OTEL_LOG_RAW_API_BODIES`), which matters if your observability pipeline isn't approved to store what your agent handles. ## How do you deploy each to production? The raw Claude API deploys like any other stateless HTTP client, because that's what it is. Drop a `client.messages.create` call into a Lambda function, a request handler, a cron job, whatever your stack already runs, and it behaves the same way any other outbound API call does: no process to supervise, no local state to worry about losing. The Agent SDK changes that shape, because `query()` doesn't call the API directly, it spawns and supervises a `claude` CLI subprocess over stdio, and that subprocess owns a shell, a working directory, and JSONL session transcripts written to local disk. Run ten concurrent agent sessions and you're running ten subprocesses, each with its own process tree, and none of that state survives a container restart, a scale-down event, or a move to a different node unless you wire up a `SessionStore` adapter to mirror transcripts somewhere durable. **Running N concurrent Agent SDK sessions means running N subprocesses, each holding its own shell, working directory, and session transcript, which is a fundamentally different capacity-planning problem than scaling a stateless API client.** Anthropic's hosting guide lays out four session patterns depending on how long your container lives relative to the work it serves: | Pattern | Container lifecycle | Fits | | --- | --- | --- | | Ephemeral | One container per task, destroyed on completion | Bug fixes, document translation, one-off extraction | | Long-running | Persistent container serving many sessions | Chat bots, email triage agents, high-volume traffic | | Hybrid | Spins down idle, rehydrates from a `SessionStore` | Support agents and research tasks that sit idle between turns | | Multi-agent | Several subprocesses in one container | Agents that must collaborate in a shared environment | For sizing, treat 1 GiB RAM, 5 GiB disk, and 1 CPU per agent as a floor, not a target, since memory grows with session length and tool activity. Anthropic's own formula for how many agents fit on a host is straightforward: `agents per host = (host RAM - overhead) / (per-session RAM ceiling)`, where you measure that ceiling by running a representative session to your target length and recording peak memory. Worth knowing before you over-provision: token cost typically dominates container cost by an order of magnitude or more. A minimally sized container runs around $0.05 an hour; a single long agent session can spend several dollars in tokens in that same hour. If one container serves multiple tenants, the SDK's default filesystem behavior becomes a real risk: it reads settings and `CLAUDE.md` memory files from disk by default, which can leak one tenant's project context into another tenant's session in a shared container. Closing that gap takes four settings together: pass `settingSources: []` (or `setting_sources=[]` in Python) so no filesystem settings load, set `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` since auto memory loads regardless of `settingSources`, point `CLAUDE_CONFIG_DIR` at a per-tenant directory, and pass a distinct `cwd` on every `query()` call. None of this is a concern for the raw API, since it never touches your filesystem in the first place. ## What trips people up when moving from the raw API to the Agent SDK? A handful of gotchas account for most of the confusion I see, and every one of them traces back to an assumption carried over from raw API habits. - **A missing `ANTHROPIC_API_KEY`.** The Agent SDK authenticates through the exact same key as the raw API. There's no separate Agent SDK credential, so `query()` fails on the first turn if the environment variable isn't set, which surprises people expecting a distinct SDK-specific login step. - **Assuming `acceptEdits` covers everything.** It only auto-approves file edits and filesystem-modifying Bash commands like `mkdir`, `rm`, and `mv`. MCP tools and anything else still prompt normally. Pair it with an `allowedTools` wildcard, like `mcp__yourserver__*`, for MCP servers you already trust. - **Reading a `pending` MCP status as a failure.** MCP servers connect in the background by default, so the very first `init` message can report `pending` for a server that connects successfully a moment later. Check specifically for `failed` or `needs-auth`, not for anything that isn't `connected`. - **Porting a raw API tool schema straight into Python's `@tool` decorator.** The simple dict schema form treats every listed key as required, with no equivalent of the raw API's JSON schema `required` array to mark one optional. Drop the optional key from the schema, mention it in the description, and read it with `args.get()` in the handler, or pass a full JSON Schema dict when you need enums, ranges, or nested objects. - **Very long subagent prompts on Windows.** The OS command line caps out around 8,191 characters, so a lengthy programmatic `AgentDefinition.prompt` can fail there specifically. Move long instructions into a filesystem-based agent file instead. - **Skipping a turn cap.** A hand-rolled raw API loop almost always had some hard iteration limit baked in, even an implicit one from how the code was structured. Carry that habit over with `max_turns` on the Agent SDK; an autonomous loop with no cap can burn far more tokens than planned on a task that goes sideways. If you're migrating an existing hand-rolled integration rather than starting fresh, the shortest path looks like this: stop parsing `tool_use` blocks by hand, move each tool's logic into a `@tool` or `tool()` handler, wrap them in `create_sdk_mcp_server`, replace the manual `while` loop with a single `query()` or `ClaudeSDKClient` call, and carry over whatever safety limits (turn caps, timeouts) the hand-rolled version already had. None of the underlying model behavior changes; you're deleting plumbing, not rewriting the agent's logic. ## How does Claude Code GitHub Actions fit into this comparison? It's worth naming as a fifth option, even though it isn't really separate: Claude Code GitHub Actions is built directly on top of the Agent SDK, according to Anthropic's own docs. It's what you'd end up building yourself if you took the raw API, wrote a tool loop, and wired it to GitHub's webhook events, except Anthropic already did that work and packaged it as a GitHub Action. Mention `@claude` in an issue or pull request comment and it reads the thread, checks out the repository, and responds, whether that's implementing a feature, fixing a bug, or answering a question about the code. It follows the repo's own `CLAUDE.md` the same way the CLI and the SDK do, so [a well-written CLAUDE.md file](https://tryuncle.com/learn/claude-code/how-to-write-a-good-claude-md-file) pays off here too, not just in interactive sessions. Setup is a slash command: run `/install-github-app` from inside Claude Code, and it installs the GitHub App and walks you through adding the workflow file and the `ANTHROPIC_API_KEY` secret. A minimal workflow looks like this: ```yaml name: Claude Code on: issue_comment: types: [created] jobs: claude: runs-on: ubuntu-latest steps: - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} ``` Every Claude Code CLI argument passes through a single `claude_args` field, so `--max-turns 10 --model claude-sonnet-5` controls the same turn cap and model choice covered earlier in the Agent SDK section. That cap matters more here than in most SDK deployments, because a runaway job burns two separate meters at once: GitHub Actions minutes and Claude API tokens. Set a workflow-level timeout alongside `--max-turns` if you're running this on every pull request rather than only on an explicit mention. If your team already has an internal bot that reviews PRs or triages issues by reading linked docs, built by hand against the raw API, this is worth comparing against directly: you'd be reimplementing the checkout, prompt assembly, and tool loop that the Action already runs on top of the SDK, for the specific case of GitHub events. ## What if you're using Claude Code to script DaVinci Resolve instead of a general app? This crossover comes up more than you'd expect. We run a 100,000+ member DaVinci Resolve editing community, and Claude Code questions have been showing up in it steadily as editors start scripting parts of their own pipeline, batch renames, Fusion macro scaffolding, render queue automation, instead of clicking through them by hand. If that's your use case, the Agent SDK is almost always the better fit over the raw API, for the same reason it's the better fit for any coding-adjacent task: you want Claude reading and editing files and running commands across multiple steps, not a single text response. Community-built MCP servers like `samuelgursky/davinci-resolve-mcp` bridge Claude to Resolve's official scripting API, letting an Agent SDK-driven agent run Python commands against your actual project, though that requires DaVinci Resolve Studio, since the free edition doesn't support external scripting at all. Configured as an external MCP server the way we covered above, that connection is one entry in `mcp_servers`, not custom integration code. Worth naming clearly here: TryUncle is the on-screen assistant for DaVinci Resolve on macOS. Ask in plain words and Uncle points at the exact control on your screen, live, on the Edit, Color, and Fusion pages. That's a different job than anything covered in this article. Claude Code and the Agent SDK write and run scripts against your project; they have no opinion on where a node belongs in your color grade or why a Power Window keeps drifting off a subject's face mid-edit. TryUncle is a paid macOS app, currently $29.99 a month at the founder rate for the first 100 seats, cancel anytime, so check [TryUncle](https://tryuncle.com/?utm_source=tryuncle-learn&utm_medium=blog&utm_campaign=claude-code-sdk-vs-api-which-should-i-use) for the current rate rather than trusting a number that might have moved by the time you read this. The category has real alternatives worth knowing too, tools like Sottocut, PremiereCopilot, and heyeddie.ai automate specific edits or answer chat questions about your project; Uncle's job is narrower and more literal, watching your actual screen and pointing at the control, live, while you're still stuck on it. If you're mid-edit and stuck on exactly where a control lives, Uncle can point at it on your own screen while you keep working, instead of you pausing to search for the answer. ## What about Claude Managed Agents, where does that fit? Claude Managed Agents is a fourth option, distinct from both the raw API and the Agent SDK: a hosted REST API where Anthropic runs the agent and its sandbox for you, rather than in a process you operate. It bills on two dimensions, standard token rates plus session runtime at $0.08 per session-hour, metered only while a session's status is `running`, not while it's idle waiting on your next message. Reach for Managed Agents when you want long-running or asynchronous agents without standing up your own sandbox or session infrastructure. Reach for the Agent SDK when you want that same agent loop but running inside your own process, under your own infrastructure, with direct control over deployment. The two aren't competitors so much as different points on the same spectrum: how much infrastructure do you want to own versus hand off. ## Can you use both at once? Yes, and plenty of production systems do. It's common to build most of an app against the raw Claude API for tight control over specific single-turn features (classification, extraction, a chat widget with a fixed system prompt) while dropping into the Agent SDK for a specific autonomous subsystem, like an internal tool that reviews pull requests or triages support tickets by reading linked documentation. The two share the same underlying Messages API and the same model pricing, so there's no lock-in cost to mixing them inside one codebase. The only real constraint is language: any part of your stack that isn't Python or TypeScript has to stay on the Client SDK or shell out to the CLI, since the Agent SDK itself isn't available there. ## Which one should you use? **Pick the Agent SDK when you want Claude to own the loop; pick the Client SDK when you want to own it yourself.** That's the entire decision in one sentence, and almost every edge case resolves against it. Choose the **Claude API** (Client SDK) if: - Your task is single-turn: one prompt in, one response out, no tools. - You need a language the Agent SDK doesn't support: C#, Go, Java, PHP, or Ruby. - You already have a working custom agent loop and don't want to replace it. - You want the tightest possible control over exactly which tokens go into every request. Choose the **Claude Agent SDK** if: - Your task needs more than one step: reading files, running commands, calling multiple tools in sequence. - You're building in Python or TypeScript and don't want to write and maintain your own tool-execution loop, permission system, and context management. - You want sessions that persist and resume automatically across process restarts. - You want subagents, hooks, built-in observability, or Claude Code's skills and slash-command system available inside your own app. Neither one is a wrong answer in the sense that both call the identical underlying model at the identical token price. The wrong answer is picking based on which name sounds more current rather than what your task actually needs: a chatbot that answers one question at a time doesn't need an agent loop, and a coding agent that reads and edits files across a real project shouldn't be hand-rolling one from scratch. Match the tool to the shape of the work, and the SDK-versus-API question mostly answers itself. ## FAQ ### What is the actual difference between the Claude API and the Claude Agent SDK? The Claude API (the Messages endpoint, reached through a Client SDK) sends one prompt, gets one response, and stops. Your code decides what happens next: whether to run a tool, feed the result back, and when to end the exchange. The Claude Agent SDK wraps that same API in a loop that Claude itself drives, calling built-in tools, managing context, and continuing until the task is done, in Python or TypeScript only. ### Is the Claude Code SDK the same thing as the Claude Agent SDK? Yes. Anthropic renamed the Claude Code SDK to the Claude Agent SDK on September 29, 2025, because the same harness that powers Claude Code turned out to be useful for agents that have nothing to do with coding. No code changed in the rename. If a tutorial or package still says Claude Code SDK, it's describing the same product under its old name. ### Can I use the Claude Agent SDK in a language other than Python or TypeScript? Not as a library. The Agent SDK ships officially only as claude-agent-sdk for Python and @anthropic-ai/claude-agent-sdk for TypeScript. To drive the same agent loop from another language, Anthropic's own docs point you to running the Claude Code CLI as a subprocess with the -p flag and --output-format json, which is a workaround, not a native SDK. ### Which is cheaper, the raw Claude API or the Claude Agent SDK? Neither is inherently cheaper. Both bill the same underlying token rates, for example $2 per million input tokens and $10 per million output tokens on Claude Sonnet 5 through August 31, 2026. The Agent SDK often costs more per task in practice because its tool definitions add input tokens to every call and because an autonomous loop can run more turns than a hand-written one that stops earlier. ### Do I need Claude Code installed separately to use the Agent SDK? No. Both the Python and TypeScript Agent SDK packages bundle a native Claude Code binary for your platform as part of the install, so a separate Claude Code installation isn't required to run an agent built on the SDK. ### Should I use the Claude API or the Agent SDK to build a simple chatbot with no tools? The Claude API. If your app just needs Claude to read a message and write a reply, with no file access, no shell commands, and no multi-step tool use, the Agent SDK's agent loop and built-in tools are overhead you don't need. Reach for the Client SDK's Messages endpoint directly. ### What's the difference between the Agent SDK and Claude Managed Agents? The Agent SDK is a library that runs the agent loop inside a process you operate and pay standard token rates for. Claude Managed Agents is a separate, hosted REST API where Anthropic runs the agent and the sandbox for you, billed on tokens plus session runtime at $0.08 per session-hour, so you don't manage your own infrastructure. ### How do I decide between the Claude API and the Claude Agent SDK for a new project? Ask whether you're willing to write and maintain a tool-execution loop by hand. If yes, and you need a language other than Python or TypeScript, or want the simplest possible integration for single-turn tasks, use the Client SDK against the Messages API. If no, and you're building anything that reads files, runs commands, or takes more than one step to finish, use the Agent SDK. ## Sources - [Agent SDK overview - Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/overview) - [CLI, SDKs, and libraries - Claude Platform Docs](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) - [Quickstart - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/quickstart) - [Tools reference - Claude Code Docs](https://code.claude.com/docs/en/tools-reference) - [Work with sessions - Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/sessions) - [Pricing - Claude Platform Docs](https://platform.claude.com/docs/en/about-claude/pricing) - [Building agents with the Claude Agent SDK, by Thariq Shihipar - Claude (Anthropic)](https://claude.com/blog/building-agents-with-the-claude-agent-sdk) - [Explore the context window - Claude Code Docs](https://code.claude.com/docs/en/context-window) - [Configure permissions - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/permissions) - [Give Claude custom tools - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/custom-tools) - [Subagents in the SDK - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/subagents) - [Streaming Input - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/streaming-vs-single-mode) - [Connect to external tools with MCP - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/mcp) - [Intercept and control agent behavior with hooks - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/hooks) - [Claude API errors - Claude Platform Docs](https://platform.claude.com/docs/en/api/errors) - [Rate limits - Claude Platform Docs](https://platform.claude.com/docs/en/api/rate-limits) - [Error classes reference - claude-agent-sdk-python (GitHub)](https://github.com/anthropics/claude-agent-sdk-python/blob/main/src/claude_agent_sdk/_errors.py) - [Observability with OpenTelemetry - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/observability) - [Hosting the Agent SDK - Claude Code Docs (Agent SDK)](https://code.claude.com/docs/en/agent-sdk/hosting) - [Claude Code GitHub Actions - Claude Code Docs](https://code.claude.com/docs/en/github-actions) - [TypeScript Agent SDK reference - Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/typescript) - [TryUncle](https://tryuncle.com)