API
API Reference
Reference for inference endpoints, request shapes, and response schemas
The public relay exposes model inventory and HTTP inference, with Server-Sent Events (SSE) for streaming. Protocol compatibility depends on the serving client, inference engine, and model capabilities.
Follow API Quickstart for executable requests.
Base URL and authentication#
Use https://api.actual.inc and send an inference credential from User > Keys:
Authorization: Bearer ac_your_credential_here
Console Chat uses your signed-in web session. Device-management authentication is separate from public inference credentials.
Supported public inference routes#
| Endpoint | Method | Compatibility | Streaming |
|---|---|---|---|
/v1/models | GET | OpenAI-style inventory with Actual extensions | No |
/v1/chat/completions | POST | OpenAI Chat Completions | SSE |
/v1/completions | POST | Legacy text completions | SSE |
/v1/responses | POST | OpenResponses | SSE |
/v1/messages | POST | Anthropic Messages | SSE |
The current relay does not serve public cluster-listing, model-loading, or legacy session-WebSocket endpoints. Use actual clusters --format json for the cluster roster, and the CLI or website for model installation and loading.
Cluster targeting and readiness#
Send X-Cluster-ID to scope inventory or inference to a specific cluster:
X-Cluster-ID: cl_example
Discover cluster IDs from clusters[].cluster_id in model inventory or the authenticated CLI. Without this header, inventory aggregates your account's clusters. Inference filters eligible online nodes by the loaded model and required protocol or media capabilities, then selects among them. It does not select a fixed first cluster.
Installed is not loaded. A model must be resident and ready on an eligible node before inference. Cluster connectivity also does not establish that a model executes across multiple machines.
GET /v1/models#
Returns models advertised by online devices, including installed models available to load and currently loaded models.
| Field | Meaning |
|---|---|
id | Public model identifier. May be a display name, or a stable selector when names collide. |
selector | Optional stable selector for a particular installed model. |
display_name / canonical_name | Optional descriptive names. |
object, created, owned_by | Model metadata; owned_by is actual. |
loaded | Whether the model is loaded anywhere in the returned inventory. Missing or false is not readiness. |
clusters | Cluster availability, with cluster_id and optional per-cluster loaded. |
Use a returned id or selector as the request's model. For pinned inference, check loading on that specific cluster instead of using the aggregate flag.
Illustrative response:
{
"object": "list",
"data": [{
"id": "Gemma 4 12B IT",
"object": "model",
"created": 1700000000,
"owned_by": "actual",
"selector": "mdl_a81e2d366803a648",
"display_name": "Gemma 4 12B IT",
"loaded": true,
"clusters": [
{"cluster_id": "cl_loaded", "loaded": true},
{"cluster_id": "cl_installed", "loaded": false}
]
}]
}
POST /v1/chat/completions#
Provide a model identifier and an array of messages. This example uses an illustrative model selector; replace it with inventory output.
{
"model": "mdl_a81e2d366803a648",
"messages": [
{"role": "system", "content": "Answer concisely."},
{"role": "user", "content": "Say hello."}
],
"max_tokens": 128,
"stream": false
}
Common optional sampling fields include temperature, top_p, frequency_penalty, presence_penalty, and stop. Supported fields, bounds, defaults, reasoning levels, and tool behavior depend on the serving engine and model; the website's Chat defaults are not universal API defaults.
A successful non-streaming response includes choices[].message, finish_reason, and usage when available:
{
"id": "chatcmpl-example",
"object": "chat.completion",
"created": 1700000000,
"model": "mdl_a81e2d366803a648",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "Hello!"},
"finish_reason": "stop"
}],
"usage": {"prompt_tokens": 12, "completion_tokens": 3, "total_tokens": 15}
}
With stream: true, consume SSE data events and accumulate deltas. A simplified text stream is:
data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"Hello!"},"finish_reason":null}]}
data: [DONE]
Handle tool-call deltas and stream errors separately from text. A chunk arriving is not proof that generation completed successfully.
POST /v1/completions#
The legacy text-completion route accepts model, prompt, and optional generation controls such as max_tokens, temperature, top_p, stop, and stream. Prefer Chat Completions or Responses for new conversational integrations.
{
"model": "mdl_a81e2d366803a648",
"prompt": "Complete this sentence: The computer",
"max_tokens": 64,
"stream": false
}
Non-streaming completions return text in choices[].text, with finish reason and usage where available.
POST /v1/responses#
Provide model and input, which can be text or an array of input items. Common fields include:
| Field | Use |
|---|---|
instructions | System instructions. |
max_output_tokens | Output token limit. |
stream | Enable SSE. |
tools, tool_choice | Tool definitions and selection, when supported by the model and engine. |
reasoning | Reasoning settings, including model-supported effort levels. |
previous_response_id | Continue a previous response when it is still available. |
temperature, top_p, top_k, min_p | Sampling controls supported by the runtime. |
frequency_penalty, presence_penalty, repetition_penalty | Penalty controls. |
seed, stop, metadata | Additional generation and request metadata. |
Do not assume every runtime supports every optional field. A previous response may become unavailable after restarting or changing the serving computer. Keep your conversation history so the application can recover if continuation fails.
Example with a function tool:
{
"model": "mdl_a81e2d366803a648",
"input": "What is the temperature in Boston?",
"tools": [{
"type": "function",
"name": "get_temperature",
"description": "Get the current temperature for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}],
"max_output_tokens": 256
}
Your application validates and executes any requested function, then returns a function_call_output using the returned call_id. The inference endpoint does not execute your application functions for you.
A text response has an output array containing message items:
{
"id": "resp-example",
"object": "response",
"status": "completed",
"output": [{
"type": "message",
"role": "assistant",
"content": [{"type": "output_text", "text": "Hello!"}]
}],
"error": null
}
Streaming uses named events. Check the terminal event and response status.
| Event | Meaning |
|---|---|
response.created | Response created. |
response.in_progress | Processing started. |
response.output_item.added | New output item. |
response.output_text.delta | Incremental text. |
response.output_text.done | Text complete. |
response.function_call_arguments.delta | Incremental tool arguments. |
response.function_call_arguments.done | Tool arguments complete. |
response.output_item.done | Output item complete. |
response.completed | Response complete. |
response.failed | Response failed. |
POST /v1/messages#
The Anthropic-compatible route accepts model, messages, max_tokens, and optional system, tools, and stream. Use the same bearer authentication and cluster header as the other public routes.
{
"model": "mdl_a81e2d366803a648",
"max_tokens": 128,
"messages": [{"role": "user", "content": "Say hello."}],
"stream": false
}
Responses use the Anthropic-style message shape; streaming uses Anthropic-style SSE events. Client compatibility still depends on the selected model's capabilities and the application's provider configuration.
Images, audio, and request size#
Use a model and serving runtime that advertise the required input capabilities. Console Chat currently accepts text only. See Vision & Audio for application and file guidance.
The current standard request limit is 16 MiB for the complete prepared body, including encoded attachments. A smaller source file can become a larger encoded request.
Errors#
Inspect both HTTP status and content type. Errors are not uniformly JSON. For example, a missing bearer credential can produce a 401 plain-text body:
missing_or_malformed_credential
A no-node condition can produce 503 with plain text no node for user. Other failures return structured JSON. Preserve the received diagnostic instead of assuming legacy error codes such as invalid_api_key or user_offline.
| Status | Typical interpretation |
|---|---|
| 400 | Invalid parameters or unsupported request shape. |
| 401 / 403 | Authentication or authorization failed. |
| 404 | Unknown route or unavailable target; inspect the body. |
| 413 | Prepared request body exceeds the limit. |
| 429 | Rate limit or capacity policy; honor retry information when supplied. |
| 503 | No eligible serving node or temporary service unavailability. |
Refresh inventory after disconnects or model changes. Retry transient failures with backoff; correct credentials, unsupported inputs, and invalid routes before retrying.