Kotonia

How to use the API

One REST API key for LLM, image, audio, avatar and video. Voice TTS returns the first audio bytes in ~100ms, images in seconds, and video via async jobs.

Issue/revoke keys and view usage limits on the API Management page:Open API Management

How to use (4 steps)

  1. 1. Sign in / register
    Sign in to your account first. Registration is free if you do not have one.
  2. 2. Issue an API key
    On the API Management page, enter a project name and click Issue. The plaintext key is shown only once at creation — copy it somewhere safe (only a hash is stored in the DB).
  3. 3. Your first request
    Call an endpoint below with Authorization: Bearer <your key>.
  4. 4. Handle the response / operate
    Image and audio return base64; video returns a job id to poll. Check the timing block for speed. Revoke unused keys, and revoke + reissue if a key leaks.

Latency (RTX PRO Blackwell)

EndpointModeSpeed
Audio /audio/speechsyncfirst audio ~85–120ms / under 1s for short text
Image /images/generationssync~4s (1024², 20 steps)
Video /videos/generationsasync job~60–90s (poll the job id)

Authentication

Send your API key as a Bearer token on every request. Issue keys from the API Management page (the plaintext is shown only once at creation).

Authorization: Bearer kotonia_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Chat / LLM API (OpenAI-compatible)

An OpenAI-compatible chat completions endpoint. Point your OpenAI Python / JS SDK at our base_url and it just works. `tools` / `tool_choice` pass straight through, so agent clients get native tool calling.

curl -X POST https://kotonia.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $KOTONIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kotonia-llm-basic",
    "messages": [ { "role": "user", "content": "Hello!" } ]
  }'

# → OpenAI-shaped: { "choices": [ { "message": { "content": "..." } } ],
#     "usage": { ... }, "timing": { "total_ms": 480 } }

Use it with the OpenAI SDK (just swap base_url)

from openai import OpenAI

client = OpenAI(
    base_url="https://kotonia.ai/api/v1",
    api_key="YOUR_KOTONIA_API_KEY",   # kotonia_...
)

resp = client.chat.completions.create(
    model="kotonia-llm-basic",        # free (local) · or "kotonia-llm-standard" (metered)
    messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://kotonia.ai/api/v1",
  apiKey: process.env.KOTONIA_API_KEY,   // kotonia_...
});

const resp = await client.chat.completions.create({
  model: "kotonia-llm-basic",
  messages: [{ role: "user", content: "Hello!" }],
});
console.log(resp.choices[0].message.content);

Models

Two models are available:

  • kotonia-llm-basic (= kotonia-llm): the default, running on our local GPU. Free (with a daily request cap), fast first token. Append :think (kotonia-llm-basic:think) to enable the reasoning pass.
  • kotonia-llm-standard: a cloud-grade higher tier. Billed per token actually used against your prepaid balance (returns 402 if the balance is insufficient, so you can fall back to basic).

Note: stream:true is not supported yet (it falls back to non-streaming internally). max_tokens is capped. Check the timing block in the response to verify speed.

Anthropic-compatible (/messages)

An Anthropic Messages-compatible /messages endpoint is also available. Point the official Anthropic SDK or Claude Code at our base_url — the same kotonia_ key is sent as x-api-key. tools / tool_result are translated to/from the OpenAI shape internally.

curl -X POST https://kotonia.ai/api/v1/messages \
  -H "x-api-key: $KOTONIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kotonia-llm-basic",
    "max_tokens": 256,
    "messages": [ { "role": "user", "content": "Hello!" } ]
  }'

# → { "type": "message", "content": [ { "type": "text", "text": "..." } ],
#     "stop_reason": "end_turn", "usage": { "input_tokens": N, "output_tokens": M } }
from anthropic import Anthropic

client = Anthropic(
    base_url="https://kotonia.ai/api",          # the SDK appends /v1/messages
    api_key="YOUR_KOTONIA_API_KEY",  # kotonia_... (sent as x-api-key)
)

msg = client.messages.create(
    model="kotonia-llm-basic",       # or "kotonia-llm-standard" (metered)
    max_tokens=256,
    messages=[{"role": "user", "content": "Hello!"}],
)
print(msg.content[0].text)

