The Claude Agent SDK is the library form of Claude Code: the same tool loop, permission system and session handling that power the CLI, callable from your own Python process instead of a terminal. Point it at NativePort’s native Anthropic route and it runs against Claude on the same one key and one balance as every other provider here, billed from your NativePort balance instead of a separate Anthropic account. This guide covers the minimal working setup: the environment variables NativePort’s route requires, a copy-paste example that returns a real response, and what’s worth knowing before you enable tools or run this somewhere with restricted network egress.
What you’ll need
- A NativePort API key. Sign up to get a key and $5 in credits.
- Python 3.10 or later.
pip install claude-agent-sdk==0.2.128. This installs the bundled Claude Code CLI that the SDK spawns as a subprocess; you don’t invoke it directly.- Your key exported as an environment variable, never hardcoded:
export NATIVEPORT_API_KEY="<your NativePort API key>"
Claude Agent SDK is not the Anthropic Python SDK
These are two different packages that solve different problems, and mixing up their setup is the most common way to get stuck.
The Anthropic Python SDK (pip install anthropic) is a thin client for POST /v1/messages. You build the request, manage the conversation state and drive any tool-use loop yourself. Pointing it at NativePort means constructing an Anthropic(base_url=..., auth_token=...) client and calling messages.create() directly.
The Claude Agent SDK (pip install claude-agent-sdk, the subject of this guide) packages the full Claude Code agent, including its own tool loop, file and shell tools, permission system and multi-turn session handling, as a library you call from Python. Under the hood it spawns a bundled claude CLI subprocess and talks to it over a local pipe. You never call the Messages API yourself; you configure ClaudeAgentOptions and let the SDK’s spawned process do it. Because that subprocess makes the actual HTTP calls, routing it through NativePort means setting variables in its environment, not passing a base_url argument to a Python client object.
If your goal is a plain chat completion or a tool loop you write yourself, the Anthropic Python SDK against the same route is the simpler choice. If you want Claude Code’s own agent loop (multi-step tool use, file edits, session resumption) driven from your own code, the Claude Agent SDK is the one that gives you that, and it’s what this guide sets up.
The environment variables NativePort’s route needs
The Claude Agent SDK has no gateway-specific configuration option. It works by forwarding environment variables to the Claude Code subprocess it spawns, and ClaudeAgentOptions(env={...}) is the documented way to set them for that one call without touching your shell’s environment. Five variables matter here:
ANTHROPIC_BASE_URL:https://api.nativeport.ai/anthropic. This is NativePort’s native Anthropic-format route; requests keep the Messages API’s own request and response shape.ANTHROPIC_AUTH_TOKEN: yourNATIVEPORT_API_KEY. Use this variable, notANTHROPIC_API_KEY. Claude Code sendsANTHROPIC_AUTH_TOKENas anAuthorization: Bearer <token>header, whileANTHROPIC_API_KEYsends it asx-api-key. NativePort’s route reads the bearer header, soANTHROPIC_API_KEYhere produces a401even with a valid key, because the credential lands in a header the route doesn’t check.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:1. Stops the CLI’s own version checks, telemetry and release-note fetches, which otherwise go to Anthropic’s own hosts regardless ofANTHROPIC_BASE_URL. See the network egress note below for why this matters even when your main calls work fine without it.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS:1. Turns off the CLI’s experimental beta request headers, keeping the request shape close to Claude Code’s stable baseline.CLAUDE_CODE_MAX_OUTPUT_TOKENS:32000. This one needs its own explanation, because skipping it is the single most common way this integration fails on the very first call.
Why the output-token override is required
Claude Code requests a fixed, fairly high output-token ceiling by default when talking to a Sonnet-class model, well above what NativePort’s /anthropic route currently accepts on a single request. The route enforces its own per-request output-token cap and rejects, rather than silently reduces, any request asking for more than that. Without CLAUDE_CODE_MAX_OUTPUT_TOKENS set, the first request from an otherwise correctly configured client fails with a 400 naming the requested value and the route’s limit.
CLAUDE_CODE_MAX_OUTPUT_TOKENS overrides that default request ceiling. Setting it to 32000, at or below the route’s current per-request cap, resolves the rejection with no other change to the setup. If NativePort’s documented cap changes later, use the new value; the error response always names the limit it’s enforcing.
Minimal example
This is the smallest configuration that completes a real round trip: no tools admitted, a single turn, and a plain text response back.
import asyncio
import os
from claude_agent_sdk import AssistantMessage, ClaudeAgentOptions, TextBlock, query
options = ClaudeAgentOptions(
model="claude-sonnet-5",
allowed_tools=[],
max_turns=1,
env={
"ANTHROPIC_BASE_URL": "https://api.nativeport.ai/anthropic",
"ANTHROPIC_AUTH_TOKEN": os.environ["NATIVEPORT_API_KEY"],
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
"CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1",
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "32000",
},
)
async def main():
async for message in query(prompt="Say hello in one sentence.", options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
asyncio.run(main())
query() yields a stream of typed message objects as the session runs (system messages, assistant messages, a final result message); this example filters down to the assistant’s text blocks and prints them. allowed_tools=[] and max_turns=1 keep this a strictly text-in, text-out call: no file access, no shell commands, nothing that reaches outside the conversation itself. That’s the right starting point for a first integration test, and the right default for anything that shouldn’t touch your filesystem at all.
ClaudeAgentOptions.env merges with your shell’s own environment before the subprocess starts, with env taking priority. You could equally export all five variables in your shell and drop the env argument; passing them explicitly, as above, is more portable when the script runs somewhere your shell isn’t already configured, and it’s the pattern this guide sticks to.
Model selection
ANTHROPIC_BASE_URL changes where a request goes, not which models exist behind it. --model claude-sonnet-5 (set through ClaudeAgentOptions(model=...)) passes straight through to NativePort’s route, which must itself serve that model id for the call to succeed. Use a full model id rather than a bare alias like sonnet, so the exact model you tested against is the one that runs later, independent of how an alias might resolve on a future CLI version. Check the Anthropic provider page for the current list of Claude models the route serves before picking an id for anything beyond a quick test.
Traffic that still reaches Anthropic directly
ANTHROPIC_BASE_URL and the two CLAUDE_CODE_DISABLE_* flags above cover the traffic that matters for a normal call, but a few narrower paths in the Claude Code CLI reach Anthropic’s own hosts regardless of that configuration:
- The built-in
WebFetchtool runs a domain-safety preflight againstapi.anthropic.combefore fetching any URL, independent ofANTHROPIC_BASE_URL. Turn it off with theskipWebFetchPreflight: trueClaude Code setting if your network blocks that host. Only relevant if you enableWebFetchinallowed_tools; it’s inactive in the minimal example above. - Fast mode’s availability check also bypasses
ANTHROPIC_BASE_URLand callsapi.anthropic.comdirectly. Not relevant unless your integration enables fast mode.
None of this affects the model call itself, which always goes to ANTHROPIC_BASE_URL. It matters only if your deployment restricts outbound access to NativePort’s hosts specifically; in that case, allow the narrow exception above or avoid the features that trigger it.
Enabling tools safely
Once the minimal example works, most real uses of the Claude Agent SDK go on to allow at least some tools, at which point permissions stop being optional. A few defaults worth setting deliberately:
- Pass
allowed_toolsas an explicit list rather than leaving it unset. An unset list defaults to a broad built-in set; naming exactly what you need (for example["Read", "Grep"]for a read-only research agent) is the difference between a session that can only look at files and one that can also edit or execute them. - Leave
permission_modeat its default, which prompts for anything not explicitly allowed, rather than switching it to a mode that skips permission checks. A mode that bypasses permission checks is appropriate only for a fully sandboxed, disposable environment, never for a process with access to real files or credentials. - For programmatic gating beyond a static allow-list,
ClaudeAgentOptionssupports acan_use_toolcallback and hook functions that run before a tool executes, letting you approve, deny or rewrite a specific call based on its arguments rather than only its name. - Treat
NATIVEPORT_API_KEYand any credentials a tool might read as you would any other secret: environment variable or secret store, never written into a prompt, a tool argument or a log line.
The official secure deployment guide covers this in more depth, including patterns for running the SDK in a sandboxed subprocess or container when tools need real filesystem or shell access.
Troubleshooting
401on every request. Almost always a header mismatch, not a bad key. NativePort’s/anthropicroute reads the bearer header, so the credential must be inANTHROPIC_AUTH_TOKEN, notANTHROPIC_API_KEY. If the error names an invalid or unrecognized token even though the key is correct, check which of the two variables is actually set.400naming an output-token or max-tokens field.CLAUDE_CODE_MAX_OUTPUT_TOKENSis unset or set above the route’s current cap. Set it to32000, or to whatever value the error response itself names as the limit.400naming a beta or experimental header. ConfirmCLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1is set in the sameenvdict (or shell environment) the failing call used.404or a model-not-found error. Check the model id for typos first;claude-sonnet-5is case-sensitive and must match exactly. If the id is correct, confirm it’s one of the models NativePort’s Anthropic route currently serves on the provider page.- Connection errors, timeouts, or DNS failures. Confirm outbound access to
api.nativeport.aifrom wherever the script runs. IfCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICisn’t set, also allowapi.anthropic.com, since background version and telemetry calls go there by default even when the model call itself is configured correctly.
What this costs
Claude models through this route are billed at Anthropic’s own metered rate, including prompt-cache pricing tiers read directly from each response rather than recomputed separately, with no per-call markup from NativePort. A zero balance fails every request with a 402 rather than degrading output quality or silently queuing. See pricing for the full billing model.
Where to go next
- Anthropic provider page: current Claude model list and per-model pricing on this route.
- Claude Agent SDK overview: the full option set beyond what this guide covers, including sessions, hooks and MCP server support.
- Pricing: the full billing model.