PL Log inOpen Studio
Preview version. Model catalog, prices, calculator and docs are live. Generation goes live at launch.

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).

MethodPathPurpose
POST/v1/chat/completionsChat in OpenAI format, streaming OK
POST/v1/messagesChat in Anthropic format
POST/v1/generateNew image, video or audio job (202)
GET/v1/tasks/{id}Job status and output files
GET/v1/modelsPublic catalog with prices
GET/v1/models/{id}One model and its parameters
GET/v1/balanceWhat 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:

OutputUsual waitHow you call it
Chat, codeseconds, streamedone request, one response
Imageseconds up to a minutetask, or one request with ?wait=
Videoone to several minutestask, then poll or webhook
Music, audiotens of seconds to minutestask, 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.

python
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_images from 1 to 4, image_urls for edits and references, seed.
  • Video: prompt, duration in seconds, resolution (480p, 720p or 1080p, depending on the model), aspect_ratio, image_url as 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

bash
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:

json
{
  "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

python
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:

json
{
  "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.

python
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

javascript
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:

CodeCauseSensible reaction
401Key missing or wrongStop and fix config
402Not enough balanceAlert, top up, retry
404No such model idCheck /v1/models
422Bad parameterLog error.message, fix input
429Rate limitedBack off, retry
502Provider errorRetry 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:

bash
curl https://kadraris.com/v1/models
curl https://kadraris.com/v1/models/seedance-2.5

Balance does need your key:

bash
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.