Base URL
http://localhost:3000/v1(the gateway; the web app runs on :3001 and does not proxy /v1)Authentication
Every request carries your API key as a bearer token. Keys start with air_live_ and are shown in full only once, at creation — store it in an environment variable, never in client-side code or a public repository.
Authorization: Bearer air_live_xxx
🔒 A key is a bearer credential: anyone holding it can spend your balance. If it leaks, freeze it in the dashboard immediately and contact support.
POST /v1/chat/completions
The main endpoint, matching the OpenAI Chat Completions schema. Streaming is supported with "stream": true.
cURL
curl http://localhost:3000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer air_live_xxx" \
-d '{
"model": "gpt-6-astra",
"messages": [{"role": "user", "content": "Hello!"}]
}'Python (openai SDK)
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:3000/v1",
api_key="air_live_xxx",
)
resp = client.chat.completions.create(
model="gpt-6-astra",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)JavaScript (openai SDK)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "http://localhost:3000/v1",
apiKey: "air_live_xxx",
});
const resp = await client.chat.completions.create({
model: "gpt-6-astra",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(resp.choices[0].message.content);Where the key goes
Install the SDK with pip install openai or npm i openai. Point base_url at http://localhost:3000/v1 and use your air_live_ key. No other code changes are needed — the request and response shapes are OpenAI's.
Models
GET /models is public — no API key required — and returns the live catalog with per-model pricing and capabilities. Always resolve model ids from this endpoint rather than hard-coding them, since the catalog changes as providers release and retire models.
curl http://localhost:3000/models
| Model ID | Provider |
|---|---|
| gpt-6-astra | OpenAI |
| claude-opus-5-5 | Anthropic |
| deepseek-v4-pro | DeepSeek |
| kimi-k2.8 | Moonshot |
| glm-5.3 | Zhipu |
| gemini-4-argon |
Representative sample — the endpoint returns the full catalog, including input/output/cached token prices for each model.
Error Codes
Errors return a JSON body with a stable code, a human-readable message, and a request_id. Branch on code, not on message — the message text may change.
{
"error": {
"code": "insufficient_balance",
"message": "…",
"request_id": "…"
}
}| Status | code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, malformed, frozen or unknown API key. Check the Authorization header is exactly `Bearer air_live_…`. |
| 402 | — | Reserved for payment-required signalling. In practice our balance errors surface as 403 insufficient_balance, so handle both. |
| 403 | insufficient_balance / model_not_allowed / forbidden | Balance exhausted, the key is not permitted to use that model, or the operation is not allowed for your role. |
| 404 | not_found | The path or model does not exist. Verify the model id against GET /models — a typo lands here, not on 400. |
| 429 | rate_limit_exceeded | Too many requests. Back off and retry — respect the Retry-After header when it is present. |
| 500 | internal_error / database_error / configuration_error | Something broke on our side. Retry once; if it persists, contact support with the request_id from the response. |
| 502 / 503 / 504 | upstream_* | The model provider failed, rejected our credentials, rate-limited us, timed out, or is unavailable. Retry with backoff. |
Note: balance and model-permission failures are both 403. Distinguish them via the code field rather than the status alone.
Stuck on something?
For billing and top-ups, message us on Telegram. For technical questions, email works too.