Build with the AuraRoute API
One OpenAI-compatible endpoint. Pick a workload, we pick the model.
Try it
Send a real request from your browser before writing any code. Pick a workload, paste a key and chat — answers stream live and the model's thinking appears as it is produced. Your conversations are saved in this browser only.
Quick start
AuraRoute speaks the OpenAI API. Point any OpenAI SDK at the base URL below, use your AuraRoute key, and set model to a workload — you never name a model; we pick the right one.
| Base URL | https://auraroute.dev/api |
| Endpoint | POST /chat/completions |
| Auth | Authorization: Bearer <your key> |
1. Create a key
Create a free account, then create a key under API keys. It is shown once.
2. Keep it in an environment variable
export AURAROUTE_API_KEY=az_live_…
$env:AURAROUTE_API_KEY = "az_live_…"3. Send a request
curl https://auraroute.dev/api/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AURAROUTE_API_KEY" \
-d '{
"model": "auto",
"messages": [{ "role": "user", "content": "Say hello in one sentence." }]
}'
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://auraroute.dev/api',
apiKey: process.env.AURAROUTE_API_KEY,
});
const res = await client.chat.completions.create({
model: 'auto',
messages: [{ role: 'user', content: 'Say hello in one sentence.' }],
});
console.log(res.choices[0].message.content);
import os
from openai import OpenAI
client = OpenAI(
base_url="https://auraroute.dev/api",
api_key=os.environ["AURAROUTE_API_KEY"],
)
res = client.chat.completions.create(
model="auto",
messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(res.choices[0].message.content)Agents
Hermes and OpenClaw use the same three values as any other client: base URL https://auraroute.dev/api, your key, and model set to agent. Each guide is the commands in order, then one bash script that installs the agent and writes the config.
Authentication
Send your key as a bearer token. Keys look like az_live_<id>_<secret>. You can hold several, and revoke any of them from the dashboard — a revoked key stops working within seconds.
Only a hash of each key is stored, so it cannot be shown again after creation. If you lose it, revoke it and make a new one.
Choosing a workload
The model field takes a workload, not a model name. AuraRoute routes each request to the best available model for it, and falls back automatically if a provider is down — you never need to pick or change a model. Anything else (for example gpt-4o) is rejected with a list of valid values.
| model | Use it for |
|---|---|
| auto | Not sure? We look at the request and pick: tools → agent, code → coding, very long or research-style → research, otherwise chat. |
| chat | Everyday conversation, questions, writing and summarising. |
| coding | Writing, explaining, reviewing and debugging code. |
| research | Long inputs and in-depth analysis, where reasoning over a lot of text matters. |
| agent | Tool calling and multi-step tasks. Any request with `tools` is routed here. |
List them any time with GET /models.
curl https://auraroute.dev/api/models \
-H "Authorization: Bearer $AURAROUTE_API_KEY"
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://auraroute.dev/api',
apiKey: process.env.AURAROUTE_API_KEY,
});
const models = await client.models.list();
console.log(models.data.map((m) => m.id));
import os
from openai import OpenAI
client = OpenAI(
base_url="https://auraroute.dev/api",
api_key=os.environ["AURAROUTE_API_KEY"],
)
print([m.id for m in client.models.list().data])Chat completions
POST /chat/completions follows the OpenAI request and response shape. Parameters such as temperature, top_p and stop are passed through.
| Field | Notes |
|---|---|
| model | One of auto, chat, coding, research, agent. Defaults to auto. |
| messages | Required. Array of { role, content }. Images as image_url parts are supported by vision-capable models. |
| stream | true streams the answer as server-sent events. See Streaming. |
| stream_options.include_usage | true adds a final chunk with token counts. |
| max_tokens / max_completion_tokens | Capped at the platform limit; if omitted, the cap is used. |
| tools | Function tools. Their presence routes the request to agent models. See Tools. |
| n | Must be 1. |
Fields that would steer the upstream — models, provider, route, plugins, preset, transforms — are removed. In responses, id is ours and model echoes the workload you sent.
Streaming & thinking
Set stream: true to receive the answer as it is generated. Models that think before answering send that thinking first, in delta.reasoning_content or delta.reasoning (we pass along whichever the model uses), followed by the answer in delta.content. Some models instead wrap thinking in <think>…</think> inside the content. Show or hide it as you like; do not send it back in your next request.
curl -N https://auraroute.dev/api/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AURAROUTE_API_KEY" \
-d '{
"model": "chat",
"stream": true,
"stream_options": { "include_usage": true },
"messages": [{ "role": "user", "content": "Explain recursion briefly." }]
}'
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://auraroute.dev/api',
apiKey: process.env.AURAROUTE_API_KEY,
});
const stream = await client.chat.completions.create({
model: 'chat',
stream: true,
stream_options: { include_usage: true },
messages: [{ role: 'user', content: 'Explain recursion briefly.' }],
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
// Thinking arrives first, in a field the OpenAI types do not declare.
const thinking = delta?.reasoning_content ?? delta?.reasoning;
if (thinking) process.stderr.write(thinking);
if (delta?.content) process.stdout.write(delta.content);
}
import os
from openai import OpenAI
client = OpenAI(
base_url="https://auraroute.dev/api",
api_key=os.environ["AURAROUTE_API_KEY"],
)
stream = client.chat.completions.create(
model="chat",
stream=True,
stream_options={"include_usage": True},
messages=[{"role": "user", "content": "Explain recursion briefly."}],
)
for chunk in stream:
if not chunk.choices:
continue # the final usage chunk has no choices
delta = chunk.choices[0].delta
# Thinking arrives first, in a field the OpenAI types do not declare.
thinking = getattr(delta, "reasoning_content", None) or getattr(delta, "reasoning", None)
if thinking:
print(thinking, end="", flush=True)
if delta.content:
print(delta.content, end="", flush=True)If a provider fails before the first token, the request moves to another provider and you only see a slightly later first token. After the first token the stream cannot switch; a failure then ends the stream with an error event, shaped like the errors below.
Tools & images
Pass tools as you would to OpenAI. Requests with tools are served by models that support them, and the model replies with tool_calls for you to run and answer. Requests containing images go only to vision-capable models. If nothing capable is available you get a 503 rather than a silently degraded answer.
curl https://auraroute.dev/api/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AURAROUTE_API_KEY" \
-d '{
"model": "agent",
"messages": [{ "role": "user", "content": "What is the weather in Dhaka?" }],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}]
}'
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://auraroute.dev/api',
apiKey: process.env.AURAROUTE_API_KEY,
});
const res = await client.chat.completions.create({
model: 'agent',
messages: [{ role: 'user', content: 'What is the weather in Dhaka?' }],
tools: [{
type: 'function',
function: {
name: 'get_weather',
description: 'Current weather for a city',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
},
}],
});
console.log(res.choices[0].message.tool_calls);
import os
from openai import OpenAI
client = OpenAI(
base_url="https://auraroute.dev/api",
api_key=os.environ["AURAROUTE_API_KEY"],
)
res = client.chat.completions.create(
model="agent",
messages=[{"role": "user", "content": "What is the weather in Dhaka?"}],
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}],
)
print(res.choices[0].message.tool_calls)Response headers
Every response says how it was served. These are for observability — they are never inputs.
| Header | Meaning |
|---|---|
| X-Request-Id | Quote this when asking for help. |
| X-AuraRoute-Workload | The pool that answered. Differs from your workload after a fallback. |
| X-AuraRoute-Routed-Model | The model that actually answered. |
| X-AuraRoute-Attempts | More than 1 means a provider failed and the request was retried elsewhere. |
Limits
| Limit | Detail |
|---|---|
| Concurrency | Each key can have a few requests in flight at once. Beyond that you get 429 concurrency_limit_exceeded; wait for one to finish. |
| Daily tokens | Each account has a daily input cap and a daily output cap, shared by every key and the web chat. They reset at 00:00 UTC. Past either one you get 429 daily_quota_exceeded. An administrator can change both caps. Your usage is on the dashboard. |
| Output length | max_tokens is capped at a platform limit. |
| Request size | Bodies above the size limit are rejected with 413 request_too_large. |
Errors
Errors use the OpenAI shape: { "error": { "type", "code", "message", "param" } }. Match on code, not on the message.
| Status | code | What to do |
|---|---|---|
| 400 | model_not_found | model is not a workload. The message lists the valid ones. |
| 400 | invalid_value | A parameter is wrong; param says which. |
| 400 | invalid_json / invalid_body | The body is not valid JSON. |
| 401 | invalid_api_key | The key is missing, malformed or revoked. |
| 413 | request_too_large | Shorten the input. |
| 429 | concurrency_limit_exceeded | Too many requests at once on this key. Retry when one finishes. |
| 429 | daily_quota_exceeded | Quota used up. It resets at 00:00 UTC. |
| 503 | capacity_busy / upstream_unavailable / no_provider_available | Retry shortly with backoff; honour Retry-After. |
| 504 | timeout | The request took too long. Retry, or shorten it. |