Introduction
CanalAPI is a unified gateway over 600+ LLM, image, and audio models. Use any OpenAI-compatible client — point base_url at https://api.canalapi.com/v1, pass your key, and ship.
Quickstart
You need two things: an API key (from the console) and a model slug. Here's the simplest possible request:
# Any OpenAI-compatible client works — just swap the base URL
curl https://api.canalapi.com/v1/chat/completions \
-H "Authorization: Bearer $CANAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-6",
"messages": [{"role":"user","content":"Hello"}]
}'Response
{
"id": "chatcmpl_8K2m3",
"object": "chat.completion",
"created": 1745078400,
"model": "claude-opus-4-6",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "Hi there!" },
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 8, "completion_tokens": 3, "total_tokens": 11 }
}Authentication
Authenticated endpoints accept either Authorization: Bearer <key> (OpenAI-style) or x-api-key: <key> (Anthropic SDK style); Authorization wins when both are present. Keys are prefixed with ca_sk_live_ — create separate keys per environment (dev / staging / prod) from the console.
Chat completionsPOST/v1/chat/completions
Create a chat completion. Compatible with OpenAI's chat/completions — same parameters, same response shape, with automatic upstream-channel selection.
Request body
| Field | Type | Description |
|---|---|---|
| modelrequired | string | Model slug. Call /v1/models for the live list, or browse the pricing page. |
| messagesrequired | array | Conversation as an array of { role, content } objects. Roles: system, user, assistant. |
| streamoptional | boolean | If true, tokens are streamed via SSE. Default false. |
| temperatureoptional | number | 0–2. Higher = more random. Default is model-dependent. |
| max_tokensoptional | integer | Cap on completion tokens. Model-dependent default. |
| toolsoptional | array | Function-calling tool definitions. Request schema follows OpenAI / Anthropic native shapes. |
Streaming example
stream = client.chat.completions.create(
model="claude-opus-4-6",
messages=[{"role": "user", "content": "Write a haiku."}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")Endpoints at a glance
All endpoints share the same key and auth scheme. The table below lists the OpenAI-compatible and vendor-native endpoints currently live:
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/embeddings | Text embeddings (OpenAI Embeddings compatible) |
| POST | /v1/images/generations | Image generation (DALL·E compatible; /edits and /variations also live) |
| POST | /v1/audio/transcriptions | Speech-to-text (Whisper compatible; /audio/translations also live) |
| POST | /v1/audio/speech | Text-to-speech (TTS) |
| POST | /v1/moderations | Content moderation (typically free) |
| POST | /v1/responses | OpenAI Responses API compatible endpoint |
| POST | /v1/messages | Anthropic Messages API — native endpoint for the Claude SDK; the count_tokens sub-route is free |
| GET | /v1/models | List available models (public, no auth required) |
| GET | /v1/dashboard/billing/credit_grants | OpenAI-compatible account balance summary |
Request parameters and vendor-specific fields follow the upstream API spec; CanalAPI forwards all standard fields.
Native GeminiPOST/v1beta/models/*:generateContent
Alongside the OpenAI-compatible /v1, CanalAPI exposes the native Gemini surface at /v1beta. Just point your Gemini SDK (@google/generative-ai) base_url at https://api.canalapi.com/v1beta and authenticate with your CanalAPI key — generateContent and streamGenerateContent work with zero code changes.
Generate content
# 把 Gemini SDK 的 base_url 指向 CanalAPI 的 /v1beta,用 CanalAPI 密钥鉴权
curl "https://api.canalapi.com/v1beta/models/gemini-3.1-pro-preview:generateContent" \
-H "x-goog-api-key: $CANAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{ "parts": [{ "text": "Hello" }] }]
}'Streaming
# 流式:streamGenerateContent + ?alt=sse;鉴权头同样可用 Authorization: Bearer
curl "https://api.canalapi.com/v1beta/models/gemini-3.1-pro-preview:streamGenerateContent?alt=sse" \
-H "Authorization: Bearer $CANAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{ "parts": [{ "text": "Write a haiku." }] }]
}'Gemini SDK
import { GoogleGenerativeAI } from "@google/generative-ai";
// 把 SDK 的 baseUrl 指向 CanalAPI 的 /v1beta,apiKey 用 CanalAPI 密钥
const genAI = new GoogleGenerativeAI(process.env.CANAL_KEY, {
baseUrl: "https://api.canalapi.com/v1beta",
});
const model = genAI.getGenerativeModel({ model: "gemini-3.1-pro-preview" });
const result = await model.generateContent("Hello");
console.log(result.response.text());Image editsPOST/v1/images/edits
Edit an existing image from a prompt (with an optional mask). Requests use multipart/form-data and are compatible with OpenAI Images Edit; billed per generated image.
| Field | Type | Description |
|---|---|---|
| imagerequired | file | Source image file to edit (image/jpeg · image/png · image/webp, ≤ 25MB each). |
| promptrequired | string | Text instruction describing the desired edit. |
| maskoptional | file | Optional mask image; transparent areas mark the region to repaint. |
| modeloptional | string | Model slug, defaults to dall-e-2. You can also pass an image model such as gpt-image-1. |
| noptional | integer | Number of images to generate (1–10), default 1. |
| sizeoptional | string | Output image size, e.g. 1024x1024. |
# multipart/form-data:image + prompt 必填,mask 可选
curl https://api.canalapi.com/v1/images/edits \
-H "Authorization: Bearer $CANAL_KEY" \
-F image="@source.png" \
-F prompt="Add a red hat to the cat" \
-F model="gpt-image-1" \
-F n=1 \
-F size="1024x1024"Image variations/v1/images/variations
Create variations of a source image with no prompt. Also multipart/form-data — only the image field is required (model / n / size optional).
# multipart/form-data:仅需 image
curl https://api.canalapi.com/v1/images/variations \
-H "Authorization: Bearer $CANAL_KEY" \
-F image="@source.png" \
-F n=2 \
-F size="1024x1024"Errors & retries
CanalAPI uses conventional HTTP status codes; errors follow the OpenAI shape { error: { message, type, code } }. On 5xx upstream failures we auto-retry across healthy channels — you only see an error after the routing pool is exhausted.
| Code | Meaning | What to do |
|---|---|---|
| 400 | bad_request | Check request body — likely a bad model or malformed messages. |
| 401 | unauthenticated | Missing, invalid, or revoked key. Create a new one in the console. |
| 402 | insufficient_balance | Insufficient balance — top up and retry. |
| 429 | rate_limited | Rate limit exceeded. Back off using the Retry-After and X-RateLimit-* headers. |
| 502/503 | upstream_error | All upstreams unavailable and already retried. Open a support ticket if it persists. |
SDK integration
CanalAPI ships no proprietary SDK — it's a drop-in replacement for the major vendor SDKs. Swap the base URL and your existing code keeps working.
pip install openai • npm i openaipip install anthropic • npm i @anthropic-ai/sdk