tokencoup
Edition: International · 国内版 Language: ZH · EN
Get API keys
Gateway v0.3 · Reference

API Reference

One gateway, two modes. OpenAI-compatible request shape, Qianfan models behind it.

Pre-launch. https://api.tokencoup.com is the planned production endpoint and is not live yet. The reference server runs locally at http://127.0.0.1:8081 (mock mode by default), and the international resale key pool is not connected — Mode B returns resale_pool_empty until a real international Qianfan key is added. Everything below documents the contract that is already shipped and tested. v0.3 adds GET /v1/usage, GET /v1/models and buffered SSE streaming; all three are covered by the reference server's test suite.

Overview

The tokencoup gateway is a thin, OpenAI-compatible proxy in front of Baidu Qianfan. It adds gateway-level authentication, rate limiting, usage metering and billing without rewriting your request body.

The edition and the mode are encoded in the URL path, not in headers. That makes the data-residency boundary explicit and prevents a domestic key from ever being used to serve an overseas request (C2/S3 guard).

EditionPath prefixModesUpstream baseData residency
International (intl)/intl/a/…, /intl/b/…A + Bhttps://api.baiduqianfan.ai/v1/Overseas — S1 target; actual data residency pending written confirmation from Baidu
Domestic (cn)/cn/a/…A onlyhttps://qianfan.baidubce.com/v2/Mainland China
Everything after the {edition}/{mode}/ prefix is appended to that edition's upstream base. Bases were verified against official Qianfan docs on 2026-11-12: https://api.baiduqianfan.ai/v1/ (intl) and https://qianfan.baidubce.com/v2/ (cn). The intl edition's overseas data-residency claim still requires written confirmation from Baidu; see the Cross-border page before relying on it.

Quickstart

  • Create a gateway API key (rf_…). Its plan sets your RPM and token quota.
  • Choose Mode A (bring your own Qianfan key) — available on both editions.
  • POST an OpenAI-style chat payload to /cn/a/….
cURL — Mode A
BASE=https://api.tokencoup.com

curl -s "$BASE/cn/a/v1/chat/completions" \
  -H "X-Gateway-Key: rf_YOUR_GATEWAY_KEY" \
  -H "Authorization: Bearer YOUR_QIANFAN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ernie-4.0-8k",
    "messages": [{"role": "user", "content": "Say hi in one sentence."}]
  }'
200 OK — upstream passthrough
{
  "id": "chatcmpl-…",
  "object": "chat.completion",
  "model": "ernie-4.0-8k",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "Hi there!"},
    "finish_reason": "stop"
  }],
  "usage": {"prompt_tokens": 12, "completion_tokens": 5, "total_tokens": 17}
}
Responses are passed through from Qianfan unchanged. Server-side audit metadata (edition, mode, key id, token count) is never returned in the body, and on the international edition it never contains prompt content.

Authentication

There are two distinct keys. Confusing them is the most common integration error.

HeaderApplies toValueHandling
X-Gateway-KeyAll requestsrf_… gateway API keySHA-256 hash stored in the gateway DB; linked to a plan and subscription
Authorization: Bearer …Mode A onlyYour own Qianfan keyNot stored; forwarded to the upstream
X-Provider-KeyMode A onlyAlternative to AuthorizationNot stored; forwarded to the upstream
Content-TypeAll requestsapplication/jsonRequired for JSON bodies
The reference server serves POST only. A missing, invalid or revoked X-Gateway-Key returns 401 gateway_auth_failed before any upstream call. The plaintext key is shown only once at creation.
Key edition is validated (C2/S3). A domestic Qianfan key on /intl/… — or an international key on /cn/… — is rejected with 403 key_edition_mismatch. Any key not explicitly registered in tokencoup's edition registry fails closed with 403 key_edition_unknown. Key prefixes are deliberately ignored: domestic and international Qianfan keys share the bce-v3/ALTAK- prefix, so a prefix cannot prove an edition.

