Chat, Images, Video and Music Behind a Single API Key
A practical tour of one AI API for every model type - chat over the OpenAI format, image, video and music as async tasks, costs known before you call.
Picture a small app that writes a product blurb, draws a cover image, renders a short teaser clip and adds a music bed. Done the usual way, that is four vendors, four keys, four billing pages and four different shapes of error. With Kadraris API it is one key and one prepaid balance. Text goes through POST /v1/chat/completions, and media goes through POST /v1/generate, which you then read back with GET /v1/tasks/{id}. Changing the model means changing one string.
Below is how that works in practice, with code you can paste and run.
Start with the endpoint map
Here is everything the API exposes. Base URL: https://kadraris.com/v1. Send your key as Authorization: Bearer sk-kd-... or in an x-api-key header (see authentication).
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/chat/completions | Chat in OpenAI format, streaming OK |
| POST | /v1/messages | Chat in Anthropic format |
| POST | /v1/generate | New image, video or audio job (202) |
| GET | /v1/tasks/{id} | Job status and output files |
| GET | /v1/models | Public catalog with prices |
| GET | /v1/models/{id} | One model and its parameters |
| GET | /v1/balance | What is left on your account |
The Anthropic-format endpoint is the one Claude Code talks to, so coding agents work without extra setup.
Why text is synchronous and media is not
The split comes down to how long each kind of model takes:
| Output | Usual wait | How you call it |
|---|---|---|
| Chat, code | seconds, streamed | one request, one response |
| Image | seconds up to a minute | task, or one request with ?wait= |
| Video | one to several minutes | task, then poll or webhook |
| Music, audio | tens of seconds to minutes | task, then poll |
Chat keeps the OpenAI and Anthropic shapes because almost every library and tool already speaks them. Media jobs are tasks because a 10-second clip can take minutes to render, and an HTTP connection held open that long tends to die on proxies, serverless platforms and phone networks.
Text models with the OpenAI SDK
Point the official SDK at Kadraris and leave everything else alone.
from openai import OpenAI
client = OpenAI(
base_url="https://kadraris.com/v1",
api_key="sk-kd-...",
)
reply = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{
"role": "user",
"content": "Suggest a name for a linen tote bag.",
}],
)
print(reply.choices[0].message.content)Replace claude-sonnet-5 with gpt-6-sol, gemini-3.6-flash or deepseek-v4-flash and nothing else needs to change. Streaming and fallbacks between models are covered in Use the OpenAI SDK with Claude, Gemini, DeepSeek and GPT.
Media models share one request body
Every image, video and audio model takes a model id plus an input object. When two models mean the same thing, the field has the same name.
- Images:
prompt,negative_prompt,aspect_ratio(1:1,16:9,9:16,4:3,3:4),num_imagesfrom 1 to 4,image_urlsfor edits and references,seed. - Video:
prompt,durationin seconds,resolution(480p,720por1080p, depending on the model),aspect_ratio,image_urlas the first frame,generate_audio. - Music and audio:
prompt,duration,lyrics,instrumental.
Options unique to one model are listed in its schema at GET /v1/models/{id}. Read it before relying on a field.
Send a video job
curl -X POST https://kadraris.com/v1/generate \
-H "Authorization: Bearer $KADRARIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5",
"input": {
"prompt": "Mountain hut in fog, drone glides forward",
"duration": 5,
"resolution": "720p",
"aspect_ratio": "16:9"
}
}'You get a 202 straight away:
{
"id": "task_8f2k...",
"object": "task",
"status": "queued",
"model": "seedance-2.5",
"estimated_cost": { "amount": "X.XX", "currency": "USD" },
"poll_url": "https://kadraris.com/v1/tasks/task_8f2k..."
}The estimated_cost field is worked out before the job reaches the model. That amount is put on hold on your balance while the task runs.
Poll until it finishes
import time
import requests
API = "https://kadraris.com/v1"
HEADERS = {"Authorization": "Bearer sk-kd-..."}
def wait_for(task_id, every=5, timeout=900):
stop_at = time.time() + timeout
while time.time() < stop_at:
task = requests.get(
f"{API}/tasks/{task_id}",
headers=HEADERS, timeout=30,
).json()
if task["status"] in ("succeeded", "failed"):
return task
time.sleep(every)
raise TimeoutError(task_id)
task = wait_for("task_8f2k...")
if task["status"] == "succeeded":
for item in task["output"]:
print(item["type"], item["url"])
else:
print("failed:", task["error"])What a finished task contains:
{
"id": "task_...",
"status": "succeeded",
"model": "seedance-2.5",
"output": [{ "type": "video", "url": "https://...",
"duration": 5, "width": 1280, "height": 720 }],
"cost": { "amount": "X.XX", "currency": "USD" },
"error": null
}In production, a webhook_url beats polling. Signature checks and retries are in AI video pipeline with async tasks and webhooks.
Skip polling for images
An image is usually ready in seconds. Add ?wait=60 (the ceiling is 120) and the server keeps the connection open until the task is done, then answers with the finished task.
r = requests.post(
f"{API}/generate?wait=60",
headers=HEADERS,
json={
"model": "nano-banana-pro",
"input": {
"prompt": "Flat poster, the word HARBOR in heavy serif",
"aspect_ratio": "3:4",
},
},
timeout=130,
)
task = r.json()
print(task["status"], task.get("output"))Look at status anyway. If it is not succeeded yet, poll by id as above.
A song through the same door
const res = await fetch("https://kadraris.com/v1/generate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KADRARIS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "mureka-v9",
input: {
prompt: "Mellow lo-fi beat, Rhodes keys, tape hiss, 80 bpm",
lyrics: "[Verse]\nRain on the window glass\n[Chorus]\nLet the evening pass",
},
}),
});
const task = await res.json();
console.log(task.id, task.estimated_cost);The task object, the key and the polling function are the same ones the video used.
Errors and failed jobs
Once you push real volume, some jobs will fail. A content filter fires, an upstream provider times out, a render hangs.
On Kadraris a job that ends as failed costs nothing: the hold is released in full and cost reads 0. You are billed for results only.
Problems at submission time come back as { "error": { "type": "...", "message": "..." } } with a predictable status code:
| Code | Cause | Sensible reaction |
|---|---|---|
| 401 | Key missing or wrong | Stop and fix config |
| 402 | Not enough balance | Alert, top up, retry |
| 404 | No such model id | Check /v1/models |
| 422 | Bad parameter | Log error.message, fix input |
| 429 | Rate limited | Back off, retry |
| 502 | Provider error | Retry with backoff |
Full reference: errors.
Prices and balance, read from code
The catalog needs no key, so a script can check prices before it spends anything:
curl https://kadraris.com/v1/models
curl https://kadraris.com/v1/models/seedance-2.5Balance does need your key:
curl https://kadraris.com/v1/balance \
-H "Authorization: Bearer $KADRARIS_API_KEY"Some current rates: Seedance 2.5 at from $0.261 per second, GPT Image 2.5 at $0.05 per image, Claude Sonnet 5 at $3.24 / $16.20 per 1M tokens. Every model is listed on the pricing page. The smallest top-up is $10, with quick amounts of $10, $25, $50 and $100.
What one key changes day to day
Separate vendor SDKs are each fine. The pain shows up when a project needs several of them: different auth, different retry rules, and a different card on file that can expire in each one. With a single API:
- One key (
sk-kd-...) runs Claude Sonnet 5, GPT Image 2.5, Seedance 2.5 and Mureka V9. - One prepaid balance, no subscription, no minimum commitment, and it never expires.
- One way to price things: per image, per second of video, per 1M tokens, all public before you call.
- One error shape, as shown above.
Before you build on it
- Store your files. Output links stay valid for a limited time, so copy results to your own storage once a task succeeds. Studio keeps a generation history on your account; with the API, storage is your job.
- Privacy. Prompts and outputs are not used to train models.
- Trust the schema. Video models differ in the durations and resolutions they accept. A value outside the schema returns 422 before any charge.
- No code needed if you prefer a browser. The same models and the same balance are in Studio.
The quickstart gets you from sign-up to a first video in about five minutes.
Questions
Do I need a separate SDK for video and music?
No. Media generation is plain HTTP: one POST to /v1/generate and one GET to /v1/tasks/{id}. Any HTTP client works, in any language. Only the chat side benefits from the OpenAI or Anthropic SDKs.
How do I switch my chat code from OpenAI to Kadraris?
Set base_url="https://kadraris.com/v1" and use your sk-kd-... key. The model id becomes whatever you pick from the catalog. Streaming works on /v1/chat/completions, and tools built for Anthropic can use /v1/messages.
Where do I see the cost before a video starts?
In the 202 response, under estimated_cost. Per-unit prices are also public in /v1/models and on the pricing page, so you can check before sending anything.
What if my balance runs out mid-batch?
New submissions return 402 until you top up. Tasks that were already accepted had their cost held when they were queued, so they finish normally.


























