API Docs
OpenAI-compatible. Set base_url to https://api.tokengp.com/v1 and your existing SDK code works.
Looking for the Merchant Management API?
This page covers the end-user inference API — calls to /v1/* from any OpenAI-compatible SDK. If you’re a white-label tenant admin and want to manage brand, pricing, finance, or custom domains programmatically, use the separate Merchant Management API: token prefix sk-mgmt-*, base path /api/v1/merchant/*. Mint a token from the Console under API keys; the browsable OpenAPI spec lives at admin.tokengp.com/api/v1/merchant/docs.
Quickstart
One chat completions call — pick your favorite client.
curl https://api.tokengp.com/v1/chat/completions \
-H "Authorization: Bearer $TOKENGP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.8-max",
"messages": [
{"role": "user", "content": "Hello!"}
]
}'Authentication
All endpoints require an API key in the Authorization header:
Authorization: Bearer sk-tokengp-<your-key>- • Keys are issued at /keys and shown to you ONCE — store them securely.
- • Optional per-key spend cap, RPM limit, allowed-models list, and source-IP allowlist can be set when creating a key. If an IP allowlist is set, only those IPv4/CIDR sources may call the key (403 otherwise); leaving it empty allows any IP.
- • Revoke compromised keys immediately from the same page; revocation is instant.
/v1/modelsList models
Returns the catalog of models your account can call. Filters out coming-soon and disabled models.
Example
curl https://api.tokengp.com/v1/models \
-H "Authorization: Bearer $TOKENGP_API_KEY"- • Each entry has OpenAI-shape fields (id, object, created, owned_by) plus extras: display_name, input_price_per_1m, output_price_per_1m. Most OpenAI SDKs ignore unknown fields.
/v1/models/{id}Retrieve a model
Lookup a single model by id. Used by some SDKs during client initialization to validate the model name.
Example
curl https://api.tokengp.com/v1/models/qwen3.8-max \
-H "Authorization: Bearer $TOKENGP_API_KEY"- • 404 model_not_found if the id is unknown or disabled.
/v1/chat/completionsChat completions
OpenAI-compatible chat-completions endpoint. Supports streaming (SSE) and non-streaming. Token usage is tracked at the upstream and your balance is debited at the end of each request.
Request body
{
"model": "qwen3.8-max",
"messages": [
{"role": "system", "content": "You are concise."},
{"role": "user", "content": "Explain JWT in one sentence."}
],
"max_tokens": 256,
"temperature": 0.2,
"stream": false
}Example
curl https://api.tokengp.com/v1/chat/completions \
-H "Authorization: Bearer $TOKENGP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.8-max",
"messages": [{"role":"user","content":"hi"}],
"max_tokens": 32
}'- • For streaming, set "stream": true. Each SSE event is "data: {...}" carrying an OpenAI-shape chunk with "choices[0].delta.content" for partial text and "choices[0].finish_reason" on the final non-DONE chunk. Consume until you see "data: [DONE]".
- • Vision and tool / function calling are passed through to upstream models that support them (e.g. qwen-vl-*, qwen3-class). See the examples below.
- • Balance is pre-checked against worst-case cost (input + max_tokens × output_price). Insufficient balance returns 402.
- • Reasoning models (e.g. DeepSeek R1) bill the chain-of-thought as output_tokens — your usage.completion_tokens may exceed the visible message length. The chain itself is not exposed in the response.
Tool / function calling
Request body
{
"model": "qwen3.8-max",
"messages": [
{"role": "user", "content": "What's the weather in Paris?"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}]
}- • The model decides whether to call the tool. When it does, the response’s "choices[0].message.tool_calls" lists the function name + JSON arguments, and "finish_reason" is "tool_calls". Send back a follow-up request with the result as a {"role":"tool", ...} message.
- • Not every upstream supports tools. Currently Qwen-class, DeepSeek, and most OpenAI-compatible models do; small open-source models often do not.
Vision (image inputs)
Request body
{
"model": "qwen3.8-max",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}}
]
}]
}- • Pass each user message’s "content" as an array of parts instead of a plain string. "image_url" accepts either an HTTPS URL or a "data:image/<mime>;base64,<...>" data URL.
- • Use a vision-capable model (e.g. qwen3.8-max, qwen-vl-plus). Sending an image to a text-only model is upstream-dependent — it may 400 or silently drop the image.
/v1/messagesAnthropic Messages (compat)
Anthropic Messages API compatibility endpoint. Tools built on the Anthropic SDK — Claude Code, ZCode, and friends — work against TokenGP by pointing their base URL here. Requests are translated to chat-completions internally, so auth, model allowlists, rate limits, and billing behave identically.
Request body
{
"model": "qwen3.8-max",
"max_tokens": 1024,
"system": "You are concise.",
"messages": [
{"role": "user", "content": "Explain JWT in one sentence."}
],
"stream": false
}Example
curl https://api.tokengp.com/v1/messages \
-H "x-api-key: $TOKENGP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.8-max",
"max_tokens": 256,
"messages": [{"role":"user","content":"hi"}]
}'- • Auth accepts both the Anthropic-style "x-api-key: <key>" header and "Authorization: Bearer <key>" — same sk-tokengp-* keys as everywhere else.
- • Streaming emits standard Anthropic SSE events (message_start, content_block_delta, message_delta, message_stop). Tool use and image inputs are translated both ways.
- • The "thinking" request parameter is accepted but not forwarded — whether a model reasons is decided upstream. Models that emit reasoning (e.g. DeepSeek) return it as a standard Anthropic "thinking" content block.
- • POST /v1/messages/count_tokens returns a local estimate (~4 chars/token). It never hits the upstream and is free.
Using TokenGP from ZCode
Request body
// %USERPROFILE%\.zcode\cli\config.json (macOS/Linux: ~/.zcode/cli/config.json)
{
"provider": {
"tokengp": {
"kind": "openai-compatible",
"name": "TokenGP",
"options": {
"baseURL": "https://api.tokengp.com/v1",
"apiKey": "sk-tokengp-...",
"apiKeyRequired": true,
"includeUsage": true
},
"models": {
"qwen3.8-max": { "name": "Qwen3.8 Max" },
"deepseek-v4-pro": { "name": "DeepSeek V4 Pro" }
}
}
},
"model": { "main": "tokengp/qwen3.8-max", "lite": "tokengp/deepseek-v4-pro" }
}- • ZCode desktop: Model Settings → Add provider → API format "Chat Completions", Base URL "https://<api-host>/v1" (the /v1 suffix is required), API key sk-tokengp-..., then add model ids copied verbatim from the /models page or GET /v1/models.
- • ZCode CLI (headless): create the config.json shown above. Model references use "provider/model-id" format.
- • Models configured with thinking levels make ZCode call /v1/messages instead of /chat/completions — supported either way as of this endpoint.
/v1/embeddingsEmbeddings
Generate vector embeddings for one or more strings. Only models with modality=embedding are accepted here (chat models return 400 wrong_endpoint).
Request body
{
"model": "text-embedding-v3",
"input": "Hello, world."
}Example
curl https://api.tokengp.com/v1/embeddings \
-H "Authorization: Bearer $TOKENGP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "text-embedding-v3",
"input": ["hello", "world"]
}'- • input accepts a single string or an array of strings.
- • Billing is by input tokens only (output_tokens = 0).
/v1/images/generationsImage generation
Generate images from a text prompt. We wrap Aliyun’s async task API so the client sees a synchronous response — typical latency is 3-10s. Charged per image regardless of resolution.
Request body
{
"model": "qwen-image-plus",
"prompt": "a red apple on a wooden table",
"n": 1,
"size": "1024x1024"
}Example
curl https://api.tokengp.com/v1/images/generations \
-H "Authorization: Bearer $TOKENGP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-image-plus",
"prompt": "a red apple on a wooden table",
"n": 1,
"size": "1024x1024"
}'- • Response shape: {"created":<ts>,"data":[{"url":"<signed-oss-url>","revised_prompt":"..."}]}.
- • n is capped at 4. Sizes supported depend on the model; 1024x1024 works everywhere.
- • URLs are temporary signed OSS links — download and re-host within ~1 hour.
- • Internal polling deadline is 90s; long jobs return 504 upstream_timeout.
Billing
Usage is billed per request in CNY and debited from your balance when the request completes. Prices are listed per 1M tokens on the pricing page.
- • Settled on actual usage: you are charged on the token counts the upstream reports for the request (input + output), not on estimates. Failed requests are not charged.
- • Pre-flight balance check: before a call is forwarded we check your balance against a worst-case estimate (input + max_tokens at the most expensive tier the request could land in). Insufficient balance returns 402 before any upstream call is made.
- • Tiered pricing: some models are priced in tiers rather than at one flat rate — the rate depends on how long the input is, whether the model reasoned, and on some models the time of day. Tiers can differ by 10x between the cheapest and priciest band, and each request is billed at the tier it actually lands in, based on its real input token count. Every model with tiers is marked on the pricing page with a “Tiered” badge — expand it to see the rate for each band. The headline price for such a model is its cheapest band.
- • How the thinking tier is decided: a request is billed at the thinking rate if it asked for thinking (enable_thinking, reasoning_effort, or thinking.type) OR if the response actually contains reasoning content. Some models reason by default without being asked, and the upstream charges the thinking rate whenever they do.
- • Cache hits: when the upstream reports a prompt-cache hit, the cached portion of the input is billed at that model’s cache-hit rate instead of the full input rate; the rest of the input is billed normally. On tiered models the cache-hit rate is tiered too, and follows the same band as the request.
- • Checking a charge: the tier each call was billed at is shown next to that call on your dashboard. If a charge still looks wrong, send us the request id and we will reconcile it against the upstream bill.
Error codes
All errors return JSON of shape {"error":{"code":"...","message":"...","type":"..."}}. HTTP status maps to error.code as follows.
The type field groups codes into families so you can branch on it without pattern-matching every name: auth_error (key issues), invalid_request_error (your input), rate_limit (RPM caps), billing_error (balance / cap), upstream_error (provider side), server_error (rare — our side).
| HTTP | error.code | When you see it |
|---|---|---|
| 400 | bad_request | Required field missing or malformed body. Inspect error.message for specifics. |
| 400 | wrong_endpoint | Model modality does not match endpoint (e.g. chat model on /v1/embeddings). |
| 401 | missing_key | No Authorization header. Send "Authorization: Bearer sk-tokengp-...". |
| 401 | invalid_key | Key not found or revoked. Generate a new one at /keys. |
| 402 | insufficient_balance | Worst-case cost exceeds account balance. Top up via /recharge. |
| 402 | cap_reached | Per-key spend cap (balanceCapUsd) reached. Raise it or rotate the key. |
| 403 | disabled | API key was disabled by an admin. |
| 403 | expired | API key expiresAt is in the past. |
| 403 | model_not_allowed | This key has a model allowlist and the requested model is not on it. Edit the key at /keys or use an unrestricted key. |
| 404 | model_not_found | Unknown model id. Call /v1/models to see what is available. |
| 429 | rate_limit_exceeded | Per-key requests-per-minute (RPM) limit hit. Slow down or split across keys. |
| 429 | tenant_rate_limit_exceeded | Tenant-wide RPM ceiling reached — aggregated across every key in your tenant. Distinct from the per-key 429 above. Ask your tenant admin to raise the cap. |
| 500 | pricing_misconfigured | The model is missing required price config on our side (rare admin-side bug). Retry shortly; if it persists, contact support with the model id. |
| 502 | upstream_unreachable | Couldn't connect to the upstream provider. Usually transient — retry with backoff. |
| 502 | upstream_error | Upstream returned an error. The response message has upstream details + upstream_status. |
| 502 | image_generation_failed | Upstream image task ended without success (returned FAILED or empty). Retry, change the prompt, or try a different image model. |
| 503 | upstream_unavailable | All upstream tokens for the channel are in cooldown or disabled. We are aware. |
| 504 | upstream_timeout | Image generation took longer than 90s. Submit again; same prompt may succeed. |
Rate limits & quotas
- • Per-key RPM: each API key has a requests-per-minute limit (default 60). Exceeding it returns 429 rate_limit_exceeded. Raise the limit on a key in the /keys page.
- • Tenant-wide RPM ceiling: on white-label tenants, the tenant admin can set a tenant-level RPM cap that applies to all keys in the tenant combined. Hitting it returns 429 with
tenant_rate_limit_exceeded— a distinct code from the per-key 429 so you can tell the two apart in your retry logic. Splitting traffic across more keys doesn’t help here; ask your tenant admin to raise the cap. - • Account balance: every successful request is debited from your CNY balance. Insufficient balance returns 402; top up via /recharge.
- • Per-key spend cap: optional balanceCapUsd cuts off a key after a cumulative spend. Useful for embedding short-lived keys in client apps.
- • Upstream cooldowns: if all upstream tokens for a channel are throttled or erroring, the gateway returns 503 upstream_unavailable. Retry on backoff.