Endpoint reference

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

application/json
{
  "status": "ok",
  "timestamp": "2026-01-15T10:30:00.000Z",
  "version": "1.0.0",
  "appName": "AI Gateway API",
  "defaultProvider": "openrouter",
  "failover": true
}
bash — try it
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

application/json
{
  "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:

ProviderWhat gets listed
openrouterOnly :free models (e.g. z-ai/glm-5.2:free)
opencodezenOnly -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

ParamDescription
refresh=1Bypass the cache and re-fetch all catalogs now (Models page button + admin panel).
provider=<id>Limit the response to a single provider id.

Example

bash — try it
curl https://YOUR-DOMAIN/api/v1/models?provider=openrouter

Response — 200

application/json
{
  "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

application/json
{
  "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

ParamDescription
promptRequired. The question (aliases: q, text, message, p). URL-encode it: spaces as %20 or +.
providerOptional. Pin one provider; without it the auto-failover chain picks.
modelOptional. Model id override (see GET /api/v1/models).
system_promptOptional. Instruction for the AI (alias: system).
temperature / max_tokensOptional. Same meaning as the POST body fields.
failoverOptional. false = only the named provider, no fallback.
raw=1Return the plain-text reply only (Content-Type: text/plain) — handy for scripts and spreadsheets.
pretty=0Compact JSON (default output is pretty-printed for browsers).

Click-to-try examples (base URL auto-detected)

GET — JSON
<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

FieldTypeRequiredDescription
messagestringone of message / messagesSimple single-turn input (≤ 8000 chars).
messagesarrayone of message / messagesMulti-turn turns: {role, content} — roles: system, user, assistant (≤ 50 items).
providerstringoptionalopenrouter | opencodezen | poolside | custom. Default: DEFAULT_PROVIDER; omit to let auto-failover pick the whole chain.
modelstringoptionalModel override for this request only.
failoverbooleanoptionalfalse = only try the named provider, no automatic fallback.
system_promptstringoptionalSystem instruction (simple style only).
temperaturenumberoptional0–2, default 0.7.
max_tokensintegeroptional1–8192; omitted upstream when unset.

Example — simple

bash
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

bash
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

application/json
{
  "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

HTTPCodeWhen
400VALIDATION_ERROR / INVALID_JSONMalformed body or fields.
400UNKNOWN_PROVIDERprovider is not registered.
429RATE_LIMITED / UPSTREAM_RATE_LIMITYour limit or the provider's limit.
500PROVIDER_NOT_CONFIGURED / UPSTREAM_AUTH_ERROR / NO_PROVIDER_CONFIGUREDServer-side env/key problem.
502ALL_PROVIDERS_FAILEDAuto-failover exhausted every configured provider — see error.details.attempts.
502 / 503 / 504UPSTREAM_ERROR / UPSTREAM_UNAVAILABLE / UPSTREAM_TIMEOUTProvider-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 →