The Anthropic Python SDK (pip install anthropic) is a thin client over POST /v1/messages: you build the request, Anthropic’s own tokenizer and model decide the rest, and the response comes back as a typed Message object. NativePort’s /anthropic route serves that exact same request and response shape — Claude on the same key and balance as every other provider here, billed from your NativePort balance instead of a separate Anthropic account. If what you actually want is Claude Code’s own multi-step agent loop rather than a single call you drive yourself, see using the Claude Agent SDK with NativePort instead — that’s a different package solving a different problem, built on top of this same route.
What you’ll need
- A NativePort API key. Sign up to get a key and $5 in credits.
- Python 3.8 or later.
pip install anthropic(0.120.2 is current as of this writing).- Your key exported as an environment variable, never hardcoded:
export NATIVEPORT_API_KEY="np_..."
The one header swap this route needs
The Anthropic client accepts two different, mutually exclusive credential parameters, and only one of them works here:
api_keysends the value as anx-api-keyheader. This is the SDK’s default and what Anthropic’s own docs lead with.auth_tokensends the value as anAuthorization: Bearerheader instead.
NativePort’s gateway authenticates every request against its own credit ledger by reading the Authorization header specifically, before the request ever reaches the Anthropic-specific proxy logic — and once it does reach that logic, both x-api-key and Authorization are stripped from what you sent and replaced with the gateway’s own real Anthropic credential, so neither one ever carries your key to Anthropic itself. Pass your key via api_key, and the gateway’s own auth check finds no Authorization header and rejects the call with a 401 before your key or the request body matter at all. Pass it via auth_token, and it lands in the one header the gateway’s gate is actually checking.
Minimal example
import os
from anthropic import Anthropic
client = Anthropic(
auth_token=os.environ["NATIVEPORT_API_KEY"],
base_url="https://api.nativeport.ai/anthropic",
)
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(message.content[0].text)
max_tokens is required here for the same reason it’s required against Anthropic directly — it’s not something this gateway adds, it’s the Messages API’s own contract, and the SDK validates its presence client-side before a request is ever sent.
Streaming
client.messages.stream(...) works the same way it does against Anthropic directly — the gateway forwards Anthropic’s own SSE stream rather than buffering it:
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Count to five."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Model selection
base_url changes where the request goes, not which models exist behind it — model still has to name one this route actually serves. GET /anthropic/v1/models lists the current admitted set (equivalently, client.models.list()); check the Anthropic provider page for the same list with pricing attached before picking an id for anything beyond a quick test, since which Claude models are GA — and which superseded ones have aged out — changes over time. Both model-lookup paths are answered locally by the gateway’s own catalog rather than round-tripped to Anthropic, and they disagree on status code for the same reason it’s asking two different questions: POST /v1/messages with a model outside the catalog is a 400 naming model_not_available (a real Claude model, just not one this route serves), while GET /anthropic/v1/models/{id} for that same id is a 404 naming model_not_found (this endpoint doesn’t recognize the id at all, valid or not).
What this route accepts and what it doesn’t
/v1/messages and /v1/messages/count_tokens take Anthropic’s own request body, with a fixed set of fields closed off:
mcp_serversis rejected outright — the MCP connector is a credential-relay risk this gateway doesn’t take on.speed(fast mode) is rejected — not enabled on this gateway build.service_tieraccepts only"auto"or"standard_only";"priority"is never a valid request value here.max_tokensis capped; a value above the gateway’s current ceiling (32000 at this writing — the response names the live number if it’s changed) is a400, not a silent clamp.tools[]is checked item by item: a client-defined tool (one with notypefield, justname/description/input_schema) passes through normally; a server tool (one with atype) is only admitted if that type starts withweb_search_,web_fetch_orcomputer_—code_executionand the MCP tool-search types are rejected the same waymcp_serversis.metadata.user_id, if you set it, is silently overwritten with a value the gateway derives from your account — per-tenant attribution for abuse enforcement is the gateway’s to assert, not the client’s. Any other key undermetadatapasses through untouched.
Beyond /v1/messages, the route also supports the Files API (/v1/files) and Message Batches (/v1/messages/batches), each scoped so an account only ever sees resources it created itself — the upstream list endpoints aren’t proxied directly, since that would enumerate every tenant sharing the underlying key.
Errors you’ll actually hit
Two distinct error shapes show up here, depending on which layer rejects the request. A failure at the gateway’s own credit gate — before anything Anthropic-specific runs — is a bare {"error": "..."} body:
402 Payment Required:{"error": "Insufficient credits. Top up to continue."}— the NativePort balance is at $0.401 Unauthorized:{"error": "Unauthorized."}— theAuthorization: Bearer <NATIVEPORT_API_KEY>header is missing, malformed, or doesn’t resolve to an account (or you passedapi_keyinstead ofauth_token— see above).403 Forbidden:{"error": "Account suspended."}— the key is valid but the account isn’t active.
Once a request clears that gate, a rejection carries Anthropic’s own error envelope instead — {"type": "error", "error": {"type": ..., "message": ..., "code": ...}, "request_id": "req_..."} — with a code naming the specific gateway rule:
400/gateway_field_not_allowed: the request usedmcp_servers,speed, aservice_tierother thanauto/standard_only, a disallowed server tooltype, or another field this route closes off.400/gateway_output_cap:max_tokensexceeds the current cap; the message names the exact ceiling.400/model_not_available: the model id is real but not in this route’s admitted set — checkGET /anthropic/v1/models.400/gateway_beta_not_allowed: ananthropic-betaheader named a flag outside this route’s allowlist. The SDK doesn’t set beta headers on its own for a plainmessages.create()call, so this only comes up if you set one explicitly.429: rate-limited, with aretry-afterheader naming the wait.502/gateway_upstream_auth: an internal credential issue on the gateway’s side, not something the client caused — operators are alerted automatically.
Security
Treat NATIVEPORT_API_KEY like any other credential: environment variable or secret store, never a literal string in source, never logged — including in whatever wraps the SDK’s own request/response logging if you enable it.
What this costs
Claude models through this route are billed at Anthropic’s own metered rate, read directly from each response’s real usage — including the cache-write and cache-read tiers, which price differently from a fresh input token — with no per-call markup from NativePort. Per-model rates vary by tier; see the Anthropic provider page for the current table. A zero balance fails every request with a 402 rather than degrading. Adding credit carries a flat 5.5% fee on top-ups between $10 and $5,000 — never on the calls themselves. Full breakdown: pricing.
Where to go next
- Anthropic provider page: current Claude model list and per-model pricing on this route.
- Using the Claude Agent SDK with NativePort: the same route, driven by Claude Code’s own agent loop instead of a single
messages.create()call. - Pricing: the full billing model.