Note: the SDK appends /v1/messages, so set base_url to /api (not /api/v1). Streaming is not supported yet, and image blocks are not bridged (text/tools focused).

Image API

curl -X POST https://kotonia.ai/api/v1/images/generations \
  -H "Authorization: Bearer $KOTONIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a serene japanese garden at dawn, soft light",
    "size": "1024x1024",
    "steps": 20
  }'

# → { "data": [ { "b64_json": "<PNG base64>" } ], "timing": { "total_ms": 4200 } }

Optional: seed, guidance_scale, shift, ref_image (base64, edit mode). Limits: prompt ≤ 4000 chars, size 256–2048 per side (out-of-range returns 400).

Audio API

Streaming speech synthesis (recommended)

curl -X POST https://kotonia.ai/api/v1/audio/speech \
  -H "Authorization: Bearer $KOTONIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "Hello, what a lovely day.",
    "engine": "qwen3",
    "language": "en",
    "split_mixed_languages": false
  }' \
  --output speech.frames

# Binary stream: repeated [4-byte big-endian WAV length][WAV bytes]

engine: qwen3 (default, multilingual) / irodori / voicevox. Optional: voice, speed, instruct, split_mixed_languages. Limit: input ≤ 4000 chars. The stream contains length-prefixed WAV frames.

Base64 speech synthesis (batch)

curl -X POST https://kotonia.ai/api/v1/audio/generations \
  -H "Authorization: Bearer $KOTONIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "Hello, what a lovely day.",
    "engine": "qwen3",
    "language": "en"
  }'

# → { "audio": { "b64": "<WAV base64>", "format": "wav", "sample_rate": 24000 },
#     "timing": { "first_byte_ms": 92, "total_ms": 480 } }

Speech transcription (STT)

curl -X POST https://kotonia.ai/api/v1/audio/transcriptions \
  -H "Authorization: Bearer $KOTONIA_API_KEY" \
  -F "[email protected]"

# → { "text": "...", "timing": { "stt_api_ms": 320, "total_server_ms": 325 } }

Send the file as multipart/form-data. Maximum upload size: 25 MB.

Avatar API

# List prepared avatars
curl https://kotonia.ai/api/v1/avatars \
  -H "Authorization: Bearer $KOTONIA_API_KEY"

# Stream speech plus avatar frames
curl -X POST https://kotonia.ai/api/v1/avatars/my-avatar/speech \
  -H "Authorization: Bearer $KOTONIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "input": "Hello!", "tts_backend": "qwen3", "language": "en", "fps": 25 }' \
  --output avatar.stream

# Binary stream: repeated [1-byte type][4-byte big-endian length][payload]
# type 0 = WAV audio, type 1 = JPEG video frame

GET /avatars and speech require the avatar scope. POST/DELETE require an admin-issued avatar:write scope. In the speech stream, type 0 is WAV audio and type 1 is a JPEG frame.

Video API (async)

# 1) submit job
curl -X POST https://kotonia.ai/api/v1/videos/generations \
  -H "Authorization: Bearer $KOTONIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "prompt": "a cat walking through neon-lit streets", "width": 768, "height": 512 }'

# → { "id": "<job_id>", "status": "queued", "poll_url": "/api/v1/videos/generations/<job_id>" }

# 2) poll
curl https://kotonia.ai/api/v1/videos/generations/<job_id> \
  -H "Authorization: Bearer $KOTONIA_API_KEY"

# → { "status": "completed", "data": [ { "url": "/api/ltx/video?path=..." } ] }

Optional: image (base64, I2V), audio (base64, A2V lip-sync), num_frames. Limits: prompt ≤ 4000 chars, width/height 256–1280 per side, num_frames ≤ 200 (out-of-range returns 400).

Response codes

200Success. Image/audio return body (base64); video returns a job id.
400Bad request. Missing or invalid params (e.g. missing prompt / input, bad base64).
401Unauthorized. API key missing or invalid (check the Authorization header).
429Too Many Requests. Free-tier daily limit exceeded. Resets at JST midnight.
503Service unavailable. Generation server temporarily down/busy — retry later.