Endpoints

POST is used for inference: the first two path segments select edition and mode, and the remainder is forwarded verbatim to the Qianfan upstream. GET covers two read-only endpoints: /v1/usage (local ledger) and /v1/models (upstream passthrough). GET calls are authenticated and rate-limited, but never consume tokens or incur billing.

MethodPathModeMeaning
POST/cn/a/<qianfan-path>ABring-your-own-key relay — all editions
POST/intl/b/<qianfan-path>BMetered resale — international edition only
GET/cn/{a|b}/v1/usageA/BMeasured local usage — no upstream call, no billing
GET/cn/{a|b}/v1/modelsA/Bverified public snapshot (17 models, 14 priced)
Request body
{
  "model": "ernie-4.0-8k",
  "messages": [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Summarize this in two lines: …"}
  ],
  "temperature": 0.7,
  "stream": false
}
Model IDs above are illustrative (ERNIE family). Confirm the available IDs in the Qianfan console — tokencoup does not rewrite model. Treat any path below the prefix as Qianfan's own API surface (for example v1/chat/completions).

Usage (GET, local truth)

GET /cn/{a|b}/v1/usage returns measured values from the gateway's own subscriptions and usage_ledger tables. It makes no upstream call, is not metered and is not billed. Quota and RPM come from gateway/plans.py — the same source injected into the pricing page at build time.

Request
curl -s http://127.0.0.1:8081/cn/a/v1/usage \
  -H 'X-Gateway-Key: rf_...'
FieldMeaning
token_quota / rpm_limitPlan values from gateway/plans.py
token_used_billableMode B billable tokens consumed this period
token_remainingquota − token_used_billable
ledger_callsAll recorded calls (Mode A and Mode B)
ledger_tokensEstimated tokens across all recorded calls
billed_amount_usdSum of billed_amount in the ledger
overage_usd_per_1k_tokensDerived from plans.TOKEN_PRICE_USD
Token counts are still estimates (len(body)/4) until the upstream usage field is parsed. token_used_billable and ledger_tokens have different scopes: only billable Mode B calls increment the former. Neither is a final invoice — see Billing & quotas.

Model list (GET, passthrough)

GET /cn/{a|b}/v1/models is relayed to the Qianfan upstream under the same edition/key checks (C2 S3) as inference. Verified 2026-11-12: the public endpoint https://api.baiduqianfan.ai/v1/models returned HTTP 200 with 17 models, 14 carrying published per-token prices, with no authentication required. tokencoup does not rewrite that catalogue — the homepage model price table is a dated snapshot produced by site/fetch_models.py. Mock mode makes no network call and returns an empty list.

Request
curl -s http://127.0.0.1:8081/cn/a/v1/models \
  -H 'X-Gateway-Key: rf_...' -H 'Authorization: Bearer <your-qianfan-key>'
Compliance: all 14 models that declare datacenters report country_code: CN. This is upstream, first-party counterevidence to any claim that international traffic never enters mainland China. Directory prices are not a settlement commitment — confirm model IDs, prices and actual residency in the Qianfan console and in writing before launch. Models using a nested pricing schema (schema_version 2.x) are listed but not priced on the homepage.

Streaming (SSE)

Set stream: true in the request body. The response uses Content-Type: text/event-stream with OpenAI-style data: chunks terminated by data: [DONE]. v0.3 uses buffered passthrough: the upstream SSE body is read in full and written once. Chunk-by-chunk flushing is not implemented yet, so clients must not assume token-by-token latency.

Request
curl -N http://127.0.0.1:8081/cn/a/v1/chat/completions \
  -H 'X-Gateway-Key: rf_...' -H 'Authorization: Bearer <your-qianfan-key>' \
  -H 'Content-Type: application/json' \
  -d '{"model":"ernie-4.0-8k","messages":[{"role":"user","content":"hi"}],"stream":true}'
