The official Python SDK for Routeplane. It meets you at whatever level of integration you want: change one line and keep the stock OpenAI client, add typed steering headers to any client, or use the full Routeplane client for auth, default routing, and typed response metadata. Requires Python 3.9+.
pip install routeplane
Minimal — just change base_url
If you already use the OpenAI SDK, point it at the gateway and pass your virtual key. Nothing else changes.
import openai
client = openai.OpenAI(
api_key="rp_your_gateway_key",
base_url="https://api.routeplane.ai/v1",
default_headers={"x-routeplane-api-key": "rp_your_gateway_key"},
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
Steer any client with headers()
Routeplane's per-request behaviour is configured through x-routeplane-* headers. headers() is a typed builder for them: it emits only the options you set, JSON-serializes dict values (config, metadata), and stringifies ints (timeout_ms). Because it returns a plain dict, it drops into any client's extra-headers escape hatch — not just this SDK.
import openai
from routeplane import headers
client = openai.OpenAI(
api_key="rp_your_gateway_key",
base_url="https://api.routeplane.ai/v1",
default_headers={"x-routeplane-api-key": "rp_your_gateway_key"},
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Summarize this contract."}],
extra_headers=headers(
provider="anthropic,openai", # fallback chain
strategy="cost", # cheapest eligible provider first
residency="IN", # keep Indian PII in-region
use_case="contract-summary", # shows up in FinOps
),
)
All 17 request headers are typed. See the reference below.
The Routeplane client
Routeplane subclasses openai.OpenAI, so everything works exactly as before — but it wires up auth, lets you set default routing once, and can parse the gateway's response headers into a typed RouteplaneMeta. Per-call extra_headers=headers(...) still override the client defaults.
from routeplane import Routeplane
client = Routeplane(
api_key="rp_your_gateway_key",
provider="openai,anthropic", # client-wide default fallback chain
strategy="latency",
residency="IN",
use_case="support-bot",
)
# Ordinary call — same OpenAI API you already know.
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hi"}],
)
Response metadata
create_with_meta returns the completion and a typed RouteplaneMeta decoded from the gateway's response headers — so you can see what the gateway actually did.
completion, meta = client.create_with_meta(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "What is DPDP?"}],
)
print(meta.provider) # which provider actually served it
print(meta.cache) # "hit" | "miss" | "bypass"
print(meta.budget_remaining) # spend headroom
print(meta.pii_masked) # was PII masked on the way out?
If you prefer to stay on the raw OpenAI API, client.meta_from_headers(raw.headers) parses the same metadata from a with_raw_response call. RouteplaneMeta exposes provider, trace_id, request_id, cache, guardrails, hedged, shed, budget_remaining, budget_warning, compliance_warning, pii_masked, and idempotent_replayed.
Streaming with metadata
stream_with_meta returns an iterable whose .meta is populated from the response headers — which arrive before the first chunk — so you can read which provider served the request immediately, then consume the stream.
stream = client.stream_with_meta(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Explain sovereign routing"}],
)
print(f"Served by: {stream.meta.provider}")
for chunk in stream:
content = chunk.choices[0].delta.content
if content:
print(content, end="")
Async
from routeplane import AsyncRouteplane
client = AsyncRouteplane(api_key="rp_your_gateway_key", strategy="cost")
resp = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello!"}],
)
Beyond OpenAI — resource namespaces
The OpenAI-shaped surfaces (chat, embeddings) come from the inherited client. Everything Routeplane adds on top is exposed as typed namespaces on the Routeplane client:
| Namespace | What it covers |
|---|---|
client.prompts | Fetch and render stored prompt templates. |
client.logs | Recent request logs. |
client.analytics | Recent usage analytics events. |
client.finops | FinOps usage and daily rollups. |
client.cache | Response-cache stats and purge. |
client.feedback | Attach quality scores to prior requests. |
client.residency | Sovereign-routing decisions and ledger. |
client.status | Gateway health and per-provider circuit state. |
client.rp_models | The gateway model catalog. |
client.rp_providers | Custom OpenAI-compatible providers. |
client.mcp_security | Agentic-security MCP runs. |
Why rp_models and rp_providers? The inherited OpenAI client already owns client.models, so the gateway's catalog is namespaced rp_models (and rp_providers, mcp_security) to avoid shadowing the base class.
Framework integrations
Because headers() returns a plain dict, it plugs into anything that forwards headers to the OpenAI API.
from langchain_openai import ChatOpenAI
from routeplane import headers
llm = ChatOpenAI(
model="gpt-4o-mini",
api_key="rp_your_gateway_key",
base_url="https://api.routeplane.ai/v1",
default_headers={
"x-routeplane-api-key": "rp_your_gateway_key",
**headers(provider="anthropic,openai", strategy="cost"),
},
)
from llama_index.llms.openai import OpenAI
from routeplane import headers
llm = OpenAI(
model="gpt-4o-mini",
api_key="rp_your_gateway_key",
api_base="https://api.routeplane.ai/v1",
default_headers={
"x-routeplane-api-key": "rp_your_gateway_key",
**headers(residency="IN", use_case="rag"),
},
)
from crewai import LLM
from routeplane import headers
llm = LLM(
model="openai/gpt-4o-mini",
base_url="https://api.routeplane.ai/v1",
api_key="rp_your_gateway_key",
extra_headers={
"x-routeplane-api-key": "rp_your_gateway_key",
**headers(strategy="latency", use_case="research-crew"),
},
)
Request headers reference
All are x-routeplane-* and all are optional except the API key. Build them with headers(...).
| Option | Header | Notes |
|---|---|---|
provider | x-routeplane-provider | Provider or comma-separated fallback chain. |
residency | x-routeplane-residency | Data-residency region (e.g. IN). |
strategy | x-routeplane-strategy | priority | weighted | cost | latency. |
config | x-routeplane-config | Inline routing config (JSON). |
timeout_ms | x-routeplane-timeout-ms | Upstream timeout, ms. |
use_case | x-routeplane-use-case | Analytics / FinOps label. |
log_level | x-routeplane-log-level | metadata | none | full. |
conversation_id | x-routeplane-conversation-id | Groups a conversation. |
currency | x-routeplane-currency | Cost-reporting currency. |
metadata | x-routeplane-metadata | Arbitrary tags (JSON). |
pii_mode | x-routeplane-pii-mode | tokenize. |
output_mask | x-routeplane-output-mask | Output masking policy. |
cache_control | x-routeplane-cache-control | no-store. |
idempotency_key | x-routeplane-idempotency-key | Safe-retry key. |
cohort | x-routeplane-cohort | Experiment cohort. |
batch | x-routeplane-batch | Batch id. |
trace_id | x-routeplane-trace-id | Client trace id (echoed back). |
Links
- routeplane on PyPI ↗
- routeplane-core/routeplane-python on GitHub ↗
- TypeScript SDK — the same design for Node.