BetopiaAI API / v1

API documentation

Use the BetopiaAI API for chat, embeddings, image generation, and image editing with models available to your workspace. Each operation has a request contract suited to its output.

Base URL

https://api.betopia.ai/v1

Manage API keys
01

Quickstart

Create an API key, add it as a Bearer token, and send a request to the chat completions endpoint.

first request
curl https://api.betopia.ai/v1/chat/completions \  -H "Authorization: Bearer sk_your_key" \  -H "Content-Type: application/json" \  -d '{    "model": "auto",    "messages": [{"role": "user", "content": "Hello"}]  }'
02

Authentication

Inference endpoints use an API key. Account, key-management, analytics, and billing endpoints use the JWT returned by login. Keep both credentials on your server.

inference request header
Authorization: Bearer sk_your_key
POST/v1/auth/registername, email, password; email verification follows
POST/v1/auth/verify-email-otpreturns a JWT and the initial API key once
POST/v1/auth/loginreturns a JWT for user endpoints
login
POST https://api.betopia.ai/v1/auth/loginContent-Type: application/json{"email":"you@example.com","password":"your_password"}→ {"token":"eyJ..."}
03

Chat completions

Send an ordered list of messages. Use auto for platform routing or a model ID from the model catalog.

POST/v1/chat/completionsOpenAI-compatible chat request
request
curl https://api.betopia.ai/v1/chat/completions \  -H "Authorization: Bearer sk_your_key" \  -H "Content-Type: application/json" \  -d '{    "model": "auto",    "messages": [      {"role": "system", "content": "Be concise."},      {"role": "user", "content": "Summarize this release."}    ],    "temperature": 0.2,    "max_tokens": 300  }'
modelstringRequired. A model ID or auto.
messagesarrayRequired. Role and content message list.
temperaturenumberOptional sampling control.
max_tokensintegerOptional output token limit.
streambooleanOptional. Return Server-Sent Events.
04

Responses

Use Responses for structured output, tools, reasoning configuration, and continuation state. The response contains an output array and token usage.

POST/v1/responsesstateful response interface
request
curl https://api.betopia.ai/v1/responses \  -H "Authorization: Bearer sk_your_key" \  -H "Content-Type: application/json" \  -d '{    "model": "auto",    "input": "Draft a product update.",    "max_output_tokens": 150  }'
modelstringRequired. A model ID or auto.
inputstring | arrayRequired user input or structured items.
instructionsstringOptional high-level instruction.
previous_response_idstringContinue a previous response.
max_output_tokensintegerOptional output token limit.
toolsarrayOptional flat function tool definitions.
05

Streaming

Set stream: true on chat or Responses requests. The server returns Server-Sent Events and finishes with data: [DONE].

data: {"choices":[{"delta":{"content":"Hello"}}]}

data: {"choices":[{"delta":{"content":" world"}}]}

data: [DONE]

06

Tools

Pass function definitions in the Responses request. Check the model capability flags before depending on tools, parallel tool calls, reasoning, JSON schema, or vision.

function tool
POST https://api.betopia.ai/v1/responsesAuthorization: Bearer sk_your_keyContent-Type: application/json{  "model": "auto",  "input": "Find the weather in Dhaka.",  "tools": [{    "type": "function",    "name": "get_weather",    "description": "Get current weather for a city.",    "parameters": {      "type": "object",      "properties": {"city": {"type": "string"}},      "required": ["city"]    }  }]}
07

Embeddings and images

These operations use separate endpoints, not chat completions. Select a concrete model whose verified capability flag is enabled; autois only for chat and Responses. Discover each model's image limits, formats, defaults, and exact size/quality variants before sending a generation request.

POST/v1/embeddingsstring or batch input; returns vectors
single embedding
curl https://api.betopia.ai/v1/embeddings \  -H "Authorization: Bearer sk_your_key" \  -H "Content-Type: application/json" \  -d '{"model":"embedding-model-id","input":"One document to embed"}'
single embedding · JavaScript
const response = await fetch("https://api.betopia.ai/v1/embeddings", {  method: "POST", headers: { Authorization: "Bearer sk_your_key", "Content-Type": "application/json" },  body: JSON.stringify({ model: "embedding-model-id", input: "One document to embed" }),});const result = await response.json();if (!response.ok) throw new Error(`${response.status}: ${result.error ?? result.message}`);console.log(`Vector dimensions: ${result.data[0].embedding.length}`);

