GPU service in validation
Build with Vuto.
OpenAI-compatible chat completions. GPU inference is still being validated.
Sign in to create and manage API keys in your console.
API essentials
Use your OpenAI client.
Set baseURL, your key, and the model ID.
export VUTO_BASE_URL="https://vuto.ai/v1"
export VUTO_API_KEY="paste_your_key_here"
curl "$VUTO_BASE_URL/chat/completions" \
-H "Authorization: Bearer $VUTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "vuto-code-1",
"reasoning_effort": "low",
"messages": [
{"role": "user", "content": "Say hello in one sentence."}
]
}'import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://vuto.ai/v1",
apiKey: process.env.VUTO_API_KEY
});
const completion = await client.chat.completions.create({
model: "vuto-code-1",
reasoning_effort: "low",
messages: [{ role: "user", content: "Say hello in one sentence." }]
});
console.log(completion.choices[0].message.content);import os
from openai import OpenAI
client = OpenAI(
base_url="https://vuto.ai/v1",
api_key=os.environ["VUTO_API_KEY"],
)
completion = client.chat.completions.create(
model="vuto-code-1",
reasoning_effort="low",
messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(completion.choices[0].message.content)Authenticate every request.
Every /v1 endpoint, including GET /v1/models, requires a bearer key. Anthropic clients can send the same key in an x-api-key header instead.
| Base URL | https://vuto.ai/v1 |
|---|---|
| Model | vuto-code-1 |
| Header | Authorization: Bearer <your key> |
| Account | Sign in at /console to view balance and usage history. |
Keys are shown once, at creation, and can't be re-displayed. Call this API from a server you control, not directly from browser JavaScript: the API does not send CORS headers for cross-origin requests, and a bearer key placed in client-side code is visible to anyone who opens it.
Stream a response.
Set "stream": true and the response becomes
text/event-stream: a series of chat.completion.chunk
objects ending with data: [DONE].
A final chunk carries usage by default. Send
"stream_options": {"include_usage": false} to leave it out, or
{"include_usage": true} to make the request explicit. The official
OpenAI SDKs send that automatically from their .stream() helper.
stream_options on a request that isn't streaming returns
400 unsupported_parameter.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://vuto.ai/v1",
apiKey: process.env.VUTO_API_KEY
});
const stream = client.chat.completions.stream({
model: "vuto-code-1",
messages: [{ role: "user", content: "Say hello in one sentence." }]
});
stream.on("content.delta", ({ delta }) => process.stdout.write(delta));
const completion = await stream.finalChatCompletion();
console.log("\n" + completion.choices[0].message.content);import os
from openai import OpenAI
client = OpenAI(
base_url="https://vuto.ai/v1",
api_key=os.environ["VUTO_API_KEY"],
)
with client.chat.completions.stream(
model="vuto-code-1",
messages=[{"role": "user", "content": "Say hello in one sentence."}],
) as stream:
for event in stream:
if event.type == "content.delta":
print(event.delta, end="", flush=True)
completion = stream.get_final_completion()
print("\n" + completion.choices[0].message.content)Use Claude Code and Anthropic SDKs.
Vuto also answers the Anthropic Messages API at POST /v1/messages.
Set the client's base URL to https://vuto.ai without /v1,
because Anthropic clients add the path themselves, and use your Vuto key.
Claude Code
{
"env": {
"ANTHROPIC_BASE_URL": "https://vuto.ai",
"ANTHROPIC_AUTH_TOKEN": "paste_your_key_here",
"ANTHROPIC_MODEL": "vuto-code-1",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "vuto-code-1",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "vuto-code-1",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "vuto-code-1",
"CLAUDE_CODE_MAX_CONTEXT_TOKENS": "131072",
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8192"
}
}export ANTHROPIC_BASE_URL="https://vuto.ai"
export ANTHROPIC_AUTH_TOKEN="$VUTO_API_KEY"
export ANTHROPIC_MODEL="vuto-code-1"
export ANTHROPIC_DEFAULT_OPUS_MODEL="vuto-code-1"
export ANTHROPIC_DEFAULT_SONNET_MODEL="vuto-code-1"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="vuto-code-1"
export CLAUDE_CODE_MAX_CONTEXT_TOKENS=131072
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=8192
claude
Put the env block in ~/.claude/settings.json, not in a project
file you commit: it holds your key. ANTHROPIC_AUTH_TOKEN sends the key as a
bearer token and ANTHROPIC_API_KEY sends it as x-api-key; Vuto
accepts both. Claude Code asks for a model by tier for background work, so the three
ANTHROPIC_DEFAULT_*_MODEL variables point every tier at
vuto-code-1, and Vuto also serves any claude-* model name with
vuto-code-1. The model reads 131,072 tokens of context, while Claude Code
assumes 200,000 for a model it does not know: CLAUDE_CODE_MAX_CONTEXT_TOKENS
makes it compact the conversation before it outgrows that window. A request that still
overflows gets a prompt is too long error, which Claude Code also answers by
compacting. Replies stop at 8,192 tokens, and
CLAUDE_CODE_MAX_OUTPUT_TOKENS gives Claude Code the same budget.
Anthropic SDKs and OpenCode
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://vuto.ai",
api_key=os.environ["VUTO_API_KEY"],
)
message = client.messages.create(
model="vuto-code-1",
max_tokens=1024,
output_config={"effort": "low"},
messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(message.content[0].text)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://vuto.ai",
apiKey: process.env.VUTO_API_KEY
});
const message = await client.messages.create({
model: "vuto-code-1",
max_tokens: 1024,
output_config: { effort: "low" },
messages: [{ role: "user", content: "Say hello in one sentence." }]
});
console.log(message.content[0].text);{
"$schema": "https://opencode.ai/config.json",
"provider": {
"vuto": {
"npm": "@ai-sdk/openai-compatible",
"name": "Vuto",
"options": {
"baseURL": "https://vuto.ai/v1",
"apiKey": "{env:VUTO_API_KEY}"
},
"models": {
"vuto-code-1": {
"name": "vuto-code-1",
"limit": { "context": 131072, "output": 8192 }
}
}
}
},
"model": "vuto/vuto-code-1"
}
The SDKs stream with stream=True or their messages.stream()
helpers. output_config.effort sets the model's reasoning effort to
low, medium or high; xhigh and
max run as high. Leave it out and the model reasons at its
default, which can spend a short max_tokens budget before any visible text.
OpenCode reaches Vuto through chat completions: opencode.json declares Vuto as
a custom provider with the OpenAI-compatible package, as the OpenCode providers guide
describes.
Messages
POST /v1/messages
| Field | Handling |
|---|---|
model |
vuto-code-1 or one of its aliases. A claude-* name runs on vuto-code-1, and every response names vuto-code-1. |
messages, system |
Text, tool_use and tool_result blocks, plus system turns inside the conversation. Vuto drops thinking blocks from earlier turns. Vuto does not support response prefill, so the list cannot end with an assistant message. |
max_tokens |
Required. A value above 8192 runs as 8192. |
stream |
Anthropic events from message_start to message_stop. Text arrives as the model writes it; each tool_use block arrives whole when the turn ends. message_start carries an estimate of input_tokens, and message_delta carries the billed usage. |
tools, tool_choice |
Custom tools only: names up to 128 characters, descriptions up to 8,192, and no cap on the count. Claude Code sends the schema of every connected MCP tool with each request, so each one uses context. auto and any leave the call to the model, so Vuto does not enforce any; tool names one function; none removes the tools. |
thinking, output_config.effort |
Set the reasoning effort. Vuto never returns thinking text. |
temperature, top_p, stop_sequences, metadata.user_id |
Vuto passes them to the model, with up to 4 stop sequences, and hashes user_id the same way as user on chat completions. |
cache_control, context_management, service_tier, top_k |
Vuto accepts and ignores them. |
Server tools, mcp_servers, container, safeguards, output_config.format |
400 invalid_request_error: Vuto runs no web search, web fetch, code execution, MCP connector, tool-use classifier or structured output enforcement. Claude Code then resends without safeguards and keeps its own auto mode checks. |
The model reads text only. Vuto refuses an image or document in a user message with
capability_rejected: image_block (or document_block), so
Claude Code removes it and retries, and replaces one inside a tool_result
with a short note. No message or tool count cap applies: the 512 KiB body limit and the
131,072-token context bound a request, and past either Vuto answers
prompt is too long, which makes Claude Code compact the conversation.
Claude Code sends one request
per model turn, and each one counts toward the per-key request rate under
Rate limits. /v1/messages/count_tokens is not
available. After an idle period the first request can return 503 with an
overloaded_error that carries the model_warming or
model_offline message and a Retry-After header; Vuto does not
charge the failed request.
Errors on /v1/messages use Anthropic's shape,
{"type":"error","error":{"type":...,"message":...}}, and keep the HTTP
status, Retry-After and x-request-id: 400
invalid_request_error, 401 authentication_error, 404
not_found_error, 413 request_too_large, 429
rate_limit_error for rate limits and insufficient balance, 503
overloaded_error, other 5xx api_error. Insufficient balance,
model_warming and model_offline carry
x-should-retry: false, so the SDKs return them at once instead of waiting
out Retry-After.
API reference
Authenticated endpoints for models, chat, decisions and usage.
Models
GET /v1/models
Lists configured public models and their current rates. When JEV is enabled, vuto-jev-1 lists /v1/decisions as its endpoint.
|
vuto-code-1 is Vuto's stable public model id. It is a transparent alias over the configured preview upstream, not a claim of proprietary weights or verified Vuto-hosted inference. vuto-grandmaster-1 is the name on the box, and vuto-lilt, vuto-lilt-01 and vuto-5.2 remain accepted legacy aliases.
Chat completions
POST /v1/chat/completions
Generates a reply as JSON, or as an SSE stream when stream is
true. Request bodies over 524,288 bytes (512 KiB) are rejected with
413 request_too_large.
| Name | Type | Notes |
|---|---|---|
model Required |
string | Must be vuto-code-1, the id returned by GET /v1/models. |
messages Required |
array |
1 to 64 messages; combined message content up to 200,000 characters. Each message: |
stream Optional |
boolean | Default false. |
stream_options Optional |
object | { include_usage: boolean }. Only valid when stream is true. See Streaming. |
max_tokens / max_completion_tokens Optional |
integer | Aliases for the same value; if both are sent they must match. 1 to 8192, default 1024. |
temperature Optional |
number | 0 to 2. |
reasoning_effort Optional |
string | low, medium, high, or max. Supported by vuto-code-1 for JSON and streaming; medium maps to high. Omission keeps the model default, currently max. Use low for short output budgets: reasoning at max can leave no visible text. Other values, including null, return 400 invalid_parameter; models without this capability return 400 unsupported_parameter. Also accepted by the MCP vuto_chat tool. |
top_p Optional |
number | 0 to 1. |
stop Optional |
string or array | A string, or 1 to 4 strings, each ≤1024 characters. |
tools Optional |
array | 1 to 128 function definitions, when the selected model supports tools. Each definition has type: "function" and a function object with name, optional description (up to 8,192 characters), parameters (a JSON schema object), and strict (boolean). |
tool_choice Optional |
string or object | Requires tools. Use none, auto, required, or {"type":"function","function":{"name":"your_function"}} naming one of the supplied tools. |
user Optional |
string | ≤512 characters. Hashed into an opaque per-caller identifier server-side; Vuto does not store or forward the raw value. |
Function calling depends on the selected model's provider capabilities.
Models without tool support reject tools and tool_choice
with 400 unsupported_parameter. Any request key outside the table
above, including n, response_format,
seed, logprobs, presence_penalty, and
frequency_penalty, returns 400 unsupported_parameter.
|
finish_reason is one of stop, length, tool_calls, or content_filter.
Typed decisions (JEV)
POST /v1/decisions
Use the same bearer key and prepaid balance as chat. Send text or JSON state and 1 to 32 named questions. Each question has text instructions and a type: noul returns the probability of yes; choice takes 2 to 255 named options in a criteria object; score takes 2 to 10 ordered text levels in a criteria array. Choice descriptions may be text or null. Noul criteria may describe true and false.
{"model":"vuto-jev-1","state":"The export button fails.","questions":{"urgent":{"type":"noul","instructions":"Is this urgent?"}}}
The response contains answers under your question IDs and usage.input_tokens / usage.output_tokens. Choice and score include probabilities and confidence; a score uses zero-based levels. These are judgments, not guarantees. JEV accepts no image/audio inputs or streaming. Unknown request fields are rejected; the body limit is 512 KiB and the provider context limit is 64k tokens per question.
JEV uses the default model's current rate card, with zero cached tokens. A temporary credit hold covers up to 64k input and 64k output tokens per question; only reported usage is charged, and the remainder is released. Calls fail with 503 feature_unavailable until Vuto configures its dedicated provider key. Upstream credit exhaustion returns 503 provider_quota_exhausted.
Public GET /__vuto/features returns {"jev_enabled":false} or {"jev_enabled":true}. Show a JEV badge only when true. This reports configuration, not a live provider health check.
Usage
GET /v1/usage
Reads current prepaid balance and recorded usage for one UTC day. Omit day for today, or request /v1/usage?day=2026-09-10 for a past date. Invalid, future or repeated dates return 400 invalid_usage_day. The balance remains current.
Agents can call vuto_usage on /mcp with the same bearer key. Pass {"day":"2026-09-10"} as tool arguments, or {} for today. It returns recorded usage as JSON text and structuredContent, without running inference or spending credits. Request limits still apply.
Totals cover all models on your account. When a model registry is available, default_model identifies the model priced by rate_card, and rate_cards lists each public model with its current rates in integer microcredits per million tokens. These rates apply to new requests; recorded charges keep their original prices.
|
The per-day totals (inputTokens, cachedInputTokens,
outputTokens, costMicrocredits, requests,
recent) use their internal camelCase names rather than the snake_case
used elsewhere in this response, reproduced above exactly as the API returns them.
recent holds up to the 20 most recent settled requests for the day,
newest first.
Errors
Every error outside /v1/messages uses the same envelope; /v1/messages answers in Anthropic's error shape. Every response also carries an x-request-id header.
|
type is one of authentication_error, invalid_request_error,
rate_limit_error, insufficient_quota, provider_error,
model_warming, model_offline, or internal_error.
| Status | Code | When |
|---|---|---|
| 400 | invalid_parameter | A field failed validation. See param. |
| 400 | unsupported_parameter | An unknown or currently-unavailable field was sent. See param. |
| 400 | invalid_json | The request body is not valid JSON. |
| 400 | context_length_exceeded | The prompt and max_tokens exceed the model's context. The message starts with prompt is too long, usually followed by N tokens > M maximum. Shorten the conversation; the same request fails again. |
| 401 | invalid_api_key | The bearer key is missing, malformed, or revoked. |
| 403 | https_required | The request used plain HTTP. Use https://vuto.ai/v1 and revoke any key sent without HTTPS. |
| 404 | model_not_found | model is not vuto-code-1 or one of its aliases. |
| 404 | not_found | The path is not a documented /v1 endpoint. |
| 405 | method_not_allowed | Wrong HTTP method for a valid path. |
| 413 | request_too_large | Request body over 512 KiB. |
| 429 | rate_limit_exceeded | Per-key or platform request rate exceeded. Retry-After header is set. |
| 429 | insufficient_quota | Account balance can't cover the request's worst-case cost. |
| 429 | provider_rate_limited | The upstream model is temporarily rate limited. Retry. |
| 499 | request_aborted | The client disconnected before the response completed. |
| 502 | provider_error / invalid_provider_response / invalid_provider_usage / provider_response_too_large | The upstream model returned something Vuto couldn't validate. Retry. |
| 503 | model_warming | The model is starting or may be starting. Read Retry-After. The failed request is not charged. |
| 503 | model_offline | No GPU is running. Retry after 30 minutes. The failed request is not charged. |
| 504 | provider_timeout | The upstream model did not respond within 300 seconds. |
| 500 | internal_error / accounting_error | An unexpected server-side failure. |
If a stream disconnects mid-response after the 200 status has already
been sent, Vuto ends it with an inline error chunk followed by
data: [DONE] rather than closing silently.
Rate limits & free keys
Current limits for the beta. All of these can change as the beta progresses.
Per-key request rate
Requests beyond this return 429 rate_limit_exceeded with a Retry-After header.
Platform-wide request rate
Shared across every key while the beta is capacity-limited.
Free key balance
Free keys are not currently being issued.
Free keys per IP address
Beyond this, or beyond 100 issued platform-wide per day, requests return 429 with a Retry-After header.
Chat completion requests time out with 504 provider_timeout after 300 seconds without an upstream response.
Access & billing
Key availability follows the current beta configuration. Paid credit packs are listed below.
- Keys
- Created once and shown once
- Balance
- Available from
/v1/usage - Billing
- Prepaid credit packs
- Prompts
- Vuto keeps the text of the latest message in each API request for up to 30 days so the team can review usage and prevent abuse.
5 credits
Pack credits-5. Settled in USDC on Base, charged once.
27 credits
Pack credits-25. Settled in USDC on Base, charged once.
115 credits
Pack credits-100. Settled in USDC on Base, charged once.
Credits are prepaid, non-transferable, and do not expire on their own. Card checkout is unavailable. Check model availability in your console before buying.