Four lines to Qwen.
The Kalla gateway is OpenAI-compatible: same paths, same JSON, same SSE frames. If your tool speaks OpenAI, it speaks Kalla.
Overview
Kalla is an inference gateway. You talk to one OpenAI-compatible surface; we handle the upstream credentials, quotas, streaming normalisation and the usage ledger. Eight models are live today:
Qwen3.8Vision-capable 27B with strong reasoning and tool use.
Qwen3.5Flagship mixture-of-experts for the hardest reasoning.
Qwen3.6Previous-generation 27B, kept for evaluation and A/B.
Qwen3.5-LiteFast, cheap 9B for chat, extraction and drafting.
gpt-oss-proOpen-weight 120B mixture-of-experts with strong reasoning per euro.
gpt-oss-liteSmall gpt-oss for high-volume chat and classification.
Qwen2.5-VLVision-language model for documents, screenshots and charts.
Llama-3.3Dependable general-purpose 70B with tool use.
Quickstart
1Get an API key
Keys are issued by the Kalla team and shown once in the dashboard. They look like kalla_live_xxxxxxxxxx_… and are stored hashed — if you lose one, rotate it instead of asking for a copy.
2Point your client at the gateway
Replace the base URL and keep the OpenAI SDK you already use.
app.tsimport OpenAI from 'openai'; const kalla = new OpenAI({ apiKey: process.env.KALLA_API_KEY, baseURL: 'https://api.kalla.website/v1', });3Send your first completion
first-call.tsconst completion = await kalla.chat.completions.create({ model: 'Qwen3.8', messages: [ { role: 'system', content: 'Answer in the user language.' }, { role: 'user', content: 'اشرح الفرق بين SSE و WebSocket بجملتين' }, ], }); console.log(completion.choices[0]?.message?.content);
Authentication
Send your key as a bearer token. x-api-key is accepted for Anthropic-style clients. Keys are scoped (chat, models), can expire, and are checked on every request — revocation takes effect immediately, with no token to wait out.
export KALLA_API_KEY="kalla_live_xxxxxxxxxx_your_secret"
# preferred
curl https://api.kalla.website/v1/models -H "Authorization: Bearer $KALLA_API_KEY"
# also accepted
curl https://api.kalla.website/v1/models -H "x-api-key: $KALLA_API_KEY"Never ship a key in a browser bundle or a mobile app. Proxy through your backend. For the hosted playground, keys live in sessionStorage for the current tab only.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/chat/completions | Create a completion (buffered or SSE stream) |
| GET | /v1/models | List models available to the key |
| GET | /v1/me | Key metadata, limits and month-to-date usage |
| GET | /api/v1/public/models | Public catalogue with pricing (no auth) |
| GET | /openapi.json | OpenAPI 3.1 document |
Request
{
"model": "Qwen3.8",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Summarise our Q3 report." }
],
"temperature": 0.7,
"max_tokens": 1024,
"stream": false
}Response
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1770000000,
"model": "Qwen3.8",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "…" },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 180,
"total_tokens": 204
},
"kalla": {
"model": "Qwen3.8",
"provider": "primary",
"latency_ms": 812,
"request_id": "…"
}
}kalla is additive metadata; OpenAI clients ignore unknown fields. Reference request_id in support tickets — it appears in every error too.
Streaming
Set stream: true and read SSE frames. The stream always ends with data: [DONE]. Add stream_options.include_usage to receive a final chunk with token counts.
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"بغداد"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]Disable proxy buffering when self-hosting (nginx: proxy_buffering off;). We already send X-Accel-Buffering: no. If the upstream fails mid-stream, an error object is written as a final SSE frame.
Errors
Every error is OpenAI-shaped and carries a stable machine code plus a request id.
{
"error": {
"message": "Rate limit reached: 60 requests per minute for this key.",
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"request_id": "9f2c…"
}
}| HTTP | Code | When |
|---|---|---|
| 401 | missing_api_key | No Authorization header or x-api-key was sent. |
| 401 | invalid_api_key | The key is malformed or the secret does not match. |
| 401 | key_revoked / key_expired | The key was revoked or passed its expiry date. |
| 403 | client_suspended | The account behind the key is suspended. |
| 403 | missing_scope | The key lacks the scope required by this endpoint. |
| 400 | invalid_request | Validation failed; `error.details` lists each field. |
| 400 | invalid_json | The request body is not valid JSON. |
| 400 | model_not_found | Unknown model id — call GET /v1/models. |
| 400 | model_not_available_yet | The model is in the catalogue but not released. |
| 429 | rate_limit_exceeded | Per-key RPM reached. Respect `Retry-After`. |
| 429 | monthly_quota_exceeded | Monthly request cap reached for this key. |
| 429 | upstream_rate_limited | The provider is busy; retry shortly. |
| 502 | upstream_error | Provider returned an error; safe to retry with backoff. |
| 503 | server_overloaded / queue_timeout | Peak load. Retry with exponential backoff. |
| 503 | provider_not_configured | Operator has not connected the provider yet. |
Rate limits & quotas
Responses include RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (plus X- aliases). On 429, wait for Retry-After seconds. Use exponential backoff with jitter for 502/503.
- Every plan includes the full catalogue, streaming and tool calling. There is no per-token metering.
- Fair-use rate limits apply per API key, so a runaway script cannot exhaust the fleet for everyone else.
- When the shared GPUs are busy, requests wait in a short queue and responses can be slower — nothing is dropped.
- Need more throughput or dedicated capacity? Talk to us and we will size a plan for your team.
Usage this month: GET /v1/me.Plan price: 2,500 IQD / month or 20,000 IQD / year.
Drop it into the tools you already open daily.
If a tool speaks OpenAI, it speaks Kalla. Point the base URL at our gateway, paste your key, pick a model id.
Cursor
Point Cursor at Kalla and use Qwen for chat and inline edits.
Settings → Models → OpenAI API
Base URL https://api.kalla.website/v1
API key kalla_live_xxxxxxxxxxxxxxxx
Models Qwen3.8 # vision + tools
Qwen3.5 # flagship
Qwen3.5-Lite # fast
# Enable "Override OpenAI Base URL", add the model ids manually,
# then pick Qwen3.8 in the model selector.Common questions
- Which model should I default to?
- Start with Qwen3.8 for anything agentic or long-form; switch to Qwen3.5-Lite or gpt-oss-lite for chat surfaces where latency matters.
- Can I use the OpenAI Python SDK?
- Yes — pass base_url and api_key to the client. The JS, Python, Go and Rust SDKs all work because the wire format is identical.
- Do you support tool calling?
- Yes. Send tools and tool_choice exactly as with OpenAI; the response includes tool_calls and finish_reason: tool_calls.
- What happens during peak load?
- Requests queue in a bounded scheduler. You may see higher latency, or a 503 server_overloaded/queue_timeout if the queue is full — retry with backoff.
Bring your own editor.
We'll bring the models.
Tell us what you are building and we will provision a key, tune your limits, and stay on the line while you ship.
baseURL: "https://api.kalla.website/v1"