Endpoints
Six endpoints, one consistent JSON envelope. Base URL (auto-detected on this page): https://YOUR-DOMAIN
GET /api/v1/health
Public liveness probe — no authentication, no rate-limit auth requirement. Use it for uptime monitors and health checks. A legacy alias exists at GET /health.
Response — 200
{
"status": "ok",
"timestamp": "2026-01-15T10:30:00.000Z",
"version": "1.0.0",
"appName": "AI Gateway API",
"defaultProvider": "openrouter",
"failover": true
}
curl https://YOUR-DOMAIN/api/v1/health
Live monitoring with latency history: API Status page →
GET /api/v1/providers
Lists every registered provider with its models and configuration status. Use it to populate provider pickers (the Playground does exactly this) or to debug missing env vars.
Auth: required only when API_SECRET_KEY is set on the server.
Response — 200
{
"success": true,
"defaultProvider": "openrouter",
"total": 3,
"providers": [
{
"id": "openrouter",
"label": "OpenRouter",
"description": "Unified gateway to 200+ models…",
"defaultModel": "z-ai/glm-5.2:free",
"models": ["z-ai/glm-5.2:free", "deepseek/deepseek-chat-v3-0324:free", "…"],
"requiredEnvKeys": ["OPENROUTER_API_KEY"],
"configured": true
},
{
"id": "opencodezen",
"label": "OpenCode Zen",
"defaultModel": "deepseek-v4-flash-free",
"requiredEnvKeys": ["OPENCODEZEN_API_KEY"],
"configured": true
},
{
"id": "poolside",
"label": "Poolside AI",
"defaultModel": "poolside/laguna-s-2.1",
"requiredEnvKeys": ["POOLSIDE_API_KEY"],
"configured": true
},
{
"id": "custom",
"label": "Custom (OpenAI-compatible)",
"defaultModel": null,
"requiredEnvKeys": ["CUSTOM_BASE_URL"],
"configured": false
}
]
}
configured is false when the provider's env vars are missing — chat requests to it will fail with PROVIDER_NOT_CONFIGURED. With auto-failover enabled, those requests transparently retry the next configured provider instead.
GET /api/v1/models
Live model catalog, auto-fetched from every provider. The gateway calls each provider's own GET /models endpoint in parallel, merges the results and applies the per-provider free-only policy:
| Provider | What gets listed |
|---|---|
openrouter | Only :free models (e.g. z-ai/glm-5.2:free) |
opencodezen | Only -free models (e.g. deepseek-v4-flash-free) |
poolside, custom, … | Full catalog — every model the provider exposes |
OpenCode Zen's catalog is public, so it is listed even before its API key is set; every other provider needs its key configured. Results are cached in memory for MODELS_CACHE_TTL_MS (default 6 h).
Query parameters
| Param | Description |
|---|---|
refresh=1 | Bypass the cache and re-fetch all catalogs now (Models page button + admin panel). |
provider=<id> | Limit the response to a single provider id. |
Example
curl https://YOUR-DOMAIN/api/v1/models?provider=openrouter
Response — 200
{
"success": true,
"object": "list",
"total": 28,
"freeOnlyPolicy": [
{ "provider": "openrouter", "freeOnly": true },
{ "provider": "opencodezen", "freeOnly": true },
{ "provider": "poolside", "freeOnly": false }
],
"providers": [
{ "id": "openrouter", "label": "OpenRouter", "status": "ok", "freeOnly": true,
"total": 19, "totalBeforeFilter": 214,
"models": [ { "id": "z-ai/glm-5.2:free", "object": "model", "owned_by": "z-ai", "provider": "openrouter", "free": true } ] },
{ "id": "opencodezen", "label": "OpenCode Zen", "status": "ok", "freeOnly": true, "total": 7,
"models": [ { "id": "deepseek-v4-flash-free", "object": "model", "owned_by": "opencode", "provider": "opencodezen", "free": true } ] },
{ "id": "poolside", "label": "Poolside AI", "status": "ok", "freeOnly": false, "total": 2,
"models": [ { "id": "poolside/laguna-s-2.1", "object": "model", "owned_by": "poolside", "provider": "poolside", "free": false } ] }
],
"data": [ { "id": "z-ai/glm-5.2:free", "object": "model", "owned_by": "z-ai", "provider": "openrouter", "free": true } ],
"cache": { "hit": false, "updatedAt": "2026-09-05T04:00:00.000Z", "ttlMs": 21600000, "nextRefreshAt": "2026-09-05T10:00:00.000Z" }
}
One provider failing never fails the endpoint — its error is reported inside providers[].error while the others still return data. Browse the catalog visually on the Models page.
GET /api/v1/config
Public runtime configuration: app name/version from your env vars, the auto-detected base URL, failover settings and which providers are currently configured. The docs site reads this so a single .env change rebrands every page.
Response — 200
{
"success": true,
"appName": "AI Gateway API",
"version": "1.0.0",
"apiBaseUrl": "https://aigatewaybd.pages.dev",
"defaultProvider": "openrouter",
"failover": { "enabled": true, "maxRounds": 3, "retryDelayMs": 800 },
"providers": [ { "id": "openrouter", "configured": true, "defaultModel": "z-ai/glm-5.2:free" } ]
}
GET /api/v1/chat?prompt=… GET /ask?prompt=…
The zero-setup entry point: open the URL in any browser and the JSON answer appears directly — no request headers, no body, no API key (provider keys stay server-side). The short custom route /ask is an alias that serves the exact same handler, so both URLs work identically. Perfect for integrations anywhere a plain URL works: other apps, n8n/Make, spreadsheets, QR codes, <img> sources…
Click and see it live: /ask?prompt=Explain failover in one sentence
Query parameters
| Param | Description |
|---|---|
prompt | Required. The question (aliases: q, text, message, p). URL-encode it: spaces as %20 or +. |
provider | Optional. Pin one provider; without it the auto-failover chain picks. |
model | Optional. Model id override (see GET /api/v1/models). |
system_prompt | Optional. Instruction for the AI (alias: system). |
temperature / max_tokens | Optional. Same meaning as the POST body fields. |
failover | Optional. false = only the named provider, no fallback. |
raw=1 | Return the plain-text reply only (Content-Type: text/plain) — handy for scripts and spreadsheets. |
pretty=0 | Compact JSON (default output is pretty-printed for browsers). |
Click-to-try examples (base URL auto-detected)
<a href="<span class=\"live-base\">https://YOUR-DOMAIN</span>/ask?prompt=Say+hello+in+Bangla">Ask my gateway</a>
# same thing, plain text only:
<span class="live-base">https://YOUR-DOMAIN</span>/ask?prompt=Say+hello+in+Bangla&raw=1
# pin a provider + cap the answer length:
<span class="live-base">https://YOUR-DOMAIN</span>/api/v1/chat?prompt=Go+channels+in+one+line&provider=poolside&max_tokens=80
Rate limit: the same chat limiter applies (default 30 requests/min per IP). Errors come back as the standard JSON envelope — a missing prompt returns MISSING_PROMPT with usage examples built in.
POST /api/v1/chat
Chat completion via any configured provider — with automatic failover: if the chosen provider errors, the gateway retries the next one until a response arrives (disable per request with "failover": false). Accepts a simple message or a full multi-turn conversation. This is the endpoint your apps will call 95% of the time.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
message | string | one of message / messages | Simple single-turn input (≤ 8000 chars). |
messages | array | one of message / messages | Multi-turn turns: {role, content} — roles: system, user, assistant (≤ 50 items). |
provider | string | optional | openrouter | opencodezen | poolside | custom. Default: DEFAULT_PROVIDER; omit to let auto-failover pick the whole chain. |
model | string | optional | Model override for this request only. |
failover | boolean | optional | false = only try the named provider, no automatic fallback. |
system_prompt | string | optional | System instruction (simple style only). |
temperature | number | optional | 0–2, default 0.7. |
max_tokens | integer | optional | 1–8192; omitted upstream when unset. |
Example — simple
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
-H "Content-Type: application/json" \
-d '{
"provider": "openrouter",
"message": "What is the meaning of life?"
}'
Example — multi-turn with options
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
-H "Content-Type: application/json" \
-d '{
"provider": "poolside",
"messages": [
{ "role": "system", "content": "You are a senior Go mentor." },
{ "role": "user", "content": "What are channels in Go?" }
],
"temperature": 0.4,
"max_tokens": 500
}'
Response — 200
{
"success": true,
"provider": "poolside",
"reply": "Channels are typed conduits that let goroutines communicate safely…",
"model": "poolside/laguna-s-2.1",
"usage": { "prompt_tokens": 21, "completion_tokens": 36, "total_tokens": 57 },
"failover": { "enabled": true, "attempts": [], "rounds": 1, "servedOnRound": 1 }
}
Error responses
| HTTP | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR / INVALID_JSON | Malformed body or fields. |
| 400 | UNKNOWN_PROVIDER | provider is not registered. |
| 429 | RATE_LIMITED / UPSTREAM_RATE_LIMIT | Your limit or the provider's limit. |
| 500 | PROVIDER_NOT_CONFIGURED / UPSTREAM_AUTH_ERROR / NO_PROVIDER_CONFIGURED | Server-side env/key problem. |
| 502 | ALL_PROVIDERS_FAILED | Auto-failover exhausted every configured provider — see error.details.attempts. |
| 502 / 503 / 504 | UPSTREAM_ERROR / UPSTREAM_UNAVAILABLE / UPSTREAM_TIMEOUT | Provider-side failure — auto-failover usually retries it on another provider. |
Full error table: API Docs → Error codes. Want to click instead of curl? Open the Playground →