API reference
The API is OpenAI-compatible. If your code already talks to OpenAI, changing the base URL and the key is the whole migration. Your base URL is shown in the console alongside your keys.
Quickstart
Create a key from the console, then point any OpenAI client at the API:
from openai import OpenAI
client = OpenAI(
base_url="https://tokenkey.in/v1",
api_key="tk_live_...",
)
response = client.chat.completions.create(
model="tk-auto",
messages=[{"role": "user", "content": "hello"}],
)Authentication
Pass the key as a bearer token. Keys look like tk_live_<id>_<secret>.
Authorization: Bearer tk_live_...
Only a hash of the secret is stored, so a key is shown once at creation and cannot be recovered afterwards. If you lose one, revoke it and create another. Keys are issued from the console rather than through the API: a key that can mint keys means revoking a leaked one does not revoke the key it already created.
Models
| Model | Purpose |
|---|---|
| tk-auto | The router chooses a tier. Recommended default. |
| tk-base | Fast tier, for short high-volume requests. |
| tk-32b | Flagship reasoning tier. |
| tk-coder | Code generation and repository-scale edits. |
Naming a model explicitly always wins — the router never second-guesses an explicit choice. Which tier served a request, and why, is recorded and visible in the console.
The available models can change. Call GET /v1/models for the authoritative list.
Streaming
Set stream: true for server-sent events. Token usage is captured whether or not you ask for it — if you do not set stream_options.include_usage, the gateway requests it internally and strips the extra chunk before it reaches you, so you receive exactly the stream you asked for.
stream = client.chat.completions.create(
model="tk-32b",
messages=[{"role": "user", "content": "explain gradient descent"}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")Disconnecting mid-stream cancels the generation upstream rather than leaving it running, and what was consumed up to that point is still recorded.
Rate limits
Two limits apply: requests per minute, enforced on arrival, and tokens per minute plus a daily cap, reconciled after each response. A request’s cost is not known until it finishes, so one call may overshoot the token window and the next is refused rather than the current one being cut off.
Every response carries the current state:
x-ratelimit-limit-requests: 1000 x-ratelimit-remaining-requests: 987 x-ratelimit-reset-requests: 60
A 429 includes Retry-After in seconds. Your exact limits are on the settings page in the console.
Errors
Errors use the OpenAI shape, so SDK error handling works unchanged.
{
"error": {
"message": "The model 'gpt-4o' does not exist. Available models: ...",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_api_key | Missing, malformed, revoked or expired key. |
| 404 | model_not_found | The model is not in the registry. The message lists what is. |
| 400 | tier_unavailable | The tier this request needs is not available — sending images to a text-only service, for example. |
| 429 | rate_limit_exceeded | Request or token quota exceeded. Honour the Retry-After header. |
| 502 | api_error | The inference backend could not be reached. |
Data handling
Prompt and completion text is not stored. Usage records hold token counts, latency, the model requested and the tier that served it — nothing else. Inference runs on hardware in Amaravati, India, so your text is not forwarded to a model provider abroad.
This is asserted by a test that sends a marker string through streamed and non-streamed completions and then scans every column of every table for it. It runs on every commit, which is how the guarantee survives future changes.