tcpapi

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/completionsOpenAI chat format, streaming or not.
POST /v1/messagesAnthropic Messages format (x-api-key header works too).
GET /v1/modelsModels 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.

HeaderMeaning
X-Limit-PriceThe most you will pay, USD per million output tokens. Default: the model's list price.
X-Max-TTFT-UsLongest acceptable wait for the first token, in microseconds. A seller that misses it is replaced by a backup.
X-Min-TPSSlowest 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

StatusMeaning
400Invalid request, or refused by the model (for example longer than its context).
401Missing or unknown API key.
402Balance too low for this request's maximum cost.
429Too many requests open at once on your account (16).
503No 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.