Skip to content
NEW SELECTThe new Itzi Select plans are here

One key. Every model.

OpenAI and Anthropic compatible APIs, one balance, and model prices 75% below provider list rates.

Four routes, one key

Use a Bearer token or x-api-key. Keys begin with itzi_sk_, are displayed once, and are stored only as hashes.

GET/v1/modelsAvailable models and capabilities
POST/v1/responsesOpenAI Responses format
POST/v1/chat/completionsOpenAI Chat Completions format
POST/v1/messagesAnthropic Messages format

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.

OpenAI clientshttps://itzi.app/v1
Anthropic SDKhttps://itzi.app

Direct HTTP

Every request is JSON. The response includes x-request-id; record it when troubleshooting.

cURL
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.

Node.js
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.

Node.js
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.

Request
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.

200 OK
{
  "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
        }
      }
    }
  ]
}
Model metadata
FieldTypeStatusBehavior
itzi.context_windowinteger | nullmetadataMaximum combined input and reserved output tokens when published.
itzi.max_output_tokensinteger | nullmetadataModel output cap when published.
itzi.pricingobject | nullmetadatainput, output and optional cache rates in USD per million tokens.
itzi.cacheobjectmetadataWhether Auto Cache is supported, automatic, and its customer TTL.
itzi.capabilitiesobjectmetadataVision, custom tools, web search and Python Studio flags.
itzi.tool_pricingobjectmetadataCurrent 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.

POST/v1/responses

Responses

Best fit for new OpenAI-compatible integrations. It accepts string input or a stateless item history.

Request body
{
  "model": "gpt-5.6-terra",
  "input": "Explain cursor pagination in three sentences."
}
Responses fields
FieldTypeStatusBehavior
modelstringrequiredExact model ID returned by GET /v1/models.
inputstring | arrayrequiredPlain text or up to 500 message, function_call and function_call_output items.
instructionsstringoptionalSystem instruction, up to 1,000,000 characters.
streambooleanoptionalDefaults to false. Set true for typed Responses SSE events.
max_output_tokensintegeroptional1-131072, then capped by the selected model.
temperature / top_pnumberoptionaltemperature is 0-2; top_p is 0-1.
tools / tool_choicearray / valueoptionalUp to 64 tools with OpenAI Responses function shapes.
auto_cache / itzi_toolsboolean / string[]optionalPer-request Anthropic cache preference and built-in tool controls.
force_hard_thinkingbooleanoptionalKimi K3 only. Privately applies the hard-thinking policy to the latest user input. Also accepted as itzi.force_hard_thinking.
POST/v1/chat/completions

Chat Completions

Best fit for existing OpenAI chat clients and explicit role-based histories.

Request body
{
  "model": "gpt-5.6-terra",
  "messages": [
    {
      "role": "user",
      "content": "Explain cursor pagination in three sentences."
    }
  ]
}
Chat Completions fields
FieldTypeStatusBehavior
modelstringrequiredExact model ID returned by GET /v1/models. Maximum 160 characters.
messagesarrayrequired1-500 system, developer, user, assistant or tool messages.
streambooleanoptionalDefaults to false. Set true for an SSE response ending with data: [DONE].
max_completion_tokensintegeroptional1-131072. Takes precedence over max_tokens and is capped by the model.
max_tokensintegeroptionalLegacy output limit, 1-131072. The default is 8192 before model caps.
temperaturenumberoptional0-2. Ignored for models whose reasoning mode does not accept temperature.
top_pnumberoptional0-1.
stopstring | string[]optionalOne sequence or up to 8 sequences, each at most 1000 characters.
toolsarrayoptionalUp to 64 custom function definitions or recognized built-in selectors.
tool_choicestring | objectoptionalauto, none, required, or one named function. Responses accepts both the nested and flat named-function forms.
auto_cachebooleanoptionalUses 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_thinkingbooleanoptionalKimi K3 only. When true, Itzi privately prefixes the latest user message without changing the cache shape. Also accepted as itzi.force_hard_thinking.
itzi_toolsstring[]optionalPortable selector containing web_search, python, or both.
POST/v1/messages

Messages

Anthropic-compatible messages, content blocks, tool use and cache usage fields.

Request body
{
  "model": "claude-opus-4.8",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": "Explain cursor pagination in three sentences."
    }
  ]
}
Messages fields
FieldTypeStatusBehavior
modelstringrequiredExact model ID returned by GET /v1/models.
messagesarrayrequired1-500 user or assistant messages. Each may contain up to 100 blocks.
max_tokensintegerrequired1-131072, then capped by the selected model.
systemstring | arrayoptionalA string or up to 32 content blocks.
streambooleanoptionalDefaults to false. Set true for Anthropic-style SSE events.
temperature / top_pnumberoptionalBoth accept values from 0 to 1.
stop_sequencesstring[]optionalUp to 8 strings, each at most 1000 characters.
tools / tool_choicearray / objectoptionalUp to 64 Anthropic tool definitions and an Anthropic tool choice object.
auto_cache / itzi_toolsboolean / string[]optionalPer-request Anthropic cache preference and built-in tool controls.
force_hard_thinkingbooleanoptionalKimi 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 system and developer messages 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_id is not implemented. Include prior message and function items in input instead.

