Docs
An OpenAI-compatible endpoint. If your tool takes a base URL and a key, it already works: this is the exact configuration, not a simplified version of it.
Base URL & auth
One base URL, bearer auth with your API key. The endpoint speaks the OpenAI chat-completions and completions APIs.
https://api.tium.ai/v1
Authorization: Bearer sk-tium-...Get a key from your dashboard. Keys are shown once at creation; store them in an environment variable, never in source.
Making a request
The same call three ways. Any slug from /models drops into the same field.
curl https://api.tium.ai/v1/chat/completions \
-H "Authorization: Bearer $TIUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "glm-5.3-flash",
"messages": [{"role": "user", "content": "Say hello."}]
}'Configuring a tool rather than writing code? Every one of these has a setup page with its config, where the key belongs, and what that tool gets wrong.
- AiderAI pair programming in your terminal, pointed at Tium with two environment variables.
- OpenCodeA terminal coding agent that takes a custom provider block in opencode.json.
- PiPoint Pi at the gateway with a models.json entry and an environment variable.
- ClineThe VS Code agent, configured through its settings panel rather than a file.
All 15 setup guides, and anything else that takes an OpenAI-compatible base URL.
What every response tells you
Each response carries what it cost, what remains, and your limits. You never have to take a number on trust.
| X-Tium-Request-Id | Correlation id for support. Present on every response, including errors. |
| X-Tium-Tokens-In / -Cached / -Out | The token counts this request was billed on (non-streamed responses). |
| X-Tium-Weighted-Tokens | Weighted tokens charged: the token counts times the published weights and model multiplier. |
| X-Tium-Model-Multiplier | The multiplier applied, captured at request time. What you see is what you were charged. |
| X-Tium-Output-Weight | The output-token weight used for this model, captured the same way. |
| X-Tium-Balance-Remaining | Weighted tokens left (allotment + credits) at the start of the request. |
| X-Tium-Concurrency-Limit / -Remaining | Your tier's concurrency limit and how many slots were free. |
On streamed responses the X-Tium-Tokens-* and X-Tium-Weighted-Tokens headers are absent, because the counts do not exist yet when headers are sent. Instead we set stream_options.include_usage on every streamed request, so the stream ends with a standard OpenAI usage chunk carrying the token counts. We set it whether or not you asked for it; that is how the request gets billed correctly. Identity, balance and concurrency headers are sent up front as usual.
Error codes, and which to retry
The distinction that matters for agents: 402 is a wall (out of balance) and must not be retried; 429 is backpressure (too many at once) and should be retried after a short wait.
| HTTP | error.type | Meaning |
|---|---|---|
| 400 | invalid_request_body | The JSON body was malformed. Fix the request; do not retry as-is. |
| 400 | too_many_candidates | 'n' (or 'best_of') exceeds the per-request cap. The body carries the limit. |
| 401 | missing_api_key | No Authorization header. Send 'Authorization: Bearer sk-tium-...'. |
| 401 | malformed_authorization | The Authorization header is not of the form 'Bearer <api-key>'. |
| 401 | invalid_api_key / revoked_api_key | Key is wrong or has been revoked. |
| 402 | insufficient_balance | Allotment and credits are exhausted. Top up. Agents must NOT retry a 402. |
| 402 | key_spend_limit_reached | This key hit its per-key spend limit. Raise the limit or use another key. |
| 403 | model_not_permitted | The model exists; this key may not use it. Body carries available_models. |
| 404 | model_not_found | Unknown model slug. Body carries available_models; fall back to one of those. |
| 429 | concurrency_limit | Too many requests at once for your tier. Retry after a short wait; carries Retry-After. |
| 429 | daily_burst_cap_reached | Daily burst cap reached (abuse guard). Resets 00:00 UTC. |
| 502 | backend_unavailable | The inference backend is unreachable. Transient; safe to retry. |
| 502 | backend_error / bad_gateway | The backend answered with an error or a non-JSON body. Not billed for tokens. |
| 503 | model_unavailable | This model's provider is failing and was withdrawn. Retry-After carries the cooldown. |
| 503 | registry_unavailable | Server-side configuration problem, not your request. Retryable. |
Errors return an OpenAI-shaped body under error. Note which field is which: type is the slug above, code is the HTTP status as an integer.
| type | The error slug: the value in the table above. Switch on this. |
| code | The HTTP status, as an integer. Not a slug. |
| message | Human-readable, safe to surface to a developer. |
| request_id | Matches X-Tium-Request-Id. The one field support needs. |
| retryable | Boolean. Authoritative: prefer it over inferring from the status. |
| retry_after_ms | On 429s, the same interval as the Retry-After header, in milliseconds. |
| allotment_reset_at | On 402s, when the monthly allotment next refills. |
| top_up_url | On 402s, where to add credits. |
| available_models | On 403 and 404, the slugs this key can actually reach. |
| limit | On 429 concurrency_limit and 400 too_many_candidates, the bound that was hit. |
Fields beyond the first four appear only where they apply. The one worth wiring up is retryable: it is set deliberately per error rather than derived, so an agent that honours it gets the 402-vs-429 distinction right without encoding a table of statuses.
What the gateway changes about your request
Three request-shaping rules. The first one is silent, which is why it leads.
max_tokens is clamped to what you can afford
Before dispatch we reserve the prompt’s cost, then cap output at what the remaining balance buys at this model’s rate. Your effective ceiling is the lowest of the max_tokens you asked for, the model’s published output ceiling, and that affordable number. If the affordable number is lower, the response is truncated, not refused: you get a normal completion with finish_reason: "length". Watch X-Tium-Balance-Remaining if short completions start appearing. If the balance cannot cover even the prompt, the request is refused with a 402 and billed nothing.
n and best_of are capped
Parallel completions multiply generation, so they are settled at the door: over the cap you get a 400 too_many_candidates with the exact limit in limit. Send the requests separately instead.
stream_options.include_usage is forced on
On streamed requests, so the trailing usage chunk exists and the request can be billed on measured tokens rather than an estimate. Any other stream_options you send are preserved.
Everything else in the request body is passed through to the backend unchanged, including tools, temperature and stream. Both /v1/chat/completions and /v1/completions are served.
GET /v1/models
Authenticated, and served from our registry rather than proxied from an upstream catalog: it lists what we can actually route and price.
curl https://api.tium.ai/v1/models \
-H "Authorization: Bearer $TIUM_API_KEY"Each entry is an OpenAI model object with our own fields alongside it, so a client can price and size a request before sending it:
| id | The published slug: what you send as 'model'. |
| context_window | Context tokens served for this model. |
| tium_max_output_tokens | The output ceiling we enforce, before the affordability clamp. |
| tium_multiplier | This model's price multiplier, the same number the response header reports. |
| tium_output_weight | The output-token weight used in the weighted-token formula. |
| tium_deprecated | Present and true only while a model is on its way out. |
| tium_retires_at | Present alongside it: when the slug stops being served. |
The two deprecation fields are absent rather than null when a model is not deprecated. A null on every model trains clients to ignore them. When they do appear, the model is still served and still billed; it is telling you to migrate before the day it starts returning 404.
The same catalog, with every model’s context, output ceiling, multiplier and published weights, is on /models.
The rest of the documentation
Three things that are their own pages because each answers a question somebody arrives with, rather than one they run into halfway down this one.
What we measured
Behaviour we found by sending a request rather than reading a datasheet: thinking that cannot be disabled, effort levels that do nothing, a forced tool call that is refused. Each names its probe and the date it ran.
Prompt caching
The largest lever on what an agent costs, with the numbers off a real session rather than a benchmark we designed.
What a weighted token is
The whole formula, why the unit exists, and what the trial buys, computed from the catalog rather than typed.