API
Models
The live catalog, how we bill per token, and how TH Orchestra routes.
Models
Every model wired into Token Harbor is listed on /models. One Universal Key gets you all of them — no per-vendor signup, no per-vendor balance to keep topped up.
TH Orchestra — th-orchestra
Plan → build → review — for agentic coding. th-orchestra is built for tool-using agent clients (Claude Code, Cursor, opencode, Codex). When a request carries a tools array, every turn is classified server-side and dispatched to one of four model pools, so a coding task gets planned, built, and reviewed without you juggling models. The model you pay for is the upstream model that actually answered — there is no Orchestra surcharge.
| Stage | Pool | When it fires | Model class |
|---|---|---|---|
| Plan | planner | The opening turn of a task, "design / outline a plan", and the review pass | Premium reasoning model (vision-capable) |
| Build | coder | "fix / implement / refactor", UI work, debugging, searching — edits + runs commands | Strong agentic coding model (vision-capable) |
| Compact | summarizer | Auto-compaction under context-window pressure | Fast summarization model |
| Chat | chat | A turn with no tools and no task to execute — ordinary conversation | Fast, inexpensive general model |
The classifier still recognises finer intents (frontend, debugger, fetcher, reviewer), but they fold onto these pools: frontend / debugger / fetcher run on the build executor, and the review pass runs on the planner.
Plain chat (no tools). A request without a tools array is an ordinary conversation, not an agent loop — so it skips the plan/build/review pools and runs on the chat pool: one predictable, inexpensive model rather than a difficulty-graded lottery. Send tools to engage the full plan → build → review routing.
Images, documents, audio and video. A request carrying non-text content is always routed to a model that actually accepts that input type — never one that would silently ignore it. Capability is read from the live model catalogue (the same modality icons shown on /models), not inferred from the model's name, so a newly-added multimodal model is eligible the moment its capabilities are published. If the pool your turn would normally use can't read the attachment, Orchestra borrows a model that can, and keeps capable models first in the fallback order so a mid-stream retry can't drop onto one that isn't.
Session stickiness. Within one agent session the build stays pinned to a single executor model unless a clearly different stage (plan or review) is detected. Switching every turn would cold-start the vendor's prompt cache; holding it steady keeps that cache warm so your repeated context bills at the cached rate.
The classifier is multilingual and inspects the last tool call when you're mid-loop, so multi-turn agent flows like Cursor / opencode / Claude Code stay coherent between turns.
Use it from any OpenAI-compatible client — this bare example has no tools, so it's routed as a plain chat:
curl https://tokenharbor.ai/v1/chat/completions \
-H "Authorization: Bearer $TH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"th-orchestra","messages":[{"role":"user","content":"fix the off-by-one in parser.ts"}]}'
An agent client (Claude Code, Cursor, Codex) sends the same call with its tools, which is what engages the plan → build → review pools and the role-specific system addendum. Trace each call's classified role + the upstream model that answered on /dashboard/usage.
How we bill
Metered per token, no flat per-turn fee. Every call costs:
cost = (input_tokens × upstream_in_per_1m + output_tokens × upstream_out_per_1m)
/ 1,000,000
× (1 + markup_pct / 100)
upstream_*_per_1m— the vendor's published input/output price for the model. Visible on every /models card.markup_pct— Token Harbor's per-model markup over upstream. Default is 0% today.
There is no volume discount and no per-turn orchestration fee: the per-token price on the model's card is the whole price.
What counts as input_tokens. Token counts come from the upstream provider's own usage report on the final request body.
- Direct model calls — when you name a specific model id, your
systemandmessagesare forwarded byte-for-byte. The gateway adds nothing, so you are billed for exactly the tokens you sent plus the tokens the model returned. - Agentic
th-orchestracalls — the ones that sendtools— carry a role-specific system addendum for the pool the turn was routed to. Those tokens are billed at the model's normal input rate, because the upstream charges us for them. A plain no-tools chat gets no addendum.
Three caching layers further reduce real billed amount:
| Layer | Triggered when | What it saves |
|---|---|---|
| Upstream cache (vendor side) | Long repeated prefixes (≥1024 tokens). Marked with the Upstream cache badge on /models. | ~90% off the cached input portion. |
| Semantic cache (Token Harbor) | Same/very-similar question seen recently. | 100% — billed $0, no upstream call. |
| Exact cache (Token Harbor) | Same model + same messages + same sampling within 5 minutes. | 100% — billed $0, no upstream call. |
Cache savings show as a green dollar line on /dashboard/usage under Today's Usage.
Knowing what you're paying
- /models shows every model's live price, release date, and knowledge cutoff.
- /v1/models returns the same data as JSON for SDKs that pre-fetch the catalog.
- /dashboard/usage shows your last 100 requests with the exact tokens-in / tokens-out / cost-paid for each one. Export to CSV from there.
Bypassing or forcing cache
Per-request control via header:
| Header | Behaviour |
|---|---|
X-TH-Cache-Control: bypass | Skip lookup. The response is still written into the exact cache so the next identical request can hit. |
X-TH-Cache-Control: force-refresh | Skip lookup AND skip write. Pure passthrough — useful for testing/fresh-roll scenarios. |
When a response is served from cache, the route adds X-TH-Cache-Layer: exact (or semantic) so your client can tell.
Context limits
We never truncate your conversation. Each request goes straight to the upstream; if the upstream refuses with context_length_exceeded, that error is forwarded verbatim.