Connect your favorite app
Step-by-step setup for SillyTavern, Marinara Engine, JanitorAI and other OpenAI-compatible clients.
Start with the correct base URL
OpenAI-compatible clients include /v1 in their base URL. The Anthropic SDK adds /v1/messages itself, so its base URL is the origin only.
https://itzi.app/v1https://itzi.appDirect HTTP
Every request is JSON. The response includes x-request-id; record it when troubleshooting.
curl https://itzi.app/v1/responses \
--header "Authorization: Bearer $ITZI_API_KEY" \
--header "Content-Type: application/json" \
--data '{"model":"gpt-5.6-terra","input":"Explain cursor pagination in three sentences."}'OpenAI Node SDK
Install with npm install openai.
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.ITZI_API_KEY,
baseURL: "https://itzi.app/v1",
});
const response = await client.responses.create({
"model": "gpt-5.6-terra",
"input": "Explain cursor pagination in three sentences."
});
console.log(response.output_text);Anthropic TypeScript SDK
Install with npm install @anthropic-ai/sdk.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.ITZI_API_KEY,
baseURL: "https://itzi.app",
});
const message = await client.messages.create({
"model": "claude-opus-4.8",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "Explain cursor pagination in three sentences."
}
]
});
for (const block of message.content) {
if (block.type === "text") console.log(block.text);
}SDK references: OpenAI Node and Anthropic TypeScript.
Discover models before sending
Availability and capabilities vary by model. Treat GET /v1/models as the source of truth instead of hard-coding assumptions.
curl https://itzi.app/v1/models \
--header "Authorization: Bearer $ITZI_API_KEY"Catalog response shape
Values below illustrate the shape. Read the live response for current IDs, windows, prices and capabilities. Token prices are USD per million tokens.
{
"object": "list",
"data": [
{
"id": "model-id",
"object": "model",
"created": 0,
"owned_by": "itzi",
"itzi": {
"name": "Model name",
"release_stage": null,
"context_window": null,
"max_output_tokens": null,
"pricing": null,
"cache": {
"supported": false,
"automatic": false,
"ttlSeconds": null
},
"capabilities": {
"vision": false,
"custom_tools": false,
"web_search": false,
"python_studio": false
},
"tool_pricing": {
"web_search_per_call": null,
"python_studio_per_run": null
}
}
}
]
}| Field | Type | Status | Behavior |
|---|---|---|---|
| itzi.context_window | integer | null | metadata | Maximum combined input and reserved output tokens when published. |
| itzi.max_output_tokens | integer | null | metadata | Model output cap when published. |
| itzi.pricing | object | null | metadata | input, output and optional cache rates in USD per million tokens. |
| itzi.cache | object | metadata | Whether Auto Cache is supported, automatic, and its customer TTL. |
| itzi.capabilities | object | metadata | Vision, custom tools, web search and Python Studio flags. |
| itzi.tool_pricing | object | metadata | Current customer charge per tool call, or null when unsupported. |
Generation endpoints
The three endpoints share model routing, billing, caching and built-in tools, but preserve the request and response envelope expected by each client family.
/v1/responsesResponses
Best fit for new OpenAI-compatible integrations. It accepts string input or a stateless item history.
{
"model": "gpt-5.6-terra",
"input": "Explain cursor pagination in three sentences."
}| Field | Type | Status | Behavior |
|---|---|---|---|
| model | string | required | Exact model ID returned by GET /v1/models. |
| input | string | array | required | Plain text or up to 500 message, function_call and function_call_output items. |
| instructions | string | optional | System instruction, up to 1,000,000 characters. |
| stream | boolean | optional | Defaults to false. Set true for typed Responses SSE events. |
| max_output_tokens | integer | optional | 1-131072, then capped by the selected model. |
| temperature / top_p | number | optional | temperature is 0-2; top_p is 0-1. |
| tools / tool_choice | array / value | optional | Up to 64 tools with OpenAI Responses function shapes. |
| auto_cache / itzi_tools | boolean / string[] | optional | Per-request Anthropic cache preference and built-in tool controls. |
| force_hard_thinking | boolean | optional | Kimi K3 only. Privately applies the hard-thinking policy to the latest user input. Also accepted as itzi.force_hard_thinking. |
/v1/chat/completionsChat Completions
Best fit for existing OpenAI chat clients and explicit role-based histories.
{
"model": "gpt-5.6-terra",
"messages": [
{
"role": "user",
"content": "Explain cursor pagination in three sentences."
}
]
}| Field | Type | Status | Behavior |
|---|---|---|---|
| model | string | required | Exact model ID returned by GET /v1/models. Maximum 160 characters. |
| messages | array | required | 1-500 system, developer, user, assistant or tool messages. |
| stream | boolean | optional | Defaults to false. Set true for an SSE response ending with data: [DONE]. |
| max_completion_tokens | integer | optional | 1-131072. Takes precedence over max_tokens and is capped by the model. |
| max_tokens | integer | optional | Legacy output limit, 1-131072. The default is 8192 before model caps. |
| temperature | number | optional | 0-2. Ignored for models whose reasoning mode does not accept temperature. |
| top_p | number | optional | 0-1. |
| stop | string | string[] | optional | One sequence or up to 8 sequences, each at most 1000 characters. |
| tools | array | optional | Up to 64 custom function definitions or recognized built-in selectors. |
| tool_choice | string | object | optional | auto, none, required, or one named function. Responses accepts both the nested and flat named-function forms. |
| auto_cache | boolean | optional | Uses the API key setting when omitted. False disables Anthropic Auto Cache for one request; true cannot override a key created with it disabled. |
| force_hard_thinking | boolean | optional | Kimi K3 only. When true, Itzi privately prefixes the latest user message without changing the cache shape. Also accepted as itzi.force_hard_thinking. |
| itzi_tools | string[] | optional | Portable selector containing web_search, python, or both. |
/v1/messagesMessages
Anthropic-compatible messages, content blocks, tool use and cache usage fields.
{
"model": "claude-opus-4.8",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "Explain cursor pagination in three sentences."
}
]
}| Field | Type | Status | Behavior |
|---|---|---|---|
| model | string | required | Exact model ID returned by GET /v1/models. |
| messages | array | required | 1-500 user or assistant messages. Each may contain up to 100 blocks. |
| max_tokens | integer | required | 1-131072, then capped by the selected model. |
| system | string | array | optional | A string or up to 32 content blocks. |
| stream | boolean | optional | Defaults to false. Set true for Anthropic-style SSE events. |
| temperature / top_p | number | optional | Both accept values from 0 to 1. |
| stop_sequences | string[] | optional | Up to 8 strings, each at most 1000 characters. |
| tools / tool_choice | array / object | optional | Up to 64 Anthropic tool definitions and an Anthropic tool choice object. |
| auto_cache / itzi_tools | boolean / string[] | optional | Per-request Anthropic cache preference and built-in tool controls. |
| force_hard_thinking | boolean | optional | Kimi K3 only. Privately applies the hard-thinking policy to the latest user message. Also accepted as itzi.force_hard_thinking. |
Message and content rules
- OpenAI
systemanddevelopermessages are combined into the model system prompt. - Text fields accept up to 1,000,000 characters, but the entire JSON body is limited to 10 MiB and the model context window still applies.
- Public API calls are stateless. Send the complete relevant history on every turn; Itzi does not create a chat conversation for these requests.
- For Responses,
previous_response_idis not implemented. Include prior message and function items ininputinstead.
Vision inputs
Check capabilities.vision first. Image inputs accept HTTP, HTTPS or base64 data URLs; local file URLs are rejected.
{
"model": "gpt-5.6-terra",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Describe this image."
},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/image.png"
}
}
]
}
]
}OpenAI formats
User content may contain image_url or input_image. The URL may be a string or an object with a url field.
Anthropic formats
Use an image block with a source of type url or base64. Base64 sources must include media_type.
Built-in tools run inside Itzi
Web Search and Python Studio are server-executed. The model receives their results and continues within the same API request.
Use the portable selector
itzi_tools works on all three generation endpoints and avoids provider-specific tool version names.
{
"itzi_tools": ["web_search", "python"]
}| Field | Type | Status | Behavior |
|---|---|---|---|
| itzi_tools | ["web_search", "python"] | recommended | Portable across Responses, Chat Completions and Messages. |
| tools[].type | web_search* | alias | Any type beginning with web_search selects Itzi Web Search. |
| tools[].type | code_interpreter | alias | Selects Itzi Python Studio. |
| tools[].type | code_execution* | alias | Any type beginning with code_execution selects Itzi Python Studio. |
| itzi.tools | string[] | alias | Nested alias. Top-level itzi_tools takes precedence when both exist. |
Web Search
Current charge: $0.01 per search or extraction call. Availability depends on the model catalog.
{
"model": "gpt-5.6-terra",
"input": "Find the latest stable Next.js release and cite the official release notes.",
"itzi_tools": [
"web_search"
]
}Search sources are not returned as a separate top-level array. Ask for inline Markdown links when your application needs citations in the final text.
Python Studio
Current charge: $0.012 per sandbox run. The sandbox is private, ephemeral and has no internet access.
{
"model": "gpt-5.6-terra",
"input": "Create a CSV with the first 20 Fibonacci numbers. Return a Markdown link to every generated file.",
"itzi_tools": [
"python"
]
}Ask the model to return links. Generated file metadata is given to the model, while the generation envelope has no separate artifacts field.
Python Studio limits
Supported output extensions: pdf, docx, xlsx, pptx, png, jpg, webp, csv, txt, md, json, svg, html, zip, gif, mp4. Download links use signed /v1/files/{token} URLs that expire after one hour. The public API currently has no upload endpoint, so Python Studio requests cannot attach private input files.
Custom functions run in your application
Itzi returns the model's function call. Your code validates the arguments, executes the operation, appends the result, and sends a new request.
- DefineSend the name, description and JSON Schema.
- ReceiveRead every tool call and preserve its call ID.
- ExecuteValidate inputs and run your own trusted code.
- ContinueSend the full history plus matched results.
Complete Chat Completions loop
This example handles parallel calls, preserves the assistant tool-call message, matches each result by ID, checks HTTP failures and caps the loop.
const API_URL = "https://itzi.app/v1/chat/completions";
const apiKey = process.env.ITZI_API_KEY;
const weatherApiUrl = process.env.WEATHER_API_URL;
const weatherApiKey = process.env.WEATHER_API_KEY;
if (!apiKey || !weatherApiUrl || !weatherApiKey) {
throw new Error("ITZI_API_KEY, WEATHER_API_URL and WEATHER_API_KEY are required.");
}
const tools = [
{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather for a city.",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
additionalProperties: false,
},
},
},
];
const messages = [{ role: "user", content: "What is the weather in Paris?" }];
async function callItzi(body) {
const response = await fetch(API_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
const result = await response.json();
if (!response.ok) {
const requestId = response.headers.get("x-request-id") ?? "unknown request";
throw new Error(`${requestId}: ${result.error?.message ?? "Itzi request failed"}`);
}
return result;
}
async function runTool(name, input) {
if (name !== "get_weather" || typeof input?.city !== "string") {
throw new Error("Unsupported tool call.");
}
const url = new URL("/current", weatherApiUrl);
url.searchParams.set("city", input.city);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${weatherApiKey}` },
});
if (!response.ok) throw new Error("Weather service failed.");
return response.json();
}
for (let turn = 0; turn < 8; turn += 1) {
const completion = await callItzi({
model: "gpt-5.6-terra",
messages,
tools,
tool_choice: "auto",
});
const message = completion.choices?.[0]?.message;
if (!message) throw new Error("The model returned no message.");
messages.push({
role: "assistant",
content: message.content,
...(message.tool_calls ? { tool_calls: message.tool_calls } : {}),
});
const calls = message.tool_calls ?? [];
if (calls.length === 0) {
console.log(message.content);
break;
}
const results = await Promise.all(
calls.map(async (call) => ({
role: "tool",
name: call.function.name,
tool_call_id: call.id,
content: JSON.stringify(
await runTool(call.function.name, JSON.parse(call.function.arguments)),
),
})),
);
messages.push(...results);
if (turn === 7) throw new Error("The tool loop exceeded 8 model turns.");
}Result envelope by protocol
| Field | Type | Status | Behavior |
|---|---|---|---|
| Chat Completions | assistant.tool_calls[] | receive | Return a role=tool message matched with tool_call_id. |
| Responses | output[type=function_call] | receive | Return a function_call_output item matched with call_id. |
| Messages | content[type=tool_use] | receive | Return a user tool_result block matched with tool_use_id. |
Chat Completions follow-up body
{
"model": "gpt-5.6-terra",
"messages": [
{
"role": "user",
"content": "What is the weather in Paris?"
},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_weather_1",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
},
{
"role": "tool",
"name": "get_weather",
"tool_call_id": "call_weather_1",
"content": "{\"temperature_c\":22,\"conditions\":\"clear\"}"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, such as Paris."
}
},
"required": [
"city"
],
"additionalProperties": false
}
}
}
],
"tool_choice": "auto"
}Responses follow-up body
Itzi is stateless and does not implement previous_response_id, so the input repeats the original user item, function call and function output.
{
"model": "gpt-5.6-terra",
"input": [
{
"role": "user",
"content": "What is the weather in Paris?"
},
{
"type": "function_call",
"call_id": "call_weather_1",
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
},
{
"type": "function_call_output",
"call_id": "call_weather_1",
"output": "{\"temperature_c\":22,\"conditions\":\"clear\"}"
}
],
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string"
}
},
"required": [
"city"
],
"additionalProperties": false
}
}
],
"tool_choice": "auto"
}Anthropic Messages follow-up body
{
"model": "claude-opus-4.8",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "What is the weather in Paris?"
},
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "call_weather_1",
"name": "get_weather",
"input": {
"city": "Paris"
}
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "call_weather_1",
"content": "{\"temperature_c\":22,\"conditions\":\"clear\"}"
}
]
}
],
"tools": [
{
"name": "get_weather",
"description": "Get the current weather for a city.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string"
}
},
"required": [
"city"
],
"additionalProperties": false
}
}
],
"tool_choice": {
"type": "auto"
}
}Tool choice
| Field | Type | Status | Behavior |
|---|---|---|---|
| OpenAI automatic | "auto" | "none" | "required" | Chat / Responses | Let the model choose, disable tools, or require one tool call. |
| OpenAI named | {type:"function", function:{name}} | Chat / Responses | Force one available custom function by name. |
| Anthropic automatic | {type:"auto" | "none" | "any"} | Messages | any maps to a required tool call. |
| Anthropic named | {type:"tool", name:"..."} | Messages | Force one available custom function by name. |
Streaming is SSE, not one universal event format
Set stream to true. Itzi preserves the event vocabulary of the selected endpoint and uses content-type: text/event-stream.
curl --no-buffer https://itzi.app/v1/chat/completions \
--header "Authorization: Bearer $ITZI_API_KEY" \
--header "Content-Type: application/json" \
--data '{"model":"gpt-5.6-terra","messages":[{"role":"user","content":"Explain cursor pagination in three sentences."}],"stream":true}'Chat Completions
- 1
assistant role chunk - 2
content delta chunks - 3
complete tool_calls chunk - 4
final chunk with usage - 5
data: [DONE]
Responses
- 1
response.created - 2
response.output_item.added - 3
response.output_text.delta - 4
function call completion events - 5
response.completed
Messages
- 1
message_start - 2
content_block_start - 3
content_block_delta - 4
message_delta - 5
message_stop
- Chat custom function arguments are emitted as a complete tool call near the end, not as incremental argument fragments.
- Responses emits
response.function_call_arguments.donefor each custom call; it does not emit argument delta events. - A model failure after headers produces an SSE
errorevent. HTTP status is already 200 at that point, so clients must handle stream errors. - Disconnecting the client cancels the upstream model request.
Auto Cache and usage accounting
Anthropic Auto Cache is disabled by default and must be enabled when the API key is created. The live model catalog reports cache support and the current five-minute customer window.
Control precedence
- 1. API key: Anthropic Auto Cache must be enabled when the key is created. Request controls cannot bypass a disabled key.
- 2. Header:
x-itzi-auto-cache: true|falseoverrides the JSON body. - 3. Top level:
auto_cacheoverrides the nested extension. - 4. Nested:
itzi.auto_cacheis an accepted alias. - 5. Request default: enabled when the key allows it and no per-request control is supplied, then disabled automatically if the model cannot cache.
| Field | Type | Status | Behavior |
|---|---|---|---|
| Chat Completions | usage.prompt_tokens_details.cached_tokens | cache read | Billable cache writes appear in usage.itzi.cache_write_tokens. |
| Responses | usage.input_tokens_details.cached_tokens | cache read | Billable cache writes appear in usage.itzi.cache_write_tokens. |
| Messages | usage.cache_read_input_tokens | cache read | Writes appear in usage.cache_creation_input_tokens. |
Generation responses expose token usage, not the final USD charge. The authenticated request log shows request status, cache state, tool usage and the amount charged.
Errors, limits and request IDs
Non-streaming errors use the selected protocol envelope and include request_id. The same ID is exposed in the x-request-id header.
| Field | Type | Status | Behavior |
|---|---|---|---|
| 400 | invalid_request_error / invalid_tool / unsupported_* | client | Malformed schema, invalid tool, unsupported vision or unsupported tool. |
| 401 | authentication_error | auth | Missing, revoked, invalid key, or suspended account. |
| 402 | billing error | billing | The available balance does not authorize the request. |
| 409 | request_superseded | concurrency | A newer request replaced this request under the API key's subscription concurrency setting. |
| 404 | model_not_found | model | Model ID is absent, disabled or restricted to administrators. |
| 413 | invalid_request_error / context_window_exceeded | size | JSON body is too large or input plus reserved output exceeds context. |
| 429 | rate_limit_error / concurrency_limit | retry | Per-key rate or per-user concurrency limit reached. |
| 500 | request_failed | server | Generic safe error. Internal provider details are not exposed. |
Each API key can keep the default reject behavior or cancel its own oldest active subscription request when the plan concurrency limit is full. Requests from Itzi Chat and other API keys are never interrupted. Already consumed work remains billable.
Generation routes declare a maximum duration of 800 seconds. A deployment platform, reverse proxy or SDK timeout can impose a shorter limit.
Compatibility boundaries
Itzi implements the documented fields above. Compatibility means envelope and client interoperability, not every feature exposed by OpenAI or Anthropic.
Implemented
Text generation, stateless history, SSE streaming, image inputs, custom functions, Web Search, Python Studio, automatic caching, model discovery and protocol-shaped usage and errors.
Not currently implemented
Stored responses, previous_response_id, public file uploads, embeddings, audio, batches, Assistants, Realtime, and structured-output controls such as response_format.
CORS
Preflight supports GET, POST and OPTIONS from any origin. Allowed request headers are authorization, content-type, x-api-key, anthropic-version, anthropic-beta and x-itzi-auto-cache. CORS access does not make browser-side secret storage safe.
Ready to make a real request?
Create a key, then query /v1/models.