Migrate from OpenAI or Anthropic SDKs
Point the official OpenAI or Anthropic SDK at TokenTriage with a single base-URL change — nothing else in your code moves, and you get a costed ledger plus optional routing for free.
Last updated
On this page
If your app already calls the model provider through the official OpenAI or Anthropic SDK, this is the smallest possible migration: change the SDK’s base URL to point at a TokenTriage proxy. You keep the same SDK, the same calls, the same API keys — the proxy speaks each provider’s native dialect, so the request and response bytes are unchanged.
The one change
TokenTriage’s data plane listens on 127.0.0.1:8787 by default. Each SDK gets a different
base URL because each speaks a different dialect:
- OpenAI SDK → the proxy’s
/v1prefix (http://127.0.0.1:8787/v1). The SDK appends/chat/completions, so requests land on/v1/chat/completions, which the OpenAI dialect owns. - Anthropic SDK → the proxy root (
http://127.0.0.1:8787). The Anthropic SDK appends/v1/messagesitself, and the Anthropic dialect owns/v1/messages— so you give it the root, not/v1.
Migrate the OpenAI SDK
First run a ledger proxy with an OpenAI upstream. Drop this into tokentriage.yaml:
version: 1
mode: ledger # observe and cost every request; mutate nothing
upstreams:
openai:
base_url: https://api.openai.com/v1
dialect: openai
api_key_env: OPENAI_API_KEY
$ tokentriage run -c tokentriage.yaml
Then change one line in your app.
from openai import OpenAI
# BEFORE: client = OpenAI() # talks straight to api.openai.com
# AFTER — the only change: base_url points at the proxy's /v1 dialect prefix.
client = OpenAI(base_url="http://127.0.0.1:8787/v1")
resp = client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": "Summarize this ticket."}],
)import OpenAI from "openai";
// BEFORE: const client = new OpenAI(); // talks straight to api.openai.com
// AFTER — the only change: baseURL points at the proxy's /v1 dialect prefix.
const client = new OpenAI({ baseURL: "http://127.0.0.1:8787/v1" });
const resp = await client.chat.completions.create({
model: "gpt-5",
messages: [{ role: "user", content: "Summarize this ticket." }],
});curl
$ curl http://127.0.0.1:8787/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{"model":"gpt-5","messages":[{"role":"user","content":"Hello"}]}'
Migrate the Anthropic SDK
For an Anthropic upstream (ideal for Claude Code), the zero-config quickstart is enough — no config file needed:
$ tokentriage run --anthropic
Then point the Anthropic SDK at the proxy root:
from anthropic import Anthropic
# BEFORE: client = Anthropic() # talks straight to api.anthropic.com
# AFTER — the only change: base_url is the proxy ROOT, not /v1. The SDK appends /v1/messages.
client = Anthropic(base_url="http://127.0.0.1:8787")
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=256,
messages=[{"role": "user", "content": "Summarize this ticket."}],
)import Anthropic from "@anthropic-ai/sdk";
// BEFORE: const client = new Anthropic(); // talks straight to api.anthropic.com
// AFTER — the only change: baseURL is the proxy ROOT, not /v1.
const client = new Anthropic({ baseURL: "http://127.0.0.1:8787" });
const resp = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 256,
messages: [{ role: "user", content: "Summarize this ticket." }],
});curl
$ curl http://127.0.0.1:8787/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-sonnet-4-6","max_tokens":256,"messages":[{"role":"user","content":"Hello"}]}'
What you get for the one-line change
Nothing about your responses changed — but now every request is on the ledger:
-
A costed row per request. TokenTriage stamps an
x-tt-request-idresponse header on every proxied call; that’s the join key into a ledger row with the real dollar cost, computed from the compiled-in pricing snapshot. Roll it up from the terminal:$ tokentriage top --by modelA model the server can’t price shows a
-in theCOSTcolumn, with a footnote stating how many rows were excluded because no price resolved — never a fabricated$0. -
Optional trace grouping. Add
x-tt-trace-id/x-tt-agentheaders later to group a run or split spend by sub-agent. They’re optional — a request with nox-tt-*headers is still fully costed; it just lands as a standalone row. -
A path to savings with zero further code change. Everything above is
ledgermode: pure observation. When the evidence says a cheaper model is genuinely as good, you climb toshadowand thenroute— enforcing routing decisions in the proxy config, without touching your application again.