For a batch, pass an array of strings. The response returns one indexed vector per input.

batch embeddings
curl https://api.betopia.ai/v1/embeddings \  -H "Authorization: Bearer sk_your_key" \  -H "Content-Type: application/json" \  -d '{"model":"embedding-model-id","input":["First document","Second document"]}'
batch embeddings · JavaScript
const response = await fetch("https://api.betopia.ai/v1/embeddings", {  method: "POST", headers: { Authorization: "Bearer sk_your_key", "Content-Type": "application/json" },  body: JSON.stringify({ model: "embedding-model-id", input: ["First document", "Second document"] }),});const result = await response.json();if (!response.ok) throw new Error(`${response.status}: ${result.error ?? result.message}`);console.log(`Received ${result.data.length} vectors`);
GET/v1/models/{modelID}/image-generation-pricescaller-authorized prices and operation schema

This response includes operation_schema (schema version, maximum image count, allowed output formats/backgrounds, and default format). Image generation currently supports fixed per-image pricing; use the exact size/quality pairs in prices. A legacy token-priced configuration may return supported_variants, but generation is unavailable until an admin switches that model to per-image pricing. The endpoint enforces the same model access policy as inference.

discover image options
curl https://api.betopia.ai/v1/models/<model-id>/image-generation-prices \  -H "Authorization: Bearer sk_your_key"
POST/v1/images/generationsreturns bounded base64 image data

Normalized user request: the JSON fields below are Betopia's shared image-generation contract and keep the same names and types across providers. The platform adapter translates them upstream; actual option support depends on the selected provider/model. Admin-only provider parameters configure upstream mapping and do not change the fields users send or the platform price matrix.

generate an image
curl https://api.betopia.ai/v1/images/generations \  -H "Authorization: Bearer sk_your_key" \  -H "Content-Type: application/json" \  -d '{"model":"image-model-id","prompt":"A paper-cut mountain landscape","n":1,"size":"<supported-size>","quality":"<supported-quality>","resolution":"2K","aspect_ratio":"16:9","output_format":"webp","output_compression":80,"seed":42}'
image generation · JavaScript
const response = await fetch("https://api.betopia.ai/v1/images/generations", {  method: "POST", headers: { Authorization: "Bearer sk_your_key", "Content-Type": "application/json", "Idempotency-Key": "image-job-001" },  body: JSON.stringify({ model: "image-model-id", prompt: "A paper-cut mountain landscape", n: 1, size: "<configured-size>", quality: "<configured-quality>", resolution: "2K", aspect_ratio: "16:9", output_format: "webp", output_compression: 80, seed: 42 }),});const result = await response.json();if (!response.ok) throw new Error(`${response.status}: ${result.error ?? result.message}`);console.log(`Generated ${result.data.length} image(s)`); // Keep base64 data out of logs and persistent storage.
modelstring · requiredConcrete caller-available model with image_generation enabled. The auto model is not supported.
promptstring · requiredGeneration prompt; 1–32,000 Unicode characters.
ninteger · optionalNumber of images; defaults to 1. Must be 1–4 and no greater than operation_schema.max_images.
sizestring · requiredExact configured size from the model's price matrix. The auto value is not accepted.
qualitystring · requiredExact configured quality paired with size in the model's price matrix. The auto value is not accepted.
resolutionstring · optional512, 768, 1K, 1.5K, 2K, or 4K; provider/model support varies.
aspect_ratiostring · optionalauto or a supported ratio such as 1:1, 16:9, 9:16, 4:3, 3:2, 4:5, 1:2, or 21:9.
output_formatstring · optionalA format enabled by the model schema: png, jpeg, webp; svg is OpenRouter-only. If omitted, uses operation_schema.default_output_format.
backgroundstring · optionalIf supplied, must be enabled by the model schema: auto, opaque, or transparent. Transparent output requires png or webp.
output_compressioninteger · optional0–100, only for jpeg or webp.
seedinteger · optional0–4,294,967,295; deterministic only where supported upstream.
input_referencesarray · optionalUp to 4 {type:image_url,image_url:{url}} references. HTTPS without credentials, or base64 PNG/JPEG/WebP data URL; 8 MiB per embedded image.
streamboolean · optionaltrue returns SSE partial-image events, then a completed event after billing. Provider/model must support streaming.
Idempotency-Keyheader · optional1–128 visible ASCII characters. Replays successful non-streamed results for 24 hours; not allowed with stream:true.