Vision inputs

Check capabilities.vision first. Image inputs accept HTTP, HTTPS or base64 data URLs; local file URLs are rejected.

Chat Completions request
{
  "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.

Request fragment
{
  "itzi_tools": ["web_search", "python"]
}
Equivalent built-in selectors
FieldTypeStatusBehavior
itzi_tools["web_search", "python"]recommendedPortable across Responses, Chat Completions and Messages.
tools[].typeweb_search*aliasAny type beginning with web_search selects Itzi Web Search.
tools[].typecode_interpreteraliasSelects Itzi Python Studio.
tools[].typecode_execution*aliasAny type beginning with code_execution selects Itzi Python Studio.
itzi.toolsstring[]aliasNested 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.

Responses
{
  "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.

Responses
{
  "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

3sandbox runs per request
4 minmaximum per Python run
8 minPython time per request
8generated files per request
20 MiBmaximum per generated file

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.

  1. DefineSend the name, description and JSON Schema.
  2. ReceiveRead every tool call and preserve its call ID.
  3. ExecuteValidate inputs and run your own trusted code.
  4. 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.

Node.js
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

Custom function call mapping
FieldTypeStatusBehavior
Chat Completionsassistant.tool_calls[]receiveReturn a role=tool message matched with tool_call_id.
Responsesoutput[type=function_call]receiveReturn a function_call_output item matched with call_id.
Messagescontent[type=tool_use]receiveReturn a user tool_result block matched with tool_use_id.
Chat Completions follow-up body
POST /v1/chat/completions
{
  "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.

POST /v1/responses
{
  "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
POST /v1/messages
{
  "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

Supported tool choice forms
FieldTypeStatusBehavior
OpenAI automatic"auto" | "none" | "required"Chat / ResponsesLet the model choose, disable tools, or require one tool call.
OpenAI named{type:"function", function:{name}}Chat / ResponsesForce one available custom function by name.
Anthropic automatic{type:"auto" | "none" | "any"}Messagesany maps to a required tool call.
Anthropic named{type:"tool", name:"..."}MessagesForce 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
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. 1assistant role chunk
  2. 2content delta chunks
  3. 3complete tool_calls chunk
  4. 4final chunk with usage
  5. 5data: [DONE]

Responses

  1. 1response.created
  2. 2response.output_item.added
  3. 3response.output_text.delta
  4. 4function call completion events
  5. 5response.completed

Messages

  1. 1message_start
  2. 2content_block_start
  3. 3content_block_delta
  4. 4message_delta
  5. 5message_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.done for each custom call; it does not emit argument delta events.
  • A model failure after headers produces an SSE error event. 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. 1. API key: Anthropic Auto Cache must be enabled when the key is created. Request controls cannot bypass a disabled key.
  2. 2. Header: x-itzi-auto-cache: true|false overrides the JSON body.
  3. 3. Top level: auto_cache overrides the nested extension.
  4. 4. Nested: itzi.auto_cache is an accepted alias.
  5. 5. Request default: enabled when the key allows it and no per-request control is supplied, then disabled automatically if the model cannot cache.
Usage fields
FieldTypeStatusBehavior
Chat Completionsusage.prompt_tokens_details.cached_tokenscache readBillable cache writes appear in usage.itzi.cache_write_tokens.
Responsesusage.input_tokens_details.cached_tokenscache readBillable cache writes appear in usage.itzi.cache_write_tokens.
Messagesusage.cache_read_input_tokenscache readWrites 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.

120/minrequests per API key
3concurrent requests per user
10 MiBmaximum JSON request body
HTTP errors
FieldTypeStatusBehavior
400invalid_request_error / invalid_tool / unsupported_*clientMalformed schema, invalid tool, unsupported vision or unsupported tool.
401authentication_errorauthMissing, revoked, invalid key, or suspended account.
402billing errorbillingThe available balance does not authorize the request.
409request_supersededconcurrencyA newer request replaced this request under the API key's subscription concurrency setting.
404model_not_foundmodelModel ID is absent, disabled or restricted to administrators.
413invalid_request_error / context_window_exceededsizeJSON body is too large or input plus reserved output exceeds context.
429rate_limit_error / concurrency_limitretryPer-key rate or per-user concurrency limit reached.
500request_failedserverGeneric 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.

Open API dashboard