Skip to content
TokenCenter Get API key

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 URLhttps://api.tokenctr.com/v1
AuthenticationSend Authorization: Bearer ck_… on every call except the three marked “None” below. See Authentication.
FormatJSON requests and responses. Money is in US dollars; prices are per million tokens.
ErrorsNon-2xx responses carry {"error": {"code": "…", "message": "…"}}. See Errors.
Rate limits60 requests per minute per key and 120 per minute per IP address on chat completions.
CORSBrowser requests are allowed, but never put a funded key in public front-end code. Call the API from your server.

Endpoints

EndpointAuthPurpose
POST /v1/chat/completionsKeyCreate a chat completion. Details
GET /v1/modelsKeyList models and their prices. Details
GET /v1/models/{id}KeyOne model and its prices. Details
GET /v1/usage/todayKeyBalance and today’s usage. Details
GET /v1/accountKeyPlan, balance and key list. Details
GET /v1/logsKeyRecent usage and credit events. Details
GET /v1/statementKeyCredit purchases for a month. Details
GET /v1/statement/exportKeyMonth of events as CSV. Details
GET /v1/exportKeyAll your events as JSON. Details
POST /v1/account/anonymousNoneCreate a new key ($0 balance). Details
POST /v1/keys/issueKeyAdd a key to your account. Details
POST /v1/keys/revoke · rotate · capKeyManage keys and daily caps. Details
POST /v1/credits/purchaseKeyStart a credit pack checkout. Details
POST /v1/founding/purchaseKeyStart a Founding or card-verification checkout. Details
GET /v1/founding/statusNoneFounding seats left. Details
GET /healthzNoneService health check. Details

Chat completions

POST/v1/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
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
  }'
Python
# 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)
JavaScript
// 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);
fetch
// 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

FieldRequiredNotes
modelYesA model ID from the catalog or GET /v1/models, for example mistralai/ministral-3b-2512.
messagesYesArray of {"role", "content"} objects (system, user, assistant, tool).
max_tokensRecommendedCaps 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.
streamNotrue 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 elseNotemperature, 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.
Streaming

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.

JSON
{
  "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

HeaderMeaning
x-cosmos-credit-balanceYour balance in US dollars after this call, for example 4.99999770.
x-cosmos-quota-resetNext daily reset (00:00 UTC). Daily key caps reset at this time.
x-cosmos-quota-remainingOnly sent when the key has no credit; always 0.0000 today.
Retry-AfterSeconds to wait, sent with rate-limit errors.

With the Python SDK, read headers through with_raw_response:

Python
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 object

Models

GET/v1/models
GET/v1/models/{id}

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
curl https://api.tokenctr.com/v1/models \
  -H "Authorization: Bearer $TOKENCENTER_API_KEY"
JSON (one entry shown)
{
  "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
    }
  ]
}
FieldMeaning
idThe value to send as model.
prompt_per_m_usdPrice per million input (prompt) tokens.
completion_per_m_usdPrice per million output (completion) tokens.
stockedWhether the model is currently offered.
price_per_m_usd, cogs_per_m_usd, owned_byLegacy 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

GET/v1/usage/today

Your balance and today’s usage (UTC day), grouped by model.

JSON
{
  "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 }
  ]
}
FieldMeaning
credits_face_usdCurrent balance in US dollars.
rollup[]One row per model used today: runs, tokens_in, tokens_out, measured_usd (dollars charged).
dayThe UTC date the rollup covers.
free_used_face_usd, free_quota_face_usdReserved for a free daily allowance. There is no free allowance, so both are 0.

Account

GET/v1/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

GET/v1/logs
GET/v1/logs/{event_id}

Your most recent events, newest first. Optional query parameters: limit (default 100), model (exact model ID). Logs never contain prompt or reply text.

curl
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:

FieldMeaning
event_idUnique ID. Fetch one event with GET /v1/logs/{event_id}.
atWhen it happened, ISO 8601 in UTC (for example 2026-10-18T06:31:08+0000).
eventRUN_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).
modelModel ID used for the call.
tokensCall events: {"in", "out", "cached"} token counts.
costCall events: measured_usd is what was charged, estimated_usd is the hold taken before the call, provenance is measured or estimate.
amount_usdCredit events: dollars paid (CREDIT_PURCHASED) or credit added (CREDIT_GRANTED).
accountYour account’s anonymised ID (a hash, not your key).
lane, rail, wallet, schemaInternal routing labels. Safe to ignore.

Statements

GET/v1/statement
GET/v1/statement/export

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
curl -o statement.csv "https://api.tokenctr.com/v1/statement/export?period=2026-10" \
  -H "Authorization: Bearer $TOKENCENTER_API_KEY"

Data export

GET/v1/export
GET/v1/export/full

/v1/export returns all your events as {"object": "export", "events": [ … ]}. /v1/export/full adds your plan, balance, key list and settings.

Checkout

POST/v1/credits/purchase

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
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.

POST/v1/founding/purchase

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.

GET/v1/founding/statusno key

Founding seats: {"sold": 0, "cap": 500, "remaining": 500}.

Health

GET/healthzno key

Returns {"ok": true, …} while the API is up. Useful for uptime monitors.