The JSON body is limited to 10 MiB; unlisted fields are rejected. When resolution/aspect_ratio is set, size remains the platform price selector and is omitted from the upstream request. Read the operation schema and price matrix first. Image generation currently supports fixed per-image billing only; token-priced generation is unavailable, and a model with a legacy token-pricing configuration may return 503 until an admin switches it to per-image pricing. Streaming events are image_generation.partial_image, image_generation.completed, and [DONE]; failures after stream start use an error event. The response reports the final charge in usage.cost_usd.

POST/v1/images/editsmultipart upload; source files are not persisted
edit an image
curl https://api.betopia.ai/v1/images/edits \  -H "Authorization: Bearer sk_your_key" \  -F "model=image-edit-model-id" -F "prompt=Replace the cloudy sky with sunset" \  -F "size=<configured-size>" -F "quality=<configured-quality>" \  -F "image[]=@source.png"
image editing · JavaScript
import { readFile } from "node:fs/promises";const image = await readFile("./source.png");const form = new FormData();form.set("model", "image-edit-model-id");form.set("prompt", "Replace the cloudy sky with a clear sunset");form.set("size", "<configured-size>"); form.set("quality", "<configured-quality>");form.set("image[]", new Blob([image], { type: "image/png" }), "source.png");const response = await fetch("https://api.betopia.ai/v1/images/edits", { method: "POST", headers: { Authorization: "Bearer sk_your_key" }, body: form });const result = await response.json();if (!response.ok) throw new Error(`${response.status}: ${result.error ?? result.message}`);console.log(`Received ${result.data.length} edited image(s)`); // Do not log or persist the base64 output.
modeltext · requiredConcrete caller-available model with image_edit enabled. The auto model is not supported.
prompttext · requiredEdit instructions; 1–32,000 Unicode characters.
sizetext · requiredExact size configured for this model; the auto value is not accepted.
qualitytext · requiredExact quality configured for this model; the auto value is not accepted.
image[]file · required, repeatedUpload 1–4 PNG or JPEG source images. Each file is at most 8 MiB; the entire request is at most 10 MiB. Each image is limited to 16,384 pixels per side and 100 megapixels.
maskfile · optional (one)Optional PNG with an alpha channel when supported by the configured provider. OpenRouter image-edit models do not accept mask uploads. The first source image must also be PNG, and the mask dimensions must match it. The same 8 MiB file, 10 MiB request, and image-dimension limits apply.

Send only the fields above as multipart/form-data; extra or duplicate fields are rejected. Edits do not accept n, output_format, or background, and return one edited image. Admins configure provider-specific options per exact size/quality rule; for OpenRouter models, those saved options (for example resolution and aspect_ratio) are translated into OpenRouter's image-to-image request. This keeps the user request identical across providers. A configured platform price is separate from the provider's charge. To use a mask, add -F "mask=@mask.png" only for a provider that supports masks; uploads are not persisted.

Do not persist prompts, uploaded images, or large base64 outputs in browser storage or analytics. See the API Guide for field limits, exact pricing rules, and error behavior.

08

Models

The authenticated catalog includes models available to the current caller. Model records include operation capability flags and token limits. Use flags only when capabilities_configured is true; `auto` is only for chat and Responses.

GET/v1/modelscaller-specific model availability
GET/v1/public/modelspublic model metadata
list models
GET https://api.betopia.ai/v1/modelsAuthorization: Bearer sk_your_key→ {  "models": [{    "id": "auto",    "name": "auto",    "provider": "betopia",    "capabilities_configured": false,    "available": true  }],  "count": 1}
list caller-available models
curl https://api.betopia.ai/v1/models \  -H "Authorization: Bearer sk_your_key"

