leiolai-1
Use leiolai-1 as the model ID.
OpenAI-compatible chat completions with adjustable reasoning effort, plus experimental continuous generation and an experimental context window of up to 11 million tokens.
The API authenticates with bearer tokens. The finite endpoint implements the documented subset of OpenAI Chat Completions below, so you can use raw HTTP, an OpenAI SDK, or the Vercel AI SDK. When you create a key, choose Unrestricted, Private only, or Research only. An unrestricted key must send mode on every request. A restricted key may omit mode because the server infers its only permitted mode. A request cannot override a key restriction.
Open Account in the Leiolai web app. Sign in, choose Top up beside your balance, then open More, Leiolai API, and Manage keys. Choose the key's mode access. A new key is shown once. Store it on a backend you control.
curl https://api.leiolai.com/v1/chat/completions \
-H "Authorization: Bearer sb_your_key" \
-H "Content-Type: application/json" \
-d '{
"model": "leiolai-1",
"messages": [{"role": "user", "content": "Say hi in five words."}],
"stream": true,
"mode": "private"
}' from openai import OpenAI
client = OpenAI(
api_key="sb_your_key",
base_url="https://api.leiolai.com/v1",
)
response = client.chat.completions.create(
model="leiolai-1",
messages=[{"role": "user", "content": "Say hi in five words."}],
reasoning_effort="low",
extra_body={"mode": "private"},
)
print(response.choices[0].message.content) import { generateText } from "ai";
import { leiolai } from "@leiolai/ai-sdk-provider";
const result = await generateText({
model: leiolai("leiolai-1"),
prompt: "Say hi in five words.",
});
console.log(result.text); https://api.leiolai.com/v1
Send your key on every request: Authorization: Bearer sb_...
Never put an API key in frontend JavaScript. Anyone can extract it and spend your balance. Keep the key on a backend you control, then have your frontend call that backend.
Send a conversation, get one assistant response.
Open a continuous response that can accept new context while it runs.
Send context, buffer state, or a stop command to a continuous session.
Returns leiolai-1, the currently available model.
Use Chat Completions, not the Responses API. Audio input, embeddings, fine-tuning, and batch endpoints are not part of this compatibility surface.
Use leiolai-1 for every request. Set response depth with reasoning_effort.
Use leiolai-1 as the model ID.
This one goes to 11. A floor-to-ceiling context window for the whole project.
Messages accept text, image, and supported inline file parts. Responses are text.
Creates a completion for the supplied conversation. The body follows the OpenAI chat completions schema; the supported fields are below.
{ "model": "leiolai-1", "messages": [ { "role": "user", "content": "Explain why the sky changes color at sunset." } ], "stream": true, "stream_options": {"include_usage": true}, "reasoning_effort": "medium", "mode": "private" }
Use leiolai-1. Provider-qualified forms ending in /leiolai-1 resolve to it. Unknown model IDs return 404 model_not_found.
Role and content pairs. Role is system, user, assistant, or tool. Content is a plain string or an array of typed parts. An assistant message may include tool_calls; a tool result uses role: "tool" and the matching tool_call_id.
{"type":"text","text":"..."} {"type":"image_url","image_url":{"url":"data:..."}} {"type":"file","file":{"filename":"notes.md","file_data":"data:..."}} Images work in both modes and use base64 data: URLs; remote image URLs are not accepted. Inline files may contain up to 2 MiB of UTF-8 text or code. file_id references and binary files such as PDFs are not accepted.
When true, content is returned as server-sent event deltas. When omitted, the response is a single JSON object.
When true with stream: true, the stream includes OpenAI's final usage-only chunk before data: [DONE].
Response depth: low, medium, high, or xhigh. Defaults to low.
An optional output cap, in tokens. If omitted, Leiolai derives a ceiling from the available balance and API budget. max_tokens is a compatibility alias.
Unrestricted API keys must send private or non-private. A Private-only or Research-only key may omit mode because the server infers the key's only permitted mode. A value that conflicts with the key restriction returns HTTP 403.
This is a Chat Completions-compatible subset, not a promise that every OpenAI field or SDK feature is implemented. Only the fields listed here are part of the contract. You always receive one choice.
Send OpenAI function definitions in tools. Leiolai returns validated calls for your application to execute. It never executes your functions.
{
"model": "leiolai-1",
"mode": "private",
"messages": [{"role": "user", "content": "What is the weather in Chicago?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the weather for a city",
"strict": true,
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
"additionalProperties": false
}
}
}],
"tool_choice": "auto"
} Use auto, none, required, or name one function with the standard OpenAI object shape.
Set true to allow one assistant turn to return multiple independent calls. All calls belong to that same turn. Dependent work remains sequential.
Execute every call in your application. Send the assistant message back unchanged, then add one role: "tool" message per result with the matching tool_call_id. Keep every returned call ID unchanged.
Each returned function name and argument object is checked against the supplied definition before delivery. A tool response uses finish_reason: "tool_calls".
Use response_format when the assistant response itself must be JSON. Do not combine response_format with function tools in the same request.
{
"model": "leiolai-1",
"mode": "private",
"messages": [{"role": "user", "content": "Classify the sentiment."}],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "sentiment",
"strict": true,
"schema": {
"type": "object",
"properties": {"sentiment": {"type": "string", "enum": ["positive", "negative"]}},
"required": ["sentiment"],
"additionalProperties": false
}
}
}
} {"type":"json_object"} requires one valid JSON object.
Accepts name, optional description, optional strict, and schema. Successful content is one schema-valid JSON value with no Markdown fence or trailing prose.
When stream is true, the response uses OpenAI Chat Completions server-sent events and ends with data: [DONE]. The choices field is the OpenAI-compatible response array. Leiolai returns one item, so read choices[0]. In each text event, delta.content is new text; append every value to build the reply. Chunk boundaries and timing can vary, so clients must not assume token-by-token delivery or a minimum number of chunks. When stream is omitted, the response is a single chat.completion object.
data: {"id":"chatcmpl-8f1c2d3a4b5e6f7a8b9c0d1e","object":"chat.completion.chunk","created":1787000000,"model":"leiolai-1","choices":[{"delta":{"role":"assistant","content":"Hi there"},"index":0,"finish_reason":null}],"usage":null}
data: {"id":"chatcmpl-8f1c2d3a4b5e6f7a8b9c0d1e","object":"chat.completion.chunk","created":1787000000,"model":"leiolai-1","choices":[{"delta":{},"index":0,"finish_reason":"stop"}],"usage":null}
data: {"id":"chatcmpl-8f1c2d3a4b5e6f7a8b9c0d1e","object":"chat.completion.chunk","created":1787000000,"model":"leiolai-1","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":2,"total_tokens":16}}
data: [DONE] The response ID. It stays the same across every chunk. Include it when reporting a bad answer.
assistant, on the first chunk only.
Append each value to build the reply. A short answer may arrive in one content chunk.
Collect calls by index. Keep the first id, append each function.arguments fragment in order, and parse the completed JSON string after the terminal finish_reason: "tool_calls" chunk.
Marks the end of the stream.
One chat.completion object with choices[0].message.content.
Token counts for the request: prompt_tokens, completion_tokens, and total_tokens. A non-streaming response includes them when counts are available. A stream includes them only when you send stream_options.include_usage: true; OpenAI's usage-only chunk has an empty choices array and arrives immediately before [DONE]. Earlier chunks carry usage: null. When present, these report the token counts used for billing.
stop when Leiolai completed the answer, tool_calls when your application should execute returned functions, length when it reached your max_completion_tokens cap, and content_filter when the content policy stopped the response. Null before the terminal chunk.
Continuous generation keeps a response open and lets you add context while it runs. There is no output-token ceiling. The session will end when you stop it or if your available balance or API budget is exhausted. The opening endpoint always returns server-sent events. It is a Leiolai extension, not a standard Chat Completions stream, so use an SSE client that preserves every event.
curl -N https://api.leiolai.com/v1/infinite/chat/completions \
-H "Authorization: Bearer sb_your_key" \
-H "Content-Type: application/json" \
-d '{
"model": "leiolai-1",
"messages": [{"role": "user", "content": "Teach me astronomy until I stop you."}],
"reasoning_effort": "medium",
"mode": "private",
"lookahead": true,
"consumer_lease": true,
"realtime_audio": true
}' When true, the stream includes a replaceable preview separate from delivered text.
When true, send consumer_heartbeat while the stream is live. This lets Leiolai close the session if the downstream consumer disconnects but a proxy keeps the HTTP connection open.
Set this when the client speaks delivered text as it arrives. Leiolai will not rewrite words the user may already have heard. Send buffer reports to pace delivery.
model, messages, reasoning_effort, and mode have the same meaning as on finite chat. Text, image, and supported inline file parts work in both modes. You pay the input rate for the opening messages once. Inject updates do not add another input charge.
The first event carries an opaque session ID. Keep it and send the same API key on every later inject. A session belongs to the API key that opened it.
The choices field is the OpenAI-compatible response array. Leiolai returns one item, so read choices[0]. In a text event, delta.content is new text; append each value.
data: {"session_id":"inf-0123456789abcdef"}
data: {"choices":[{"delta":{"content":"The first sentence. "}}]}
data: {"lookahead":"This preview may still change."}
data: {"lookahead":""}
data: {"inject_ack":{"id":"opaque-receipt-id","seq":1,"anchor":20,"text":"Wait, explain that more simply."}}
data: {"choices":[{"delta":{"content":"Here is the simpler version. "}}]}
: keepalive
data: [DONE] The first event. Send this opaque value as the session query parameter on every inject request.
Delivered answer text. Append every value. Delivered bytes never change.
The full current preview. Replace the previous preview with this value. An empty string clears it. Never append lookahead to delivered text.
Confirms a completed user message. anchor tells you how much assistant text belongs before it and is measured in UTF-8 bytes from the start of the delivered answer.
Ends the session. A pause in output does not end the session. SSE comment lines are keepalives and can be ignored.
The user's completed message.
The user's complete message so far. Each update replaces the previous value.
The latest image as a base64 data URI. It replaces the prior live image after screening.
Send only this field with true to confirm the downstream consumer is still connected. Use it only when the opening request set consumer_lease: true.
Send true by itself to end the session.
Send inject requests one at a time, in order, within each session. Do not send text and text_partial together. Send stop by itself. Unknown fields return HTTP 400.
As the user interrupts, send the complete transcript so far through text_partial. Leiolai clears the old lookahead and pauses delivery. When the user finishes, send the completed message through text. Leiolai adds it to the conversation, sends inject_ack with the exact insertion point, and resumes from the updated context.
{
"ok": true,
"inject_ack": {
"id": "opaque-receipt-id",
"seq": 1,
"anchor": 20,
"text": "Wait, explain that more simply."
}
} inject_ack.anchor tells you how much assistant text belongs before the user’s message. It is a UTF-8 byte count from the start of the delivered answer. Once you have received that many bytes, show the user’s message. Show any later answer text beneath it.
Opening messages accept the same image and inline file parts as finite chat. During a live session, image updates the current visual context. A live image must be a base64 data URI with up to 8 MiB of decoded image data; the complete inject body may be up to 12 MiB. Files cannot be added through the inject endpoint after the session opens.
Buffer reports are optional. They tell Leiolai how much consumable output remains queued and how quickly it is draining. Use them for latency-sensitive playback or another consumer that can measure queued work in seconds. Text-only clients can ignore them.
{
"buffer": {
"remaining_seconds": 1.2,
"drain_rate": 1.0
}
} The non-negative number of seconds currently queued by the client.
The queue's consumption speed relative to real time, from 0 through 4. Send 0 while paused or unable to consume, 1 at normal speed, and 2 at twice normal speed.
Report whenever queued output or its drain_rate changes. For latency-sensitive playback, periodic updates around every 500 ms can help correct timing drift. The server estimates the remaining buffer between reports. Less frequent updates are accepted. Send one request at a time so reports arrive in order.
At the current defaults, Leiolai pauses delivery when the client reports more than 2.5 seconds of queued output. Generation continues while delivery is paused, so lookahead may keep changing. Leiolai resumes delivery when the queue reaches 2.5 seconds or less. If the queue drops below 0.8 seconds and more output is ready, Leiolai may add enough to target about 1.5 seconds. These thresholds are controlled by the server. Clients should report their actual state instead of reproducing this logic.
Before streaming begins, API-generated errors use the OpenAI-style JSON envelope below. After an SSE stream begins, its HTTP status remains 200 because the response headers were already sent. A terminal API error then arrives in the stream before data: [DONE]. If the content policy stops a finite stream, its final choice instead has finish_reason: "content_filter".
{
"error": {
"message": "mode is required for unrestricted API keys. Send \"private\" or \"non-private\".",
"type": "invalid_request_error",
"param": "mode",
"code": "invalid_value"
}
} error.type is the broad error class. error.code is the stable programmatic code, and error.param names the request field when one caused the failure. Every API-generated response includes X-Request-Id. Log it when you contact support. You may send X-Client-Request-Id; valid values are echoed in the response.
| Status | Code | What it means |
|---|---|---|
| 401 | invalid_api_key | No valid API key was provided. Check the bearer token. |
| 402 | insufficient_credits | Your available balance cannot cover this request. Top up or request less output. |
| 402 | api_budget_exhausted | The key or the account reached its API budget for the period. Raise or clear the budget in your account. |
| 402 | api_budget_required | Auto reload is on for the account, so API use needs a budget. Set one, or turn auto reload off. |
| 400 | invalid_value | A field is invalid, two fields conflict, or an unrestricted API key omits mode. |
| 400 | invalid_tool_schema | A function definition or its JSON Schema is invalid. |
| 400 | invalid_tool_result | Assistant tool-call history or a caller-supplied tool result is invalid. |
| 400 | context_length_exceeded | The request exceeds the available model context. Shorten the messages or tool history. |
| 400 | invalid_json | The request body is not valid JSON. |
| 403 | api_key_mode_restricted | The request conflicts with the API key's Private-only or Research-only restriction. |
| 403 | content_policy_violation | The content policy blocked the input before streaming began. |
| 403 | content_filter | The content policy stopped the response. An active finite stream reports finish_reason: "content_filter". |
| 404 | model_not_found | The requested model is not available. |
| 404 | session_not_found | The continuous session ended, does not exist, or belongs to another API key. |
| 405 | method_not_allowed | This route does not accept that HTTP method. |
| 413 | request_too_large | The request body exceeds the accepted size. |
| 429 | rate_limit_exceeded | You exceeded the request rate limit. Wait for the number of seconds in Retry-After, then try again. |
| 500 | streaming_unavailable | Streaming is unavailable. Try again. |
| 503 | billing_unavailable | API billing is temporarily unavailable. Retry shortly. |
| 503 | api_budget_unverifiable | Spending records are temporarily unavailable. Retry shortly. |
| 503 | context_length_unavailable | The service could not verify the request's context length. Retry shortly. |
| 503 | vision_unavailable | Image understanding is temporarily unavailable. Retry shortly. |
| 503 | service_busy | The service is at capacity. Retry after the number of seconds in Retry-After when that header is present. |
| 503 | service_unavailable | The request could not be completed. Retry shortly. |
| 502 | invalid_tool_call | A generated tool call failed deterministic validation after repair. Retry the request. |
Low, medium, and high Research rates use launch pricing. xhigh and Private use standard pricing.
(mode: "non-private")
| Effort | Input, USD / 1M tokens | Output, USD / 1M tokens |
|---|---|---|
| low | ||
| medium | ||
| high | ||
| xhigh | $160 | $700 |
Launch pricing
(mode: "private")
| Effort | Input, USD / 1M tokens | Output, USD / 1M tokens |
|---|---|---|
| low | $20 | $90 |
| medium | $40 | $175 |
| high | $80 | $350 |
| xhigh | $160 | $700 |