Response shape (mock)
data: {"id":"mock-chunk-1","object":"chat.completion.chunk","mock":true,"choices":[...]}

data: [DONE]
Streaming bodies are never written to the audit log (GDPR: no prompt/response persistence). Mock mode emits three synthetic chunks plus [DONE] so SDK clients can be wired up without a provider key.

Modes: BYOK vs metered

Mode A — BYOKMode B — metered resale
Provider keyYou supply ittokencoup's international key pool
BillingFlat subscription, no token markupPer token on successful (200) responses
Quota pre-checkNoYes — 402 quota_exceeded
Key edition checkYes (C2/S3 guard)Not applicable; pool is edition-locked
Availabilityintl + cnintl only
Data residencyFollows the editionOverseas — target, unverified (written confirmation pending)
C1. Domestic Qianfan terms prohibit resale / redistribution. Mode B is therefore disabled on the cn edition and returns 403 mode_B_not_allowed_on_cn. Only the international edition carries resale.

Errors

Errors are JSON objects with an error machine code; some also carry detail. Upstream Qianfan errors are passed through with their original status and body.

HTTPerrorWhenWhat to do
400edition_unknownPath lacks a valid {intl|cn}/{a|b} prefixUse a documented path
401gateway_auth_failedMissing, invalid or revoked X-Gateway-KeyRegenerate the gateway key
402quota_exceededMode B token quota exhaustedUpgrade plan or wait for the reset
403mode_B_not_allowed_on_cnMode B requested on the domestic editionUse /cn/a/… or switch to intl
403missing_provider_keyMode A without a provider keyAdd Authorization or X-Provider-Key
403key_edition_unknownProvider key is not registered in tokencoup's edition registry (prefix is not used)Register the key with an explicit domestic / international edition
403key_edition_mismatchProvider key edition ≠ serving edition (C2/S3)Match the key to the edition
429rate_limitedPlan RPM exceededBack off or upgrade
503resale_pool_emptyInternational resale key pool not configuredProvider-side: add an intl Qianfan key
Error shape
{
  "error": "quota_exceeded",
  "detail": "subscription token quota exhausted; upgrade plan or wait for reset"
}

Rate limits

Limits are per gateway key, enforced with a 60-second sliding window (Redis when REDIS_URL is configured, in-memory fallback otherwise). The check happens before the upstream call, so a 429 never consumes tokens.

PlanRPMWindow
free6060 s
pro60060 s
scale6,00060 s

Recommended: exponential backoff with jitter on 429 and 5xx; never retry 401, 402 or 403 without fixing the request first.

Data residency (domestic edition)

The domestic edition serves Mode A only. Its upstream is the domestic Qianfan endpoint, log_prompt is true for domestic compliance, and requests are never routed through the Cloudflare edge — keeping the S2 closed path intact.

Resale (Mode B) is unavailable on this edition under the domestic Qianfan terms. Use the international edition if you need metered, key-free access.

Machine-readable spec

An OpenAPI 3.1 description of the three path families is published at openapi.json. Import it into Postman, Insomnia or an SDK generator to scaffold a client.

Point any OpenAI-compatible SDK's base_url at https://api.tokencoup.com/cn/a/v1 and set X-Gateway-Key as a default header. The gateway does not rewrite payloads, so standard chat-completion helpers work as-is.

FAQ

Do I need my own Qianfan account?

Mode A: yes. Mode B: no — tokencoup supplies the international pool key.

Is my prompt stored?

On the international edition, no (log_prompt=false, collect_logs=false). On the domestic edition, prompt logging is enabled for domestic compliance.

Can I use a domestic Qianfan key on the international endpoint?

No. That is the permanently blocked C2/S3 path; the gateway returns 403 key_edition_mismatch.

When am I charged?

Only for Mode B, only on successful (200) responses. Failed requests write a zero-billed ledger row but consume no quota.

Is streaming supported?

Not in v0.2 — the reference server reads and forwards a single JSON response. Set stream: false.