NativePort
← How-to

How to Use the Anthropic Python SDK with NativePort

Point the official anthropic Python package's base_url and auth_token at NativePort's native Anthropic route and call Claude's Messages API directly — the one header swap that makes it work, plus the field policy and errors specific to this gateway.

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_key sends the value as an x-api-key header. This is the SDK’s default and what Anthropic’s own docs lead with.
  • auth_token sends the value as an Authorization: Bearer header 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_servers is 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_tier accepts only "auto" or "standard_only"; "priority" is never a valid request value here.
  • max_tokens is capped; a value above the gateway’s current ceiling (32000 at this writing — the response names the live number if it’s changed) is a 400, not a silent clamp.
  • tools[] is checked item by item: a client-defined tool (one with no type field, just name/description/input_schema) passes through normally; a server tool (one with a type) is only admitted if that type starts with web_search_, web_fetch_ or computer_code_execution and the MCP tool-search types are rejected the same way mcp_servers is.
  • 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 under metadata passes 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."} — the Authorization: Bearer <NATIVEPORT_API_KEY> header is missing, malformed, or doesn’t resolve to an account (or you passed api_key instead of auth_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 used mcp_servers, speed, a service_tier other than auto/standard_only, a disallowed server tool type, or another field this route closes off.
  • 400 / gateway_output_cap: max_tokens exceeds 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 — check GET /anthropic/v1/models.
  • 400 / gateway_beta_not_allowed: an anthropic-beta header named a flag outside this route’s allowlist. The SDK doesn’t set beta headers on its own for a plain messages.create() call, so this only comes up if you set one explicitly.
  • 429: rate-limited, with a retry-after header 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