Docs › API reference
API reference
Base URL https://api.tokenctr.com/v1. OpenAI-compatible, so existing SDKs work by changing the base URL and key.
Basics
| Base URL | https://api.tokenctr.com/v1 |
| Authentication | Send Authorization: Bearer ck_… on every call except the three marked “None” below. See Authentication. |
| Format | JSON requests and responses. Money is in US dollars; prices are per million tokens. |
| Errors | Non-2xx responses carry {"error": {"code": "…", "message": "…"}}. See Errors. |
| Rate limits | 60 requests per minute per key and 120 per minute per IP address on chat completions. |
| CORS | Browser requests are allowed, but never put a funded key in public front-end code. Call the API from your server. |
Endpoints
| Endpoint | Auth | Purpose |
|---|---|---|
POST /v1/chat/completions | Key | Create a chat completion. Details |
GET /v1/models | Key | List models and their prices. Details |
GET /v1/models/{id} | Key | One model and its prices. Details |
GET /v1/usage/today | Key | Balance and today’s usage. Details |
GET /v1/account | Key | Plan, balance and key list. Details |
GET /v1/logs | Key | Recent usage and credit events. Details |
GET /v1/statement | Key | Credit purchases for a month. Details |
GET /v1/statement/export | Key | Month of events as CSV. Details |
GET /v1/export | Key | All your events as JSON. Details |
POST /v1/account/anonymous | None | Create a new key ($0 balance). Details |
POST /v1/keys/issue | Key | Add a key to your account. Details |
POST /v1/keys/revoke · rotate · cap | Key | Manage keys and daily caps. Details |
POST /v1/credits/purchase | Key | Start a credit pack checkout. Details |
POST /v1/founding/purchase | Key | Start a Founding or card-verification checkout. Details |
GET /v1/founding/status | None | Founding seats left. Details |
GET /healthz | None | Service health check. Details |
Chat completions
OpenAI-compatible chat. Send the same body you would send to OpenAI or OpenRouter; the response comes back in the standard chat-completion shape.
curl https://api.tokenctr.com/v1/chat/completions \
-H "Authorization: Bearer $TOKENCENTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mistralai/ministral-3b-2512",
"messages": [{"role": "user", "content": "Say hello in five words."}],
"max_tokens": 100
}'# pip install openai
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.tokenctr.com/v1",
api_key=os.environ["TOKENCENTER_API_KEY"],
)
resp = client.chat.completions.create(
model="mistralai/ministral-3b-2512",
messages=[{"role": "user", "content": "Say hello in five words."}],
max_tokens=100,
)
print(resp.choices[0].message.content)
print(resp.usage)// npm install openai (Node 18+, ES modules)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.tokenctr.com/v1",
apiKey: process.env.TOKENCENTER_API_KEY,
});
const resp = await client.chat.completions.create({
model: "mistralai/ministral-3b-2512",
messages: [{ role: "user", content: "Say hello in five words." }],
max_tokens: 100,
});
console.log(resp.choices[0].message.content);
console.log(resp.usage);// No SDK: plain fetch (Node 18+ or any server runtime)
const res = await fetch("https://api.tokenctr.com/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TOKENCENTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "mistralai/ministral-3b-2512",
messages: [{ role: "user", content: "Say hello in five words." }],
max_tokens: 100,
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error?.code}: ${data.error?.message}`);
console.log(data.choices[0].message.content);
console.log("Balance after call:", res.headers.get("x-cosmos-credit-balance"));Request body
| Field | Required | Notes |
|---|---|---|
model | Yes | A model ID from the catalog or GET /v1/models, for example mistralai/ministral-3b-2512. |
messages | Yes | Array of {"role", "content"} objects (system, user, assistant, tool). |
max_tokens | Recommended | Caps the reply length and the size of the hold on your balance. If you leave it out, the reply is capped at 4,096 tokens (or the model’s limit), or lower if your balance can’t cover that, and the cap is sent to the model. max_completion_tokens works too. |
stream | No | true returns OpenAI-format server-sent events (chat.completion.chunk … data: [DONE]), with usage on the last chunk. The reply is sent once it’s complete, not token by token. |
| Everything else | No | temperature, top_p, stop, seed, tools, tool_choice, response_format, reasoning, provider and other OpenAI or OpenRouter fields are passed to the model provider unchanged. Support depends on the model; each model page lists the parameters it accepts. |
Tools that stream by default work as-is: "stream": true returns standard chat.completion.chunk events ending in data: [DONE]. The full reply arrives at once rather than word by word. Errors (such as 402 out of credit) come back as normal JSON with their HTTP status.
Response
The body is the provider’s chat-completion object. Read the reply from choices[0].message.content, or choices[0].message.tool_calls when the model calls a tool. This example is illustrative; IDs, text and numbers will differ.
{
"id": "gen-…",
"object": "chat.completion",
"created": 1792310400,
"model": "mistralai/ministral-3b-2512",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Hello there, nice to meet!" },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 14,
"completion_tokens": 9,
"total_tokens": 23,
"cost": 0.0000023
}
}usage.prompt_tokens and usage.completion_tokens are what you’re billed for. usage.cost, when present, is the provider’s own measured cost; if it’s higher than the token math, that’s what is charged. See how a call is priced.
Response headers
| Header | Meaning |
|---|---|
x-cosmos-credit-balance | Your balance in US dollars after this call, for example 4.99999770. |
x-cosmos-quota-reset | Next daily reset (00:00 UTC). Daily key caps reset at this time. |
x-cosmos-quota-remaining | Only sent when the key has no credit; always 0.0000 today. |
Retry-After | Seconds to wait, sent with rate-limit errors. |
With the Python SDK, read headers through with_raw_response:
raw = client.chat.completions.with_raw_response.create(
model="mistralai/ministral-3b-2512",
messages=[{"role": "user", "content": "Hi"}],
max_tokens=50,
)
print(raw.headers.get("x-cosmos-credit-balance"))
resp = raw.parse() # the normal ChatCompletion objectModels
Lists every model your key can call, with its current prices. Prices are US dollars per million tokens. For one model, put its full ID in the path, for example /v1/models/mistralai/ministral-3b-2512.
curl https://api.tokenctr.com/v1/models \
-H "Authorization: Bearer $TOKENCENTER_API_KEY"{
"object": "list",
"data": [
{
"id": "mistralai/ministral-3b-2512",
"object": "model",
"prompt_per_m_usd": 0.1,
"completion_per_m_usd": 0.1,
"stocked": true,
"owned_by": "cosmos",
"price_per_m_usd": null,
"cogs_per_m_usd": null
}
]
}| Field | Meaning |
|---|---|
id | The value to send as model. |
prompt_per_m_usd | Price per million input (prompt) tokens. |
completion_per_m_usd | Price per million output (completion) tokens. |
stocked | Whether the model is currently offered. |
price_per_m_usd, cogs_per_m_usd, owned_by | Legacy fields, normally null or cosmos. Use the two split prices above. |
An unknown ID on /v1/models/{id} or in a chat request returns 404 model_not_found.
Usage today
Your balance and today’s usage (UTC day), grouped by model.
{
"day": "2026-10-18",
"credits_face_usd": 4.9999977,
"free_used_face_usd": 0.0,
"free_quota_face_usd": 0.0,
"rollup": [
{ "lane": "A", "model": "mistralai/ministral-3b-2512",
"runs": 1, "tokens_in": 14, "tokens_out": 9, "measured_usd": 0.0000023 }
]
}| Field | Meaning |
|---|---|
credits_face_usd | Current balance in US dollars. |
rollup[] | One row per model used today: runs, tokens_in, tokens_out, measured_usd (dollars charged). |
day | The UTC date the rollup covers. |
free_used_face_usd, free_quota_face_usd | Reserved for a free daily allowance. There is no free allowance, so both are 0. |
Account
Returns account (an anonymised ID), plan (free, verified_free or founding), credits_face_usd (balance), and keys: one entry per key with a short key_hash fingerprint, label, created_at, revoked and daily_cap_usd. Full keys are never returned. The response also includes settings and promises fields, which you can ignore.
Usage log
Your most recent events, newest first. Optional query parameters: limit (default 100), model (exact model ID). Logs never contain prompt or reply text.
curl "https://api.tokenctr.com/v1/logs?limit=20&model=mistralai/ministral-3b-2512" \
-H "Authorization: Bearer $TOKENCENTER_API_KEY"Response: {"object": "list", "data": [ …events ]}. Each event has these fields:
| Field | Meaning |
|---|---|
event_id | Unique ID. Fetch one event with GET /v1/logs/{event_id}. |
at | When it happened, ISO 8601 in UTC (for example 2026-10-18T06:31:08+0000). |
event | RUN_SETTLED (a billed call), RUN_FAILED (provider error, nothing charged), RUN_UNPRICED (provider sent no usage, hold refunded), QUOTA_HIT (call refused for lack of credit), CREDIT_PURCHASED (payment received), CREDIT_GRANTED (Founding or verification credit). |
model | Model ID used for the call. |
tokens | Call events: {"in", "out", "cached"} token counts. |
cost | Call events: measured_usd is what was charged, estimated_usd is the hold taken before the call, provenance is measured or estimate. |
amount_usd | Credit events: dollars paid (CREDIT_PURCHASED) or credit added (CREDIT_GRANTED). |
account | Your account’s anonymised ID (a hash, not your key). |
lane, rail, wallet, schema | Internal routing labels. Safe to ignore. |
Statements
Add ?period=YYYY-MM (default: the current UTC month). /v1/statement returns credit_events, the purchases and credit grants for that month. /v1/statement/export downloads every event for the month as CSV with the columns at, event, lane, model, measured_usd, event_id.
curl -o statement.csv "https://api.tokenctr.com/v1/statement/export?period=2026-10" \
-H "Authorization: Bearer $TOKENCENTER_API_KEY"Data export
/v1/export returns all your events as {"object": "export", "events": [ … ]}. /v1/export/full adds your plan, balance, key list and settings.
Checkout
Starts a Stripe checkout for a credit pack. Send pack_id: mini ($5), maker ($19) or studio ($59). Prices are set by the server; if you also send amount_usd, it must match the pack price exactly or you get 400 amount_mismatch. Optional success_url and cancel_url must be pages on tokenctr.com.
curl -X POST https://api.tokenctr.com/v1/credits/purchase \
-H "Authorization: Bearer $TOKENCENTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pack_id": "maker"}'Response: {"checkout": {"id": "cs_…", "url": "https://checkout.stripe.com/…"}, "pack_id": "maker", "amount_usd": 19.0}. Send the buyer to checkout.url. Credit is added to the account that owns the key once Stripe confirms the payment, not when the session is created.
Send tier: paid (Founding membership, $10, adds $20 credit) or verified (card verification, $1, adds $1 credit). An account that already holds the tier gets 409 already_founding. The response has the same checkout object.
Founding seats: {"sold": 0, "cap": 500, "remaining": 500}.
Health
Returns {"ok": true, …} while the API is up. Useful for uptime monitors.