Skip to main content
The Python SDK is a thin convenience layer: it points your OpenAI client at the Orbitrage gateway and tags every request with a trace id. No runtime dependencies, no background threads, no span exporters. Orbitrage is OpenAI-format only — to use Claude, Gemini, Grok, etc., just name the model; you don’t use that provider’s SDK.

PyPI

pip install orbitrage

License

Apache-2.0 · Python 3.9–3.13

Install

pip install -U orbitrage
openai (>=1.0) is the only peer you need — Orbitrage speaks OpenAI format:
pip install -U orbitrage openai

Initialize

Call init() once, at the very top of your program, before importing your LLM client.
import os
import orbitrage

orbitrage.init(os.environ["ORBITRAGE_API_KEY"])

from openai import OpenAI
client = OpenAI()        # base_url + trace headers are set for you
client.chat.completions.create(
    model="grok-4-fast",
    messages=[{"role": "user", "content": "hi"}],
)

What init() does

  1. Generates a stable trace id for the process and adds it to every request as x-orbitrage-run-id, so all your calls group into one run.
  2. Patches the OpenAI and AsyncOpenAI constructors to default base_url to https://api.orbitrage.ai/v1 and inject the trace headers. Your explicit base_url or api_key always wins.
  3. Sets OPENAI_BASE_URL as a safety net for clients constructed with no arguments.
It does not load OpenTelemetry or Traceloop, and it exports nothing — the gateway is the source of truth.

init() parameters

api_key
str
required
Your orb_ key. Falls back to ORBITRAGE_API_KEY if omitted.
user_id
str
End-user id to attribute calls to (sets x-orbitrage-end-user-id).
base_url
str
Override the gateway URL. Defaults to https://api.orbitrage.ai/v1 (or ORBITRAGE_BASE_URL).
enabled
bool
default:"True"
Set False to make init() a no-op (e.g. in tests).
quiet
bool
default:"False"
Suppress the startup log line.
init() is idempotent — calling it more than once is safe and has no effect after the first call. Legacy v0.4 keyword arguments (disable_batch, capture_content, instruments, …) are accepted and ignored.

Per-end-user attribution

# One user for the whole process:
orbitrage.init(api_key, user_id=current_user.id)

# Switch users per request in a long-running server.
# The next OpenAI() client you construct picks up the new id:
orbitrage.set_user(current_user.id)
After set_user(), construct a new client for the change to take effect — already-built clients have already copied their headers.

Forcing a model

# Pin a model (no scoring):
client.chat.completions.create(model="claude-sonnet-4-6", messages=[...])

# Route to the cheapest capable model:
client.chat.completions.create(model="auto", messages=[...])
auto, router, default, and orbitrage all trigger routing. See Routing.

API surface

SymbolPurpose
init(api_key, *, user_id=None, base_url=None, enabled=True, quiet=False)Set up the SDK.
set_user(user_id)Bind/unbind the end-user id for subsequent clients.
set_association_properties(props)v0.4 compat — extracts user_id and calls set_user.
flush(timeout_ms=5000) / shutdown()No-ops — the gateway persists synchronously.
workflow, task, tool, agentNo-op decorators kept for v0.4 compatibility.
The decorators and lifecycle functions exist so code written for v0.4 keeps importing without changes. The gateway captures the same data regardless of decorator markup.

Observability-only mode

You don’t have to let Orbitrage change your model. Pin explicit models (or use BYOK) and you still get full observability — every call is traced because it flows through the gateway:
import orbitrage
orbitrage.init(os.environ["ORBITRAGE_API_KEY"])  # trace everything, route nothing

Using a framework?

LangChain, LangGraph, CrewAI, Agno, and others use the OpenAI client under the hood, so they’re captured transparently. See Integrations for per-framework setup.