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.
| Endpoint | Mode | Speed |
|---|---|---|
Audio /audio/speech | sync | first audio ~85–120ms / under 1s for short text |
Image /images/generations | sync | ~4s (1024², 20 steps) |
Video /videos/generations | async job | ~60–90s (poll the job id) |
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
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 } }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);Two models are available:
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.
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).
curl https://kotonia.ai/api/v1/images/loras \
-H "Authorization: Bearer $KOTONIA_API_KEY"
# → { "model": "hidream-o1-image", "loras": [ { "name": "kotonia03", "requires_paid": false } ] }
# Use a returned name in /images/generations:
# "loras": [ { "name": "kotonia03", "weight": 0.75 } ]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), loras ([{name, weight}], stackable). GET /images/loras returns loaded names and requires_paid (image scope). Weight: 0–2, default 1. The lora / lora_weight shorthand is also supported; loras takes precedence. LoRAs also work with reference-image editing. Limits: prompt ≤ 4000 chars, size 256–2048 per side (out-of-range returns 400).
curl "https://kotonia.ai/api/v1/audio/voices?engine=voxcpm" \
-H "Authorization: Bearer $KOTONIA_API_KEY"
# → { "engine": "voxcpm", "voices": [
# { "ref_id": "...", "label": "...", "description": "...", "native_lang": "en",
# "visibility": "official", "mine": false }
# ] }
# Use the same engine and a returned ref_id in /audio/speech or /audio/generations:
# "engine": "voxcpm", "ref_id": "<ref_id from list>"
# For Irodori references, list with ?engine=irodori instead.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": "voxcpm",
"language": "en"
}' \
--output speech.frames
# Binary stream: repeated [4-byte big-endian WAV length][WAV bytes]engine: voxcpm (default, multilingual) / irodori / voicevox. Optional: voice, speed, instruct, ref_id, seed. For voxcpm, voice is the voice-design description. GET /audio/voices?engine=voxcpm (or irodori) lists official, public, and your own references (audio scope; default voxcpm). Use the same engine and a returned ref_id to pin the voice in both streaming and batch synthesis. VOICEVOX speakers are outside this reference catalog. Limit: input ≤ 4000 chars. The stream contains length-prefixed WAV frames.
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": "voxcpm",
"language": "en"
}'
# → { "audio": { "b64": "<WAV base64>", "format": "wav", "sample_rate": 48000 },
# "timing": { "first_byte_ms": 60, "total_ms": 480 } }curl -X POST https://kotonia.ai/api/v1/audio/transcriptions \
-H "Authorization: Bearer $KOTONIA_API_KEY" \
-F "file=@speech.wav"
# → { "text": "...", "timing": { "stt_api_ms": 320, "total_server_ms": 325 } }Send the file as multipart/form-data. Maximum upload size: 25 MB.
# 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": "voxcpm", "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 frameGET /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.
# 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).
200 | Success. Image/audio return body (base64); video returns a job id. |
400 | Bad request. Missing or invalid params (e.g. missing prompt / input, bad base64). |
401 | Unauthorized. API key missing or invalid (check the Authorization header). |
429 | Too Many Requests. Free-tier daily limit exceeded. Resets at JST midnight. |
503 | Service unavailable. Generation server temporarily down/busy — retry later. |