Build with Xenition: OpenAI-compatible chat, embeddings, images, speech, transcription and video, plus decks, diagrams, documents and 3D through one API. Pay only for calls that succeed.
The Xenition API gives your code everything Xenition does: chat and reasoning, embeddings, images, speech, transcription, video, 3D models, decks, diagrams, documents, writing tools and more. It uses one key and one bill, and every response follows one format.
It speaks the OpenAI format. If you already use an OpenAI SDK, change the base_url, use a Xenition key and a Xenition model name, and your code works.
/runs for everything OpenAI has no endpoint for.| Base URL | https://api.xenition.com/api/v1 |
| Auth | Authorization: Bearer xk_live_… |
| Format | JSON in, JSON out (audio endpoints return audio bytes) |
| Keys | xenition.com → Developers |
Sign in to Xenition, open Developers in the left sidebar (/app/developers), and create a key. Copy it: it is shown only once. Keep it on your server and never in a browser or mobile app.
Set XENITION_API_KEY in your environment and run one of the examples on the right. Use the official OpenAI SDK (pip install openai or npm install openai) or plain HTTP.
Every successful response reports what it cost you in usage.cost (USD). The X-Xenition-Credits-Remaining header shows your balance after the call.
curl -X POST https://api.xenition.com/api/v1/chat/completions \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"xenition","messages":[{"role":"user","content":"Hello!"}]}'{
"id": "chatcmpl-8f2c…",
"object": "chat.completion",
"created": 1790726400,
"model": "xenition",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "Hello! How can I help?" },
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 9, "completion_tokens": 7, "total_tokens": 16, "cost": 0.000021 }
}Send your key as a bearer token on every request: Authorization: Bearer xk_live_…. Keys start with xk_live_, so secret scanners can recognise one committed by mistake. Revoke a leaked key at once from the Developers page.
A key can be limited to scopes. If no scopes are chosen, the key can use everything.
| Scope | Allows |
|---|---|
chat | /chat/completions |
embeddings | /embeddings |
images | /images/generations |
audio | /audio/speech, /audio/transcriptions |
videos | /videos |
runs | /runs |
tools | /tools |
Never put a key in client-side code. Call Xenition from your server and pass the result to your app.
You always use a Xenition model name. What runs behind a name is managed by Xenition and improves over time without changes to your code. model is optional: each endpoint has a default.
| Model | Use for | Endpoint |
|---|---|---|
xenition | General purpose (default) | chat/completions |
xenition-fast | Lowest latency and cost | chat/completions |
xenition-pro | Deeper reasoning | chat/completions |
xenition-code | Writing and editing code | chat/completions |
xenition-embed | Embeddings (default) | embeddings |
xenition-image | Text to image (default) | images/generations |
xenition-video | Text to video (default) | videos |
xenition-voice | Text to speech (default) | audio/speech |
xenition-transcribe | Speech to text (default) | audio/transcriptions |
GET /models lists them with current per-token prices. Any other model name returns 400 invalid_request_error with param: "model".
curl https://api.xenition.com/api/v1/models \
-H "Authorization: Bearer $XENITION_API_KEY"Xenition is also a remote MCP server, so any assistant that speaks the Model Context Protocol — ChatGPT, Claude, Claude Code, VS Code, Cursor and Microsoft 365 Copilot — can use your workspace directly: create and edit documents, decks, sheets, boards, forms and 3D models, build and deploy apps, run automations, and answer questions from your own files.
Unlike the REST API above, MCP needs no API key. Users sign in with their Xenition account, and the assistant acts only on that user's own workspace.
Both are remote Streamable HTTP endpoints. Point an MCP client at the URL — there is nothing to install or host.
| URL | What it exposes |
|---|---|
https://api.xenition.com/mcp | Everything: the full workspace and AI image, video and voice tools. Use this everywhere. |
https://api.xenition.com/mcp/workspace | The same workspace without AI media generation. This is the address listed in the Claude connector directory, which does not accept connectors that generate media with AI. |
Same account, same sign-in and the same credits on both URLs. Most users only ever need the first one.
Sign-in uses OAuth 2.1 with PKCE, discovered automatically from the server (RFC 8414 / 9728), so clients that support Client ID Metadata Documents or Dynamic Client Registration connect with nothing to configure. The first time a tool touches your workspace, the client opens a Xenition sign-in page. Utility tools — calculators, converters and the form and template catalogues — work before you sign in; anything that reads or writes your own content asks you to connect first.
Listing the tools needs no key, so you can check the server from a terminal. Send a tools/list request to either URL:
curl -X POST https://api.xenition.com/mcp/workspace \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-06-18" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{ "name": "search_artifacts", "title": "Search your workspace", "annotations": { "readOnlyHint": true } },
{ "name": "create_slides", "title": "Create a slide deck" },
{ "name": "build_app", "title": "Build a web app" }
]
}
}Give any client the one address https://api.xenition.com/mcp (or the /mcp/workspace URL where AI media is not allowed). On first use the client opens Xenition sign-in; approve it once and your workspace is available.
| Client | How to add it |
|---|---|
| Claude & Claude Desktop | Settings → Connectors → Add custom connector, then paste the URL. |
| Claude Code | claude mcp add --transport http xenition https://api.xenition.com/mcp |
| ChatGPT | Add a custom connector and paste the URL. |
| VS Code & Cursor | Add an MCP server with the URL over HTTP transport. |
Xenition is also published in the official MCP Registry as com.xenition.api/xenition, so directories and the VS Code @mcp gallery can find it without a manual URL.
Tools that only read, or that compute a result (calculators, converters, searches), run without a sign-in. Tools that create, change, publish or deploy always act on the signed-in user's own workspace and nothing else.
402 insufficient_credits and nothing runs.usage.cost (USD). Audio responses report it in the X-Xenition-Cost header.An error, a timeout, an empty or invalid answer, a stream that breaks before it finishes, or a video or 3D job that fails or expires: each releases the hold, and you pay nothing. Built-in tools (/tools) are always free.
An account that has not paid yet (no paid plan, no credits, never bought any) can make 2 successful calls per service and 10 in total. Each call is charged from the 25,000 one-time credits of your free plan, at the normal price. Failed calls do not count and cost nothing. GET /usage shows what is left in free_tier. After that, upgrade or add credits to keep going.
Errors always use OpenAI's shape, so OpenAI SDKs raise their usual typed exceptions: {"error": {"message", "type", "param", "code"}}.
| HTTP | type | Meaning | Charged? |
|---|---|---|---|
| 400 | invalid_request_error | Bad parameters or unknown model; param names the field | No |
| 401 | authentication_error | Missing, wrong or revoked key | No |
| 402 | insufficient_credits | Not enough credits, key cap reached, or free tier used up | No |
| 403 | permission_error | The key lacks the scope, or the account is suspended | No |
| 404 | not_found_error | Unknown id, including another account's | No |
| 409 | idempotency_error | Idempotency-Key reused with a different body, or still in flight | No |
| 429 | rate_limit_error | Per-key rate limit; wait for Retry-After | No |
| 502 | upstream_error | The model failed to answer | No |
| 503 | service_unavailable | Temporarily unavailable, or busy (see Retry-After) | No |
| 504 | timeout_error | The model took too long | No |
{
"error": {
"message": "Unknown model for chat. Use a Xenition model name from GET /api/v1/models.",
"type": "invalid_request_error",
"param": "model",
"code": "invalid_request_error"
}
}Each key has a requests-per-minute limit (60 by default). Every response carries the OpenAI-named headers below. Over the limit you get 429 with Retry-After in seconds.
| Header | Meaning |
|---|---|
X-RateLimit-Limit-Requests | Requests allowed per minute for this key |
X-RateLimit-Remaining-Requests | Requests left in the current minute |
X-RateLimit-Reset-Requests | Time until the window resets, e.g. 42s |
X-Xenition-Credits-Remaining | Credits you can still spend |
Send Idempotency-Key: <a unique value> with any POST to make retries safe. A repeat within 24 hours returns the first response and is never charged twice. The same key with a different body returns 409.
If the first attempt failed with a 5xx or 429, it is not remembered: your retry with the same key really runs. Those failures were not charged, so there is nothing to protect.
Retry 429, 502, 503 and 504 with exponential backoff. Do not retry 400, 401, 402, 403 or 404; fix the request first.
curl -X POST https://api.xenition.com/api/v1/chat/completions \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Idempotency-Key: order-1234-summary" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Summarise order 1234"}]}'Set stream: true on chat completions to receive tokens as they are generated, as server-sent events of chat.completion.chunk objects, ending with data: [DONE]. Add stream_options: {"include_usage": true} to get a final chunk with usage, including cost.
If the model fails mid-answer, the stream ends with data: {"error": {…}} instead of [DONE], and you are not charged. If your client disconnects but the answer still completes, it is charged, as with any completed answer.
curl -X POST https://api.xenition.com/api/v1/chat/completions \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"xenition-fast","stream":true,"messages":[{"role":"user","content":"Write a haiku"}]}' \
--no-bufferAsynchronous jobs (videos and 3D runs) can call you when they finish. Pass webhook_url, a public https URL, when you create the job. You get one POST:
{"id": "evt_…", "object": "event", "type": "video.completed" | "video.failed" | "run.completed" | "run.failed", "created_at": …, "data": { …the video or run object… }}
Each delivery has Xenition-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of "<t>." + raw body, keyed with your webhook secret (on the Developers page). Recompute it over the raw body, compare in constant time, and reject timestamps older than a few minutes.
Answer with any 2xx within 10 seconds. Other answers are retried with exponential backoff, up to 8 attempts.
# Webhooks are received, not sent: see the Python or Node verifier.Generated files (images, speech, videos, 3D models, illustrations) come back as Xenition links, …/api/v1/media/m_…. They need no key and expire after 24 hours. Download what you want to keep. Links support HTTP Range, so video players can seek.
POST /chat/completions
Generates a model response for a conversation. OpenAI-compatible, including tools, JSON mode and streaming.
| Name | Type | Description |
|---|---|---|
messages (required) | array | The conversation: [{role: "system"|"user"|"assistant"|"tool", content}]. Content may be text or an array of parts (text, image_url). |
model | string | xenition (default), xenition-fast, xenition-pro or xenition-code. |
max_tokens | integer | Maximum tokens to generate, up to 4096 (the default). max_completion_tokens is accepted too. |
temperature | number | Sampling temperature, 0–2. |
top_p | number | Nucleus sampling. |
stop | string | array | Up to 4 stop sequences. |
seed | integer | Best-effort determinism. |
response_format | object | {"type": "json_object"} for JSON mode. |
tools | array | Function tools the model may call. |
tool_choice | string | object | auto, none, required, or a specific function. |
parallel_tool_calls | boolean | Allow several tool calls in one turn. |
presence_penalty / frequency_penalty | number | Repetition controls. |
stream | boolean | Stream the answer as server-sent events. See Streaming. |
stream_options | object | {"include_usage": true} adds a final usage chunk with cost. |
n | integer | Only 1 is supported. |
curl -X POST https://api.xenition.com/api/v1/chat/completions \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"xenition","messages":[{"role":"system","content":"Reply in JSON."},{"role":"user","content":"Three fruits and their colours"}],"response_format":{"type":"json_object"}}'{
"id": "chatcmpl-…",
"object": "chat.completion",
"model": "xenition",
"choices": [{ "index": 0, "message": { "role": "assistant", "content": "{\"fruits\": […]}" }, "finish_reason": "stop" }],
"usage": { "prompt_tokens": 24, "completion_tokens": 31, "total_tokens": 55, "cost": 0.00009 }
}POST /embeddings
Turns text into vectors for search, clustering and retrieval.
| Name | Type | Description |
|---|---|---|
input (required) | string | array | A string, or up to 2048 strings (each at most 32,000 characters). |
model | string | xenition-embed (default). |
encoding_format | string | float (default) or base64 (little-endian float32; the OpenAI Python SDK requests this by default and decodes it). |
curl -X POST https://api.xenition.com/api/v1/embeddings \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":["first document","second document"]}'{
"object": "list",
"model": "xenition-embed",
"data": [{ "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, …] }],
"usage": { "prompt_tokens": 4, "total_tokens": 4, "cost": 0.0000009 }
}POST /images/generations
Generates images from a prompt.
| Name | Type | Description |
|---|---|---|
prompt (required) | string | What to draw, up to 2000 characters. |
model | string | xenition-image (default). |
n | integer | 1–4 images (default 1). |
size | string | 1024x1024 (default), 1536x1024 (landscape), 1024x1536 (portrait) or auto. |
response_format | string | url (a Xenition link, 24h; default) or b64_json. |
curl -X POST https://api.xenition.com/api/v1/images/generations \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"a red fox in the snow, watercolor","size":"1024x1024"}'{
"created": 1790726400,
"data": [{ "url": "https://api.xenition.com/api/v1/media/m_4f1c…" }],
"usage": { "cost": 0.0112 }
}POST /audio/speech
Turns text into spoken audio. The response body is the audio file. The cost is in the X-Xenition-Cost header.
| Name | Type | Description |
|---|---|---|
input (required) | string | The text, up to 4000 characters. |
model | string | xenition-voice (default). |
voice | string | A voice name. OpenAI stock names (alloy, nova, …) select the default voice. |
response_format | string | mp3 (default), opus, aac, flac or wav. |
curl -X POST https://api.xenition.com/api/v1/audio/speech \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"Welcome to Xenition."}' \
--output welcome.mp3POST /audio/transcriptions
Transcribes audio into text. Send multipart/form-data.
| Name | Type | Description |
|---|---|---|
file (required) | file | The audio file, up to 25 MB (mp3, m4a, wav, webm, …). |
model | string | xenition-transcribe (default). |
language | string | ISO language hint, e.g. en or bn. |
response_format | string | json (default), text or verbose_json. |
curl -X POST https://api.xenition.com/api/v1/audio/transcriptions \
-H "Authorization: Bearer $XENITION_API_KEY" \
-F [email protected] \
-F language=en{ "text": "Good morning, everyone…", "usage": { "cost": 0.0021 } }POST /videos
Video generation is asynchronous. POST /videos starts a job and returns a video object with status: "queued". Poll GET /videos/{id} until completed (or pass webhook_url), then download from GET /videos/{id}/content. GET /videos lists your recent videos.
| status | Meaning |
|---|---|
queued | Accepted, waiting |
in_progress | Rendering |
completed | Ready: download from /content (for 24h) |
failed | No video was made; you were not charged |
| Name | Type | Description |
|---|---|---|
prompt (required) | string | What happens in the video, up to 2000 characters. |
model | string | xenition-video (default). |
seconds | string | integer | Length, 1–20 (default 5). |
size | string | e.g. 1280x720 (default) or 720x1280. |
webhook_url | string | A public https URL to notify when the job finishes. |
curl -X POST https://api.xenition.com/api/v1/videos \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"a fox running through snow at dawn","seconds":"8","size":"1280x720"}'{
"id": "video_9c1e…",
"object": "video",
"model": "xenition-video",
"status": "completed",
"progress": 100,
"seconds": "8",
"size": "1280x720",
"created_at": 1790726400,
"completed_at": 1790726495,
"expires_at": 1790812895,
"error": null,
"usage": { "cost": 2.1 }
}POST /runs
Everything OpenAI has no endpoint for runs through one call: decks, diagrams, documents, spreadsheet formulas, quizzes, lessons, book chapters, code edits, writing tools, research synthesis, illustrations and 3D models. Pass a capability and its input, and get a run object back.
Most capabilities finish in the same call (status: "completed"). Asynchronous ones (such as model3d.generate) return queued: poll GET /runs/{id} or pass webhook_url. Add "background": true to run any capability that way (useful for long inputs); capabilities marked background in the catalogue always run so, because they can take longer than a request may stay open. GET /capabilities (no key needed) lists every capability with its inputs:
The full capability list is live at GET https://api.xenition.com/api/v1/capabilities.
| Name | Type | Description |
|---|---|---|
capability (required) | string | A capability id from GET /capabilities, e.g. deck.create. |
input (required) | object | The capability's inputs. Unknown inputs are rejected with 400. |
webhook_url | string | For asynchronous capabilities only. |
curl -X POST https://api.xenition.com/api/v1/runs \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"capability":"deck.create","input":{"source":"Q3 results: revenue up 18%, two new markets, hiring plan for Q4","title":"Q3 review"}}'{
"id": "run_7a3f…",
"object": "run",
"capability": "deck.create",
"status": "completed",
"output": {
"markdown": "# Q3 review\n---\n## Revenue up 18%…",
"slides": 8
},
"error": null,
"usage": { "cost": 0.0187 },
"created_at": 1790726400,
"completed_at": 1790726412
}POST /tools/{id}/run
Nearly 70 converters and calculators computed by Xenition itself: length, weight, temperature and data size; loan, compound interest, APR/APY, CAGR, ROI, margin, salary, split bill and unit price; and text tools such as find/replace, case formats, reading time and sort lines. Always free. GET /tools lists them with their inputs; converters take value, from and to.
| Name | Type | Description |
|---|---|---|
inputs (required) | object | The tool's inputs, e.g. {"value": 5, "from": "km", "to": "mi"}. |
curl -X POST https://api.xenition.com/api/v1/tools/length-convert/run \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputs":{"value":5,"from":"km","to":"mi"}}'{
"object": "tool.result",
"tool": "length-convert",
"title": "Length converter",
"result": "5 km = 3.10686 mi",
"usage": { "cost": 0 }
}GET /forms
Every one of the app's 200+ document generators: resume, cover letter, invoice, proposal, contract, emails, plans and more. GET /forms lists them (q to search, category to filter) and GET /forms/{id} shows a form's inputs. Both are public: no key needed.
Generate a document with the form.generate capability on POST /runs: pass the form id and its values. Required inputs are checked, and an input name the form does not have is refused, before anything is charged. For anything the catalogue does not cover, document.generate takes a purpose and your own labelled answers.
Results are Markdown. Turn one into a file with POST /exports/pdf.
| Name | Type | Description |
|---|---|---|
q | string | Search the catalogue, e.g. resume. |
category | string | Only this category. |
limit | integer | At most this many forms. |
curl -X POST https://api.xenition.com/api/v1/runs \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"capability":"form.generate","input":{"form":"abandoned-cart-email","values":{"brand":"Wildgrain Coffee","customer":"Jordan"}}}'{
"id": "run_7f3c…",
"object": "run",
"capability": "form.generate",
"status": "completed",
"output": {
"result": "Subject: Your coffee is waiting, Jordan…",
"title": "Abandoned Cart Email"
},
"usage": { "cost": 0.0021 }
}POST /actions/{name}
Everything you can do in your own Xenition workspace, as the key's owner: build, edit and deploy no-code apps (and start from a template), run agents and missions, create slides and documents, export a saved document to PDF, keep notes, tasks, a calendar and a ledger, search and publish your files, ask questions across your workspace, manage automations, and more.
GET /actions lists every action with its JSON input_schema and whether it is paid. These are the same actions ChatGPT and Claude use through Xenition's MCP server, so they behave the same everywhere. The key needs the actions scope.
Actions are billed like the app: model work draws your credits, and an account at its credit limit gets 402 before a paid action runs. Reads and bookkeeping (lists, notes, tasks, deploys, PDF export) are free. Actions do not use the API's free calls. Each result reports what the call charged in usage.cost (with billed_to: "app_credits"), and the same figure appears in your usage and request log; work an action starts in the background, such as an app build, is billed as it runs.
| Name | Type | Description |
|---|---|---|
input (required) | object | The action's inputs, matching its input_schema. |
curl -X POST https://api.xenition.com/api/v1/actions/add_task \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":{"title":"Send the invoice to Rahim","due":"2026-10-10"}}'{
"object": "action_result",
"action": "add_task",
"is_error": false,
"text": "Added \"Send the invoice to Rahim\" to your board.",
"data": { "…": "the updated board" },
"usage": { "cost": 0, "billed_to": "app_credits" }
}POST /exports/pdf
Turn any result into a file. All four are free.
| Endpoint | Send | Get |
|---|---|---|
POST /exports/pdf | markdown, title | A print-ready PDF, with the same renderer as the app |
POST /exports/docx | markdown, title | A Word document: headings, lists, tables, links, quotes and code keep their formatting |
POST /exports/pptx | markdown (slides separated by a --- line; the first heading of each slide is its title), title | A 16:9 PowerPoint deck: deck.create output works as is |
POST /exports/xlsx | csv, sheet | An Excel sheet with a bold, frozen header; numbers stay numbers, IDs and ZIP codes stay text |
Images must be inline data:image/... URIs; linked images are left out (nothing is fetched).
| Name | Type | Description |
|---|---|---|
markdown (required) | string | The document, up to 2 MB. |
title | string | A title printed at the top. |
curl -X POST https://api.xenition.com/api/v1/exports/pdf \
-H "Authorization: Bearer $XENITION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"markdown":"# Jordan Lee\n\nProduct designer…","title":"Resume"}' \
--output resume.pdfGET /usage
Your spend per day and kind for the last days (default 30, max 90), credits remaining, and your free tier. GET /generation?id=… returns one call's status and cost.
| Name | Type | Description |
|---|---|---|
days | integer | How far back, 1–90. |
curl https://api.xenition.com/api/v1/usage?days=7 \
-H "Authorization: Bearer $XENITION_API_KEY"{
"object": "usage",
"days": 7,
"data": [{ "day": "2026-09-30", "kind": "chat", "requests": 42, "failed": 1, "cost": 0.0931 }],
"credits_remaining": 118230,
"balance_usd": 11.823,
"free_tier": { "active": false, "remaining_calls": 0, "total_calls": 10, "per_service": 2 }
}