From signup to first token in 60 seconds.
Drop-in for the OpenAI, Anthropic, and Gemini SDKs. Swap one line — same client, same request and response shapes, lower bill.
Get a key
Sign in to the console and mint a key. Keys are scoped per workspace; revoke any time.
Swap base_url
Wherever you initialize the OpenAI client, change the base_url to ours. Everything else stays. The SDK version, the request shape, the response shape — identical.
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
- base_url="https://api.openai.com/v1",
+ base_url="https://api.tkex.ai/v1",
)Not just OpenAI — the same gateway is a drop-in for the Anthropic and Gemini SDKs too. Point each SDK at the base below; its own native auth header is accepted (Authorization: Bearer, x-api-key, or x-goog-api-key — all take the same sk- key).
| SDK | base_url | Endpoint |
|---|---|---|
| OpenAI | https://api.tkex.ai/v1 | /chat/completions · /embeddings · /images · /responses |
| Anthropic | https://api.tkex.ai/v1 | /messages |
| Gemini | https://api.tkex.ai/v1beta | /models/{model}:generateContent |
First request
Pick a language. Set TX_API_KEY in your env. Send the request. We route to the model node, you get the same shape OpenAI returns.
curl https://api.tkex.ai/v1/chat/completions \
-H "Authorization: Bearer $TX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek/deepseek-v4-pro",
"messages": [
{"role": "user", "content": "Say hello in five words."}
]
}'Models use a vendor/slug id (e.g. deepseek/deepseek-v4-pro). Append @tag to pin a quality variant (e.g. deepseek/deepseek-v4-pro@exacto); omit it for the default. Available slugs are listed on /models.
Error codes
Every error is a standard HTTP status with a JSON body — { "detail": "..." }. Your existing retry / backoff logic works unchanged.
| Status | Meaning | What to do |
|---|---|---|
| 400 | Bad request — malformed body or an unsupported parameter. | Check the request against the OpenAI schema. Unknown params are rejected, not silently forwarded. |
| 401 | Invalid or missing API key. | Check the auth header. Rotate the key in /console/keys if leaked. |
| 402 | Spend limit reached (period_quota_exceeded) — a daily, weekly, or monthly cap, or an out-of-funds wallet. | Raise the cap in /console/settings or top up in /console/credits; the response includes period, limit, used, and resets_at. |
| 429 | Rate limit hit. | Back off and retry. Default ceiling is 600 requests/min per key (60/min for image generations). |
| 502 | Upstream network error reaching the model provider. | Transient — retry with backoff. |
| 503 | No provider available — all routing retries were exhausted. | The model may be temporarily unlisted or every upstream failed. Retry, or pick another model on /models. |
| 504 | Upstream model timed out. | Lower max_tokens or choose a faster model (see /models). |
Streaming
Set stream=True. Tokens arrive as Server-Sent Events. Same protocol as OpenAI — your existing stream handler works without changes.
stream = client.chat.completions.create(
model="deepseek/deepseek-v4-pro",
messages=[{"role": "user", "content": "Count to ten."}],
stream=True,
)
for event in stream:
delta = event.choices[0].delta.content
if delta:
print(delta, end="", flush=True)The stream terminates with a data: [DONE] sentinel, exactly like OpenAI. Add stream_options={"include_usage": true} to get a final chunk with token counts.
Other endpoints
The gateway proxies more than chat. Same key, same host — just a different path. Rate limit is 600 requests/min per key (60/min for image generations).
| Endpoint | Purpose |
|---|---|
| POST /v1/embeddings | Text embeddings (OpenAI compatible) |
| POST /v1/images/generations | Image generation |
| POST /v1/responses | OpenAI Responses API |
| POST /v1/rerank | Reranking (Jina-style) |
| POST /v1/messages | Anthropic-native Messages |
| GET /v1/models | List the models your key can call |
Advanced routing
Every relay endpoint accepts an optional provider object to steer which upstream serves the request (OpenRouter-compatible). The gateway reads it and strips it — it is never forwarded upstream.
only/ignore— whitelist / blacklist providers by name (all regions) orname/region.order— preferred provider order; earlier entries win, the rest are fallbacks.sort—"price"/"latency"/"throughput": deterministic pick along that axis.allow_fallbacks—falsetries the first provider once, no cross-provider retry.quantizations— pin quant variants, e.g.["fp8"].
{
"model": "deepseek/deepseek-v4-pro",
"messages": [{ "role": "user", "content": "Hello" }],
"provider": {
"sort": "price",
"allow_fallbacks": false
}
}Tracing & reasoning
Every response carries an X-Request-Id header. Send your own X-Request-Id to correlate a call across your services — the same id shows up in your request logs in the console.
For reasoning models, set reasoning_effort ("none" / "low" / "medium" / "high") on a chat request. The model's thinking is returned separately as reasoning_content — choices[0].message.reasoning_content when non-streaming, delta.reasoning_content while streaming.
Model terms
TokenExchange is a pass-through gateway: every request is relayed to a third-party model provider, and each model you call is governed by that provider's own terms— their acceptable-use policy, data-handling, and availability commitments. We don't modify, waive, or supersede them, so review the applicable provider's terms before sending sensitive data.
See the providers directory for who participates in the routing network, and each model's page for the providers serving it. Your contract with us is the Terms of Service (see §5, Model Providers and Model Terms).