API Documentation
Everything you need to integrate: endpoints, request/response formats, error codes, rate limits and configuration.
Introduction
AI Gateway API is a multi-provider REST gateway for LLM chat completions. Instead of hard-coding one vendor, you send every request to your own gateway and choose the AI provider per request. The gateway validates input, applies security and rate limits, attaches the correct server-side API key, and returns a normalized response.
It runs on two runtimes with the exact same code:
- Cloudflare Pages Functions (recommended) — serverless, global edge, free tier.
- Node.js 18+ —
npm startfor local development or self-hosting.
এই গেটওয়ে হলো আপনার অ্যাপ ও AI provider-দের মাঝখানে থাকা একটি সুরক্ষিত মধ্যস্থতাকারী। আপনার অ্যাপ শুধু গেটওয়েকে কল করে; provider-এর API key কখনো ব্রাউজারে পাঠানো হয় না — সব key সার্ভারের environment variable-এ থাকে। একই কোড Cloudflare Pages ও Node.js-এ চলে।
Quick start
- Deploy the gateway (or run
npm startlocally) — see the deployment guide. - Set at least one provider key, e.g.
OPENROUTER_API_KEYorPOOLSIDE_API_KEY. - Send your first chat request:
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
-H "Content-Type: application/json" \
-d '{ "message": "Hello!" }'
No-code option: paste this URL in any browser and the JSON answer appears directly — /ask?prompt=Hello gateway (full details in GET /api/v1/chat).
The base URL above is auto-detected — this same page shows your live domain when deployed
(https://your-project.pages.dev) and http://localhost
when running locally. No provider field? The gateway uses
DEFAULT_PROVIDER (default: openrouter) and — if that
provider errors — automatically fails over to the next configured one (see Auto-failover).
Authentication
The gateway has an optional shared key for protecting your API from third parties. It is independent of the provider keys.
| Server setting | Client behavior |
|---|---|
API_SECRET_KEY unset | Open access — anyone who knows the URL can call the API. |
API_SECRET_KEY set | Every /api/v1/* request must send the key in the X-API-Key header (or Authorization: Bearer <key>). |
GET /api/v1/health, the legacy GET /health and GET /api/v1/config always stay public so uptime monitors and the docs site work.
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_GATEWAY_KEY" \
-d '{ "message": "Hello!" }'
Wrong or missing keys get 401 UNAUTHORIZED. Key comparison is constant-time to prevent timing attacks.
Providers
A provider is any upstream AI service. Discovery endpoint:
GET /api/v1/providers lists every registered provider, its default model and
whether its key is configured. Built-ins:
| id | Provider | Required env vars | Default model |
|---|---|---|---|
openrouter | OpenRouter (200+ models, free tiers) | OPENROUTER_API_KEY | z-ai/glm-5.2:free |
opencodezen | OpenCode Zen (opencode.ai coding models) | OPENCODEZEN_API_KEY | deepseek-v4-flash-free |
poolside | Poolside AI (Laguna code models) | POOLSIDE_API_KEY | poolside/laguna-s-2.1 |
custom | Any OpenAI-compatible endpoint | CUSTOM_BASE_URL (+ CUSTOM_API_KEY, CUSTOM_MODEL) | from CUSTOM_MODEL |
Model overrides per provider: OPENROUTER_MODEL, OPENCODEZEN_MODEL, POOLSIDE_MODEL — or pass model in the request body. Adding more providers is documented on the Custom Provider page.
GET /api/v1/models — live catalog
GET /api/v1/models auto-fetches the model list from every provider —
nothing is hardcoded. The gateway calls each provider's own /models endpoint in
parallel and applies the per-provider free-only policy:
- OpenRouter → only
:freemodels (e.g.z-ai/glm-5.2:free). - OpenCode Zen → only
-freemodels (e.g.deepseek-v4-flash-free). - Every other provider (Poolside, custom, …) → the full catalog.
Results are cached for MODELS_CACHE_TTL_MS (default 6 h);
?refresh=1 re-fetches immediately and ?provider=<id>
filters to one provider. A failing provider never breaks the endpoint — its error is reported inside
providers[].error while the rest still return data. Browse it visually on the
Models page.
curl https://YOUR-DOMAIN/api/v1/models
এই endpoint প্রতিটি provider থেকে নিজে নিজে (auto) মডেল লিস্ট এনে দেয়। OpenRouter ও OpenCode Zen শুধু তাদের free মডেলগুলো দেখায়, বাকি সব provider-এর সম্পূর্ণ লিস্ট দেখানো হয়। ফলে নতুন মডেল যোগ হলে এখানে নিজে থেকেই চলে আসে — কোড বদলাতে হয় না।
Automatic provider failover
On by default. If the chosen provider fails for any reason — invalid key, exhausted quota, rate limit, timeout, outage, malformed reply — the gateway transparently retries the next configured provider and keeps cycling the chain (rotating the starting provider each round) until a response is produced or every option is exhausted. Your client code does nothing; it just always gets an answer when at least one provider is healthy.
- The preferred provider is tried first (the request
providerfield, elseDEFAULT_PROVIDER). - On any error, the remaining configured providers are tried in registry order.
- Each new round rotates the starting provider; up to
FAILOVER_MAX_ROUNDSrounds (default 3). - Permanent errors (bad key, provider not configured) are not retried; transient ones (rate limit, timeout, outage) are.
{
"success": true,
"provider": "poolside",
"reply": "Channels are typed conduits…",
"model": "poolside/laguna-s-2.1",
"usage": { "prompt_tokens": 14, "completion_tokens": 43, "total_tokens": 57 },
"failover": {
"enabled": true,
"attempts": [
{ "provider": "openrouter", "status": "failed", "code": "UPSTREAM_RATE_LIMIT", "http": 429, "round": 1 }
],
"rounds": 1,
"servedOnRound": 1
}
}
The winning provider is also reported in the X-AI-Gateway-Provider response
header. Disable failover per request with "failover": false (useful for
per-provider health probes), or globally with FAILOVER_ENABLED=false. When
every provider fails you get 502 ALL_PROVIDERS_FAILED with the full attempt log.
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
-H "Content-Type: application/json" \
-d '{
"provider": "poolside",
"failover": false,
"message": "What are channels in Go?"
}'
অটো-ফেইলওভার ডিফল্টভাবে চালু: এক provider-এ এরর হলে (rate limit, timeout, key সমস্যা, সার্ভার ডাউন) গেটওয়ে স্বয়ংক্রিয়ভাবে পরের configured provider দিয়ে চেষ্টা করে, এবং রেসপন্স না পাওয়া পর্যন্ত chain ঘুরিয়ে ঘুরিয়ে চেষ্টা চালিয়ে যায় (ডিফল্ট সর্বোচ্চ ৩ রাউন্ড)। সফল হলে রেসপন্সে provider ফিল্ডে কে উত্তর দিল তা এবং failover.attempts-এ কারা ফেইল করল তা দেখাবে। নির্দিষ্ট provider-এই আটকে রাখতে চাইলে রিকোয়েস্টে "failover": false দিন।
GET /api/v1/chat — browser-clickable
A zero-setup entry point that shares the exact same failover core as the POST endpoint: open the URL in a browser (or integrate it anywhere a plain URL works) and the JSON answer appears directly — no headers, no body, no API key. The short alias /ask serves the identical handler: /ask?prompt=Say hello in Bangla
| Param | Description |
|---|---|
prompt | Required. The question (aliases: q, text, message, p). |
provider / model | Optional — pin one provider or a specific model. |
system_prompt / temperature / max_tokens / failover | Optional — same meaning as the POST body fields. |
raw=1 | Plain-text reply only (great for scripts/spreadsheets). |
pretty=0 | Compact JSON. |
<span class="live-base">https://YOUR-DOMAIN</span>/api/v1/chat?prompt=What+is+an+API+gateway%3F
<span class="live-base">https://YOUR-DOMAIN</span>/ask?prompt=Write+a+haiku+about+clouds&raw=1
এখন ব্রাউজারে সরাসরি লিংক খুললেই JSON উত্তর পাওয়া যাবে — কোনো হেডার, বডি বা API key লাগে না (key সার্ভারেই থাকে)। /ask?prompt=আপনার+প্রশ্ন হলো ছোট শর্টকাট রাউট; &raw=1 দিলে শুধু প্লেইন টেক্সট উত্তর মেলে। এটি যেকোনো অ্যাপ, স্প্রেডশিট বা QR কোডে ইন্টিগ্রেট করা যায়।
POST /api/v1/chat
The main endpoint. Accepts two input styles — exactly one is required.
Style 1 — simple message
| Field | Type | Rules | Description |
|---|---|---|---|
message | string | required, non-empty, ≤ 8000 chars | The user message. |
system_prompt | string | optional | System instruction for this request. |
Style 2 — multi-turn conversation
| Field | Type | Rules | Description |
|---|---|---|---|
messages | array | required, 1–50 items | OpenAI-style turns: {role: "system"|"user"|"assistant", content: string}. |
Shared optional fields
| Field | Type | Rules | Description |
|---|---|---|---|
provider | string | optional | Provider id. Default: DEFAULT_PROVIDER env or openrouter. Unknown ids get 400 UNKNOWN_PROVIDER. |
model | string | optional | Model override for this request. |
failover | boolean | optional, default: server config | false pins the request to one provider (no automatic fallback). See Auto-failover. |
temperature | number | 0 – 2, default 0.7 | Sampling randomness. |
max_tokens | integer | 1 – 8192 | Upper bound on the reply length. Omitted upstream when unset. |
Examples
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
-H "Content-Type: application/json" \
-d '{
"provider": "poolside",
"model": "poolside/laguna-s-2.1",
"system_prompt": "You are a concise senior Go developer.",
"message": "What are channels in Go?",
"temperature": 0.5,
"max_tokens": 400
}'
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
-H "Content-Type: application/json" \
-d '{
"provider": "openrouter",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Hi!" },
{ "role": "assistant", "content": "Hello! How can I help?" },
{ "role": "user", "content": "Explain HTTP status codes briefly." }
]
}'
Validation failures return 400 VALIDATION_ERROR with a details.field pointing at the problem.
Responses
Success — 200
{
"success": true,
"provider": "poolside",
"reply": "Channels are typed conduits that let goroutines exchange values safely…",
"model": "poolside/laguna-s-2.1",
"usage": { "prompt_tokens": 14, "completion_tokens": 43, "total_tokens": 57 },
"failover": { "enabled": true, "attempts": [], "rounds": 1, "servedOnRound": 1 }
}
reply— the assistant text, extracted from the provider response.model— the model that actually answered (echoes upstream when available).usage— token counts normalized to the OpenAI shape;nullwhen a provider omits them.
Error — any failure
{
"success": false,
"error": {
"code": "UPSTREAM_RATE_LIMIT",
"message": "OpenRouter rate limit reached (free tiers throttle temporarily). Retry shortly.",
"details": { "retryAfterSeconds": 5 }
}
}
Error codes
Every failure uses the same envelope. These are all the codes the gateway can emit:
| HTTP | Code | Meaning | What to do |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Body failed validation (missing message, bad temperature…) | Fix the request; see error.details. |
| 400 | INVALID_JSON | Body is not valid JSON | Fix JSON syntax / Content-Type. |
| 400 | UNKNOWN_PROVIDER | provider not in the registry | Check GET /api/v1/providers. |
| 401 | UNAUTHORIZED | Gateway key missing/incorrect (when API_SECRET_KEY set) | Send X-API-Key. |
| 402 | UPSTREAM_CREDITS_EXHAUSTED | Provider account out of credits | Top up the provider account. |
| 429 | RATE_LIMITED | Your gateway rate limit hit | Back off; see details.retryAfterSeconds. |
| 429 | UPSTREAM_RATE_LIMIT | Provider throttled (e.g. shared free pool busy) | Retry after details.retryAfterSeconds. |
| 500 | UPSTREAM_AUTH_ERROR | Provider rejected the server-side key | Fix the provider key env var, redeploy. |
| 500 | PROVIDER_NOT_CONFIGURED | Provider chosen but its env vars are missing | Set the provider's env vars (see Configuration). |
| 500 | NO_PROVIDER_CONFIGURED | No provider env var is set at all | Set at least one provider key (see Configuration). |
| 502 | ALL_PROVIDERS_FAILED | Auto-failover exhausted every configured provider | Inspect error.details.attempts for the per-provider errors. |
| 500 | INTERNAL_ERROR | Unexpected gateway bug | Check logs in the Cloudflare dashboard. |
| 502 | UPSTREAM_ERROR | Provider returned an unexpected response | Usually transient; check error.message. |
| 502 | NETWORK_ERROR | Could not reach the provider (DNS/connect) | Verify the provider base URL. |
| 502 | MALFORMED_UPSTREAM_RESPONSE | Reply missing from provider payload | Retry; if persistent, verify the model id. |
| 503 | UPSTREAM_UNAVAILABLE | Provider temporarily down (5xx) | Retry later or switch provider. |
| 504 | UPSTREAM_TIMEOUT | Provider took longer than PROVIDER_TIMEOUT_MS | Raise the timeout or retry. |
| 404 | ROUTE_NOT_FOUND | Unknown API path | Check the endpoint list. |
Rate limits
| Scope | Default | Env override |
|---|---|---|
All /api/v1/* per IP | 100 requests / 15 min | RATE_LIMIT_MAX, RATE_LIMIT_WINDOW_MS |
POST /chat per IP | 15 requests / min | CHAT_RATE_LIMIT_MAX, CHAT_RATE_LIMIT_WINDOW_MS |
Responses include X-RateLimit-Limit and X-RateLimit-Remaining; 429s include Retry-After.
Runtime config & base URL auto-detection
GET /api/v1/config (public, no auth) returns the live server configuration.
The docs site calls it from every page, which is how the version badge, base URL and failover status stay in
sync with your environment:
{
"success": true,
"appName": "AI Gateway API",
"version": "1.0.0",
"apiBaseUrl": "https://your-project.pages.dev",
"defaultProvider": "openrouter",
"failover": { "enabled": true, "maxRounds": 3, "retryDelayMs": 800 },
"providers": [
{ "id": "openrouter", "label": "OpenRouter", "configured": true, "defaultModel": "z-ai/glm-5.2:free" },
{ "id": "poolside", "label": "Poolside AI", "configured": true, "defaultModel": "poolside/laguna-s-2.1" },
{ "id": "custom", "label": "Custom (OpenAI-compatible)", "configured": false, "defaultModel": null }
]
}
Base URL auto-detection: apiBaseUrl is derived from the
incoming request itself, so the same deployment reports the correct URL on
localhost, on *.pages.dev and on any custom
domain. In the browser, pages use their own origin by default (override at build time with
API_BASE_URL if you host the API on a separate domain).
Admin panel (GitHub-backed, full CRUD)
/admin/ is an owner-only panel that manages this site's editable configuration through your GitHub repository. Sign in with a repository name + branch + fine-grained GitHub token (permission needed: Contents: Read and write on that single repository) and the panel:
- Loads everything from the repo —
config/site.json(branding, announcement banner, chat-bot settings) andconfig/providers.json(display order, taglines, visibility). - Commits every change straight back to the repo via the GitHub API — no local files, no manual git.
- Cloudflare Pages then rebuilds and redeploys automatically, so admin changes go live in 1–2 minutes.
- Shows recent commits touching
config/so you can audit every change.
Security: the token stays in your browser tab (session storage) and is sent only to
api.github.com. Provider API keys are never editable in the panel —
those remain Cloudflare environment secrets.
/admin পেজে GitHub API key ও repo-এর নাম দিয়ে লগইন করুন। প্যানেলটি সরাসরি আপনার GitHub repo থেকে সব কনফিগ লোড করে, আর আপনি যা পরিবর্তন করবেন তা GitHub API-এর মাধ্যমে সাথে সাথে repo-তে commit হয়ে যাবে — তারপর Cloudflare Pages নিজে থেকেই নতুন করে build করে সাইট আপডেট করে দেবে। Token শুধু আপনার ব্রাউজার ট্যাবে থাকে, অন্য কোথাও পাঠানো হয় না।
Configuration
All settings are environment variables — on Cloudflare you set them in the dashboard; locally put them in .env (Node) or .dev.vars (wrangler). Change a value once and every endpoint, page and badge reflects it after redeploy.
| Variable | Default | Purpose |
|---|---|---|
OPENROUTER_API_KEY | — | OpenRouter key (enables openrouter). |
OPENROUTER_MODEL | z-ai/glm-5.2:free | Default OpenRouter model. |
OPENCODEZEN_API_KEY | — | OpenCode Zen key (enables opencodezen chat; its model catalog is public). |
OPENCODEZEN_MODEL | deepseek-v4-flash-free | Default OpenCode Zen model. |
POOLSIDE_API_KEY | — | Poolside AI key (enables poolside). |
POOLSIDE_MODEL | poolside/laguna-s-2.1 | Default Poolside model. |
CUSTOM_BASE_URL | — | Base URL of the custom OpenAI-compatible provider. |
CUSTOM_API_KEY | — | Custom provider key (optional for local runtimes). |
CUSTOM_MODEL | — | Default custom model. |
DEFAULT_PROVIDER | openrouter | Provider used when the body has none; also first in the failover chain. |
FAILOVER_ENABLED | true | Automatic provider failover on/off. |
FAILOVER_MAX_ROUNDS | 3 | How many times the chain is cycled before ALL_PROVIDERS_FAILED (1–20). |
FAILOVER_RETRY_DELAY_MS | 800 | Pause between failover rounds. |
APP_NAME | AI Gateway API | Display name shown by /config, health and the docs site. |
APP_VERSION | 1.0.0 | Version badge shown everywhere (navbar, footer, docs, health). |
API_BASE_URL | auto-detect | Build-time override for the docs site when the API lives on a different domain. |
API_SECRET_KEY | — | Gateway shared key (optional). |
ALLOWED_ORIGINS | * | Comma-separated CORS allow-list. |
PROVIDER_TIMEOUT_MS | 60000 | Upstream call timeout. |
MODELS_CACHE_TTL_MS | 21600000 (6 h) | How long the live model catalog is cached before re-fetching from providers. |
MAX_MESSAGE_LENGTH | 8000 | Max chars per message. |
MAX_MESSAGES | 50 | Max items in messages[]. |
MAX_TOKENS_LIMIT | 8192 | Upper cap for max_tokens. |
APP_URL | — | Sent to OpenRouter as HTTP-Referer attribution. |
CORS
Browser calls are allowed from any origin by default (Access-Control-Allow-Origin: *),
with preflight OPTIONS handled automatically. To restrict to your own sites, set
ALLOWED_ORIGINS to a comma-separated list:
ALLOWED_ORIGINS=https://mysite.com,https://app.mysite.com
Deployment
The project is built for Cloudflare Pages: build command npm run build,
build output out, root directory empty. The API itself runs as Pages Functions
from the functions/ directory — no extra config needed.
Step-by-step instructions (GitHub push → Pages connect → env vars) live on the Integration Guide → Deployment section.
ডিপ্লয়মেন্ট খুবই সহজ: পুরো ফোল্ডারটি GitHub-এ push করুন → Cloudflare ড্যাশবোর্ডে Pages → Create project → আপনার repo সিলেক্ট করুন। Build command: npm run build, Build output: out, Root directory: খালি রাখুন। এরপর Settings → Environment variables-এ provider key-গুলো যোগ করুন (Production ও Preview দুটোতেই)। বিস্তারিত: Guide পেজ।