Everything you need to call the embeddings and chat endpoints.
Authenticate every request with a bearer API key in the Authorization header. Keys are secret — the raw value is shown only once, when you create or rotate a key.
Create and manage keys under Settings → API Keys.
Authorization: Bearer wba_live_xxxxxxxxxxxxxxxxxxxxxxxxBase URL: https://api.webanto.com/api/v1
Generate embeddings with DINOv3 or CLIP. Requires a key scoped for embeddings.
POST /api/v1/embeddings
Request
curl https://api.webanto.com/api/v1/embeddings \
-H "Authorization: Bearer $WEBANTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "clip",
"input": ["a photo of a cat", "a photo of a dog"]
}'Response
{
"model": "clip",
"data": [
{ "index": 0, "embedding": [0.0123, -0.0456, "..."] },
{ "index": 1, "embedding": [0.0789, -0.0012, "..."] }
],
"usage": { "total_tokens": 14 },
"credits": 1
}Generate a chat completion. Requires a key scoped for chat.
POST /api/v1/chat
Request
curl https://api.webanto.com/api/v1/chat \
-H "Authorization: Bearer $WEBANTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen",
"messages": [{ "role": "user", "content": "Explain embeddings in one sentence." }]
}'Response
{
"id": "chatcmpl-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"object": "chat.completion",
"created": 1721260800,
"model": "qwen",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 12, "completion_tokens": 24, "total_tokens": 36 },
"credits": 1
}Embeddings are charged by number of inputs; chat is charged by total tokens used. Each response includes the credits charged. Requests are rate limited per API key; exceeding the limit returns 429 with a Retry-After header.
Wiring this up with an AI coding assistant? Copy the prompt below and hand it to your agent — a self-contained spec of the endpoints, auth, models, and error handling.
# Integrate with the Webanto AI API
You are integrating an app with Webanto's inference API (chat + embeddings).
It is OpenAI-shaped but NOT the OpenAI API — follow this exact contract.
## Base URL and auth
- Base URL: https://api.webanto.com/api/v1
- Get an API key at https://webanto.com/account/settings/api-keys (format: wba_live_...).
Keys are scoped per endpoint: "chat" and/or "embeddings".
- Send it as header: Authorization: Bearer wba_live_... (or x-api-key: wba_live_...).
- Server-side only. Never expose the key in a browser or client bundle.
## POST /api/v1/chat (scope: chat)
Request body:
{
"model": "qwen",
"messages": [{ "role": "system|user|assistant", "content": "..." }],
"max_tokens": 1024,
"temperature": 0.7,
"use_memory": false
}
- model must be "qwen" (multimodal: text + vision). messages: 1-50 turns.
- qwen is a REASONING model: keep max_tokens >= ~1024 or the visible output can be empty.
- VISION: a turn's "content" is a string OR an array of parts to send an image:
"content": [
{ "type": "text", "text": "What card is this?" },
{ "type": "image_url", "image_url": { "url": "https://cdn.example.com/card.jpg" } }
]
image_url.url accepts an https:// URL or a base64 data:image/...;base64,... URL.
Base64 must fit the ~4.5MB request-body limit — use an https URL for larger images.
Response body:
{
"id": "chatcmpl-...", "object": "chat.completion", "created": 0, "model": "qwen",
"choices": [{ "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" }],
"usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 },
"credits": 1
}
## POST /api/v1/embeddings (scope: embeddings)
Request body: { "model": "nomic-embed-text", "input": "text" | ["t1", "t2"] }
- Models: nomic-embed-text (text), clip (image), dinov3 (image).
- input: up to 100 items, each <= 8192 chars.
Response body:
{
"model": "nomic-embed-text",
"data": [{ "index": 0, "embedding": [0.01, -0.02] }],
"usage": { "total_tokens": 3 },
"credits": 1
}
## POST /api/v1/image-embeddings (scope: embeddings)
DINOv3 image embeddings (1280-dim, L2-normalized). Custom (non-OpenAI) contract.
Request body: one of, up to 80 images per batch (backend is CPU, ~0.3s/image):
{ "urls": ["https://cdn.example.com/card.jpg"] }
{ "images": [{ "b64": "<base64 image>" }] }
- urls must be https://. Do not mix urls and images in one request.
Response body:
{ "model": "dinov3", "dim": 1280,
"embeddings": [[0.01, -0.02, "..."]],
"errors": [], "credits": 1 }
## Health checks
- GET /api/v1/status (no auth) -> { "status": "ok", "timestamp": "..." } (liveness)
- GET /api/v1/health (auth) -> { "ok": true, "organizationId": "...", "scopes": ["chat"] }
## Errors (always check the HTTP status before parsing the body)
- 400 { "error": { "code": "invalid_request | invalid_json | unknown_model", "message": "..." } }
- 401 { "error": "Missing API key" } or { "error": "Invalid or revoked API key" }
- 403 { "error": "API key is not scoped for \"chat\"" }
- 402 { "error": { "code": "quota_exceeded", "message": "..." }, "quota": { "used": 0, "limit": 0, "remaining": 0 } }
- 429 { "error": "...", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 30 } plus a Retry-After header
- 502 { "error": { "code": "upstream_error", "message": "..." } }
## Reliability rules
- Every successful response returns "credits" (drawn from the organization AI pool). Budget for it.
- For safe retries, send an Idempotency-Key: <uuid> header on chat and embeddings.
- Requests are rate limited per API key; on 429, honor Retry-After and back off.
## Reference call (Node)
const res = await fetch("https://api.webanto.com/api/v1/chat", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WEBANTO_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
model: "qwen",
messages: [{ role: "user", content: "Explain embeddings in one sentence." }],
}),
});
if (!res.ok) throw new Error(`Webanto API ${res.status}: ${await res.text()}`);
const { choices, credits } = await res.json();