Docs
tcpapi speaks the OpenAI and Anthropic APIs. Point an existing client at https://api.tcpapi.com/v1
and use your key.
Quick start
from openai import OpenAI
client = OpenAI(base_url="https://api.tcpapi.com/v1", api_key="YOUR_KEY")
stream = client.chat.completions.create(
model="tinylm-a",
messages=[{"role": "user", "content": "Hello!"}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.tcpapi.com/v1", apiKey: "YOUR_KEY" });
const r = await client.chat.completions.create({
model: "tinylm-a",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(r.choices[0].message.content);curl https://api.tcpapi.com/v1/chat/completions \
-H "Authorization: Bearer $TCPAPI_KEY" \
-H "X-Limit-Price: 1.00" \
-d '{"model":"tinylm-a","max_tokens":200,"stream":true,
"messages":[{"role":"user","content":"Hello!"}]}'Endpoints
POST /v1/chat/completions | OpenAI chat format, streaming or not. |
POST /v1/messages | Anthropic Messages format (x-api-key header works too). |
GET /v1/models | Models on offer. |
Price and speed limits
Every request is an order on the model's book. These optional headers set its limits; the request takes the cheapest offer that meets all of them, or fails fast with 503 if none does.
| Header | Meaning |
|---|---|
X-Limit-Price | The most you will pay, USD per million output tokens. Default: the model's list price. |
X-Max-TTFT-Us | Longest acceptable wait for the first token, in microseconds. A seller that misses it is replaced by a backup. |
X-Min-TPS | Slowest acceptable output speed, tokens per second. A seller that falls below it mid-answer is replaced. |
Streaming and receipts
Streams are standard server-sent events. With stream_options.include_usage
the last chunk carries usage. Every stream ends with a comment line:
: aethelgard receipt_blake3=… tokens=58 prompt_tokens=12 billed_units=61 failovers=0 price_usd=0.62
The receipt is a BLAKE3 hash over every token and its arrival time. Non-streamed replies carry the same fields in an
aethelgard object, and response headers name the match and the price.
Billing
Output tokens are billed at the matched price per million; input tokens count at the model's input weight. Before a request starts, its maximum cost (max_tokens plus the input) is reserved from your balance; you are charged only for what you receive and the rest is released. If a backup finishes an answer, you still pay the original price: the defaulting seller covers the difference.
Errors
| Status | Meaning |
|---|---|
| 400 | Invalid request, or refused by the model (for example longer than its context). |
| 401 | Missing or unknown API key. |
| 402 | Balance too low for this request's maximum cost. |
| 429 | Too many requests open at once on your account (16). |
| 503 | No seller meets your limits right now: retry, or relax the limits. |
Not supported yet
Tool calling, images and audio, n > 1 and logprobs are refused
with a clear 400 rather than silently ignored.