API documentation

Base URL: https://api.example.com. After you deploy, use your real production API URL in every example.

Authentication

Create a key in the developer dashboard and send it as a bearer token. Keys start with zenn_sk_.

http
Authorization: Bearer YOUR_ZENNAI_API_KEY

Secret keys must stay on a trusted server. Never embed them in public website code or mobile apps. Anyone with the key can use your quota. Browser cross-origin access (CORS) is only allowed for configured origins, and CORS is not authentication.

Endpoints

GET /v1/models

Lists available models. Currently only zennai.

POST /v1/chat/completions

FieldDescription
modelRequired. Must be "zennai".
messagesRequired. Array of { role, content }, where role is system, user or assistant and content is a string.
streamOptional boolean. Streams Server-Sent Events when true.
max_tokensOptional. Capped by the plan's output limit.
userOptional end-user identifier (string, up to 128 characters).

Any other field (for example temperature, tools or n) is rejected with HTTP 400. ZennAI doesn't silently ignore parameters.

json
{
  "model": "zennai",
  "messages": [{ "role": "user", "content": "Hello, who are you?" }],
  "stream": false
}

Response:

json
{
  "id": "chatcmpl-…",
  "object": "chat.completion",
  "created": 1760500000,
  "model": "zennai",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "Hi! I'm ZennAI…" }, "finish_reason": "stop" }],
  "usage": { "prompt_tokens": 24, "completion_tokens": 18, "total_tokens": 42, "estimated": false }
}

When the provider doesn't report token usage, counts are estimated and marked "estimated": true.

JavaScript (Node.js / server)

javascript
const response = await fetch("https://api.example.com/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${process.env.ZENNAI_API_KEY}`,
  },
  body: JSON.stringify({
    model: "zennai",
    messages: [{ role: "user", content: "Hello!" }],
  }),
});

const data = await response.json();
console.log(data.choices[0].message.content);

Python

python
import os, requests

r = requests.post(
    "https://api.example.com/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['ZENNAI_API_KEY']}"},
    json={"model": "zennai", "messages": [{"role": "user", "content": "Hello!"}]},
    timeout=60,
)
r.raise_for_status()
print(r.json()["choices"][0]["message"]["content"])

Streaming

Set "stream": true. Each event is a chat.completion.chunk. The stream ends with data: [DONE]. Errors that happen mid-stream arrive as a { error } event.

javascript
const res = await fetch("https://api.example.com/v1/chat/completions", {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.ZENNAI_API_KEY}` },
  body: JSON.stringify({ model: "zennai", stream: true, messages: [{ role: "user", content: "Write a haiku" }] }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buf = "";
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buf += decoder.decode(value, { stream: true });
  const lines = buf.split("\n");
  buf = lines.pop();
  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;
    const data = line.slice(6);
    if (data === "[DONE]") continue;
    const chunk = JSON.parse(data);
    process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
  }
}

Telegram bots & other servers

Any backend that can make HTTPS requests can call ZennAI. Keep the key in an environment variable. Here's a minimal Telegram bot:

python
# pip install python-telegram-bot requests
import os, requests
from telegram.ext import ApplicationBuilder, MessageHandler, filters

async def reply(update, context):
    r = requests.post(
        "https://api.example.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['ZENNAI_API_KEY']}"},
        json={"model": "zennai", "messages": [{"role": "user", "content": update.message.text}]},
        timeout=60,
    )
    data = r.json()
    text = data["choices"][0]["message"]["content"] if r.ok else data["error"]["message"]
    await update.message.reply_text(text)

app = ApplicationBuilder().token(os.environ["TELEGRAM_BOT_TOKEN"]).build()
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, reply))
app.run_polling()

For websites, call ZennAI from your own backend (or serverless function) and forward the result to the browser.

Errors

json
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limited",
    "message": "Too many requests per minute. Slow down and retry.",
    "request_id": "req_…"
  }
}
  • 400: invalid JSON, unsupported fields, wrong model, or input too long.
  • 401: missing, invalid or revoked API key.
  • 413: request body too large.
  • 429: per-minute rate limit (rate_limited, includes Retry-After) or daily quota exhausted (daily_quota_exceeded).
  • 500: unexpected server error.
  • 502 / 503 / 504: the upstream AI provider failed, isn't configured, or timed out.

Every response includes an X-Request-Id header. Include it when reporting issues.

Rate limits

The free plan allows 20 requests per day per account by default, shared between website chat and the API. Requests are also limited per minute, per account and per key. Quotas reset at 00:00 UTC. Your current numbers are in the dashboard.