Point your existing SDK at our base URL and change the key. Everything on this page is the contract: the same prices, the same multiplier and the same usage fields we bill from.
| api.tokentoken.ai | Private preview |
| app.tokentoken.ai console | Private preview |
| Published prices & rate | Live |
| Skill files | Live |
Honest status, not a roadmap promise. See the notice below.
The inference API is not open for self-service yet. Today https://api.tokentoken.ai answers every path with 503 and a JSON body saying so — it is reserved, not running. The endpoints below are the contract we are building to, published early so you can review it, estimate cost and tell us what is missing.
If you are an existing relay customer (the New API service), your base URL is https://tokenindex.ai/v1 and your existing sk- key is unchanged. That service is separate from this one: different accounts, different balance, different upstreams.
Want access when it opens? sales@cflabs.ai — tell us the models and the rough monthly volume.
One bearer token on every request. Keys are shown once at creation and stored only as a hash, so we cannot recover one for you — rotate instead.
POST https://api.tokentoken.ai/v1/chat/completions Authorization: Bearer tt-... Content-Type: application/json
Every key starts with tt-. The console shows the prefix and the creation date, never the secret.
A model allowlist, a spend cap, RPM/TPM, a concurrency limit, an expiry and an optional IP allowlist. All enforced server-side.
Keys belong to an organization, not a person. Revoking a key never touches the balance or the other keys.
The wire format is OpenAI's. Any client that can take a base URL works unchanged — the official openai SDKs, LangChain, LlamaIndex, Codex CLI, Cline, Aider.
curl https://api.tokentoken.ai/v1/chat/completions \
-H "Authorization: Bearer $TOKENTOKEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "tt/kimi-k3.1-pro",
"messages": [{"role": "user", "content": "Say hello in five words."}],
"max_tokens": 64
}'
from openai import OpenAI
client = OpenAI(
base_url="https://api.tokentoken.ai/v1",
api_key=os.environ["TOKENTOKEN_API_KEY"],
)
resp = client.chat.completions.create(
model="tt/kimi-k3.1-pro",
messages=[{"role": "user", "content": "Say hello in five words."}],
max_tokens=64,
)
print(resp.choices[0].message.content)
print(resp.id) # keep this: it is the id on your invoice line
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.tokentoken.ai/v1",
apiKey: process.env.TOKENTOKEN_API_KEY,
});
const resp = await client.chat.completions.create({
model: "tt/kimi-k3.1-pro",
messages: [{ role: "user", content: "Say hello in five words." }],
max_tokens: 64,
});
Streaming: pass stream: true. You do not need stream_options for usage — we obtain usage from the upstream ourselves, so billing never depends on the client asking for it.
Self-hosted routes are prefixed tt/. A pinned id keeps serving the same weights, the same quantization and the same context window — we do not silently upgrade the thing behind an id. A new revision gets a new id.
| Field | Meaning |
|---|---|
| id | What you send in model. |
| source_label | Where it runs: own_gpu, official, cloud, licensed_third_party. Returned on every response, not just in the catalogue. |
| context | Maximum context window in tokens. |
| quantization | The exact weight format being served. |
Prices are published per million tokens. The charge for one call is an exact integer computation — we do not use floating point anywhere in the billing path, and we do not use the gateway's own cost estimate, because it ignores cache discounts.
charge = round_half_up(
( prompt_uncached × input_price
+ cached_tokens × cache_read_price
+ completion × output_price ) × rate
)
prompt_tokens includes cache hits. We subtract cached_tokens and price those at the cache-read rate, so a cache hit is never billed at full input price.
If we retry an upstream or fail over to another route, you are charged once, for the attempt that succeeded. The failed attempts are our cost, not yours.
Currently 1.00. GET /v1/pricing returns it with a version, and every invoice line records the version it was billed at. Changing it never reprices history.
GET /v1/pricing → { rate, rate_version, currency, models: [...] }
GET /v1/usage?from=&to= → one row per call: request_id, model, tokens, charge, rate_version
GET /v1/credits → { balance, currency, as_of }
Every row carries the request_id that the API returned, so any line on an invoice can be traced back to the call that produced it.
Error bodies are OpenAI-shaped: {"error":{"type","code","message"}}. The codes below are ours and are stable.
| Status | code | What it means and what to do |
|---|---|---|
| 401 | invalid_api_key | Unknown, revoked or expired key. Not retryable. |
| 402 | insufficient_credits | Balance below the estimated cost of this call. Nothing was reserved and nothing was charged. Top up, then retry. |
| 403 | model_not_allowed | The model is not on this key's allowlist. |
| 403 | ip_not_allowed | Source IP outside this key's allowlist. |
| 400 | parameter_out_of_range | A billable multiplier exceeded its cap — max_tokens, image n, video seconds, batch size. Caps exist so a bad value cannot produce a runaway charge. |
| 429 | rate_limited | This key's RPM, TPM or concurrency limit. Honour Retry-After. |
| 502 | upstream_failed | All routes for this model failed. The reservation was released; you were not charged. |
| 503 | not_launched | What the API returns today. See the notice at the top of this page. |
If your client disconnects mid-stream, the tokens already produced upstream are real and are billed; the record is marked client_disconnected so you can find it.
Machine-readable catalogue, installable skill files, and per-key limits an agent cannot talk its way around because we enforce them server-side.
support@cflabs.ai for integration questions, sales@cflabs.ai for access and volume, security@cflabs.ai for anything security-related. Data handling, retention and attestation are on the Trust page.