Filter for a capability only after capabilities_configured is true; use each returned model ID explicitly for embedding and image calls.

find embedding-capable models · JavaScript
const response = await fetch("https://api.betopia.ai/v1/models", { headers: { Authorization: "Bearer sk_your_key" } });const { models } = await response.json();const embeddingModels = models.filter((model) => model.capabilities_configured === true && model.capabilities?.embeddings === true);const imageModels = models.filter((model) => model.capabilities_configured === true && model.capabilities?.image_generation === true);console.log(embeddingModels.map(({ id, name }) => ({ id, name })));const imageModel = imageModels[0];if (imageModel) {  const optionsResponse = await fetch(`https://api.betopia.ai/v1/models/${encodeURIComponent(imageModel.id)}/image-generation-prices`, { headers: { Authorization: "Bearer sk_your_key" } });  if (!optionsResponse.ok) throw new Error(`Could not read image options: ${optionsResponse.status}`);  const options = await optionsResponse.json();  console.log(options.operation_schema, options.prices ?? options.supported_variants);}
idstringModel identifier used in requests.
providerstringBetopia model provider.
max_tokensintegerMaximum token capacity exposed.
capabilitiesobjectChat, Responses, vision, embeddings, image generation, image editing, and more.
input_cost_per_millionnumberInput token price.
output_cost_per_millionnumberOutput token price.
09

API keys

Keys are created with a JWT. The full secret is returned only at creation time; list responses expose the prefix. Revoke keys you no longer need.

GET/v1/keyslist key metadata and prefixes
POST/v1/keyscreate a key; requires JWT
DELETE/v1/keys/{keyID}revoke a key permanently
POST/v1/keys/{keyID}/reset-tokensreset the token counter
create a key
POST https://api.betopia.ai/v1/keysAuthorization: Bearer eyJ...Content-Type: application/json{"name":"production","expires_in_days":0,"token_limit":0,"allowed_models":[]}→ {"id":"...","key":"sk_...","key_prefix":"..."}
10

Usage and limits

Read the current quota snapshot or query usage analytics by day, month, model, key, and audit event.

GET/v1/user/limitscost limit, period, used, remaining, reset
read current quota
curl https://api.betopia.ai/v1/user/limits \  -H "Authorization: Bearer sk_your_key"
quota and error handling · JavaScript
const response = await fetch("https://api.betopia.ai/v1/images/generations", {  method: "POST", headers: { Authorization: "Bearer sk_your_key", "Content-Type": "application/json" },  body: JSON.stringify({ model: "image-model-id", prompt: "A simple landscape", size: "<configured-size>", quality: "<configured-quality>" }),});const payload = await response.json().catch(() => ({}));let requestError;if (response.status === 429) requestError = new Error("Quota reached: " + (payload.message ?? "request denied") + "; reset " + (payload.reset_at ?? "not scheduled"));else if (!response.ok) requestError = new Error(response.status + " request " + (response.headers.get("X-Request-Id") ?? "unknown") + ": " + (payload.error ?? payload.message));const limits = await fetch("https://api.betopia.ai/v1/user/limits", { headers: { Authorization: "Bearer sk_your_key" } });if (limits.ok) console.log(await limits.json()); // cost_limit_usd, used_usd, remaining_usd, quota_period, reset_atif (requestError) throw requestError;
GET/v1/user/analytics?days=30daily totals and model/provider composition
GET/v1/user/analytics/daily?date=YYYY-MM-DDdaily detail
GET/v1/user/analytics/monthly?months=12monthly totals
GET/v1/user/audit-log?limit=50recent account activity
11

Coding Agent access

The Coding Agent supports two explicit billing modes. Choose subscription sign-in for plan-limited access, or enter a general API key for PAYG wallet access. These credentials and routes are not interchangeable.

Subscription sign-insk_ca_…Use the Coding Agent device-login flow and the /v1/coding-agent base path. The active plan controls models and daily/monthly limits.
Manual API keysk_…Use the normal /v1 API path. Requests use PAYG wallet balance and retain normal broad model access.
Free SSO/SBUExisting credentialExisting shared tier allowance behavior is unchanged; the paid subscription boundary does not apply.
Reaching a subscription limit or losing subscription access does not switch the client to PAYG. The user must explicitly select manual API-key mode. A subscription credential is rejected on general /v1 account, billing, model, and inference routes; only the agent gateway and self-revocation are available to it.
choose an authentication mode
# Subscription credential: use only the Coding Agent gatewaycurl https://api.betopia.ai/v1/coding-agent/models \  -H "Authorization: Bearer sk_ca_your_subscription_key"# Manual general API key: PAYG wallet, normal API accesscurl https://api.betopia.ai/v1/models \  -H "Authorization: Bearer sk_your_general_key"
GET/v1/coding-agent/modelssubscription credential; honor available on each model
GET/v1/coding-agent/models/{modelID}/image-generation-pricessubscription credential; read configured size/quality choices
GET/v1/coding-agent/models/{modelID}/image-edit-pricessubscription credential; read configured size/quality choices
POST/v1/coding-agent/completionssame request body as /v1/completions
POST/v1/coding-agent/chat/completionssame request body as /v1/chat/completions
POST/v1/coding-agent/embeddingssame request body as /v1/embeddings
POST/v1/coding-agent/responsessame request body as /v1/responses
POST/v1/coding-agent/images/generationssame normalized image-generation fields
POST/v1/coding-agent/images/editssame multipart image-edit fields
GET/v1/coding-agent/plansJWT session; list plans available to purchase
GET/v1/coding-agent/subscriptionJWT session; status and percentages only
POST/v1/coding-agent/subscription/checkoutJWT session; body: { plan_id }
POST/v1/coding-agent/subscription/cancelJWT session; cancellation at paid-period end
POST/v1/coding-agent/subscription/portalJWT session; update payment method or pay an invoice

The daily counter resets at the server-configured timezone; monthly usage resets on the Stripe billing anniversary. The user interface shows percentages only, not the USD caps or consumed USD amount.

12

Errors

Failed requests return JSON with an error message. When available, keep the request ID with your logs for troubleshooting.

error body
{  "error": "invalid or expired api key",  "request_id": "req_01hzy..."}
400bad requestInvalid body or unsupported capability.
401unauthorizedMissing, invalid, or expired credentials.
403forbiddenAccount verification or permission required.
404not foundUnknown model, key, or response ID.
429quota limitAccount or API key limit reached.
500server errorRetry and include the request ID.
13

Endpoints

All paths are relative to the base URL shown in the navigation.

POST/v1/auth/registerCreate an account
POST/v1/auth/loginReturn a JWT
POST/v1/auth/verify-email-otpVerify an email address
GET/v1/modelsList models available to the caller
GET/v1/public/modelsList public model metadata
POST/v1/chat/completionsCreate a chat completion
POST/v1/completionsCreate a text completion
POST/v1/responsesCreate a response
POST/v1/embeddingsCreate embeddings with an explicit model
POST/v1/images/generationsGenerate images with an explicit model
POST/v1/images/editsEdit images with an explicit model
GET/v1/user/limitsRead current quota state
GET/v1/user/analyticsRead usage analytics
GET/v1/coding-agent/plansList plans available to purchase (JWT)
GET/v1/coding-agent/subscriptionRead subscription status and usage percentages (JWT)
POST/v1/coding-agent/subscription/checkoutStart a Coding Agent subscription (JWT)
POST/v1/coding-agent/subscription/cancelSchedule cancellation (JWT)
POST/v1/coding-agent/subscription/portalManage payment method or open invoices (JWT)
GET/v1/coding-agent/modelsList models available to a subscription credential
POST/v1/coding-agent/completionsCreate a text completion using subscription allowance
POST/v1/coding-agent/chat/completionsUse subscription allowance from Coding Agent
POST/v1/coding-agent/embeddingsCreate embeddings using subscription allowance
POST/v1/coding-agent/images/generationsGenerate images using subscription allowance
POST/v1/coding-agent/images/editsEdit images using subscription allowance
POST/v1/coding-agent/responsesCreate a response using subscription allowance
GET/v1/keysList API keys
POST/v1/keysCreate an API key
BetopiaAI API / v1