PL Log inCreate account
Launching in October 2026. Accounts, model catalog, calculator and docs are live. Studio is available in preview; generation is awaiting activation.

Generate

All documentation pages

POST /v1/generate starts an image, video or audio generation. The request returns a task right away (202). You get the result by polling the task, by passing ?wait=N, or through a webhook.

Request

http
POST https://jantaris.com/v1/generate
Authorization: Bearer sk-jt-...
Content-Type: application/json
FieldTypeRequiredDescription
modelstringyesMedia model id, for example seedance-2.5, gpt-image-2.5, mureka-v9
inputobjectyesModel parameters, validated against the model's input_schema
webhook_urlstringnoURL that receives a POST when the task succeeds or fails
Query parameterTypeDescription
waitinteger, 1-120Seconds the server waits for the task to finish before responding

Response

Without wait, or when the task did not finish in time, you get 202 Accepted:

json
{
  "id": "task_8f2k...",
  "object": "task",
  "status": "queued",
  "model": "seedance-2.5",
  "estimated_cost": { "amount": "2.9351", "currency": "USD" },
  "poll_url": "https://jantaris.com/v1/tasks/task_8f2k..."
}

The amount in this example is for the request from Video with a webhook (Seedance 2.5, 5 s, 720p) and comes from the price list of October 2, 2026. The API always returns the current amount in estimated_cost. To work it out yourself:

  • video billed per second: the per-second price at the chosen resolution times duration; per-second prices for each resolution are in pricing.variants from GET /v1/models/{id};
  • example: the 720p per-second price times 5 s.

With ?wait=N, if the task finishes within N seconds you get 200 OK with the finished task, the same object as GET /v1/tasks/{id}. A finished task is either succeeded or failed, so always check status.

Cost and holds

  1. Before the task starts, estimated_cost is held from your balance. If available balance is lower than the estimate, the request fails with 402 insufficient_balance and nothing starts.
  2. On succeeded, exactly cost is charged and the hold is settled.
  3. On failed, the hold is released in full. You pay 0.

Amounts are strings with a currency: USD for global accounts, PLN for Polish accounts. See Billing.

Input parameters

Each model has its own schema. Get it from GET /v1/models/{id} (field input_schema) before sending a request. Unknown fields, wrong types and out-of-range values return 422 invalid_request with a message naming the field.

Common fields by category (a given model may support a subset):

CategoryFields
Imageprompt, negative_prompt, aspect_ratio (1:1, 16:9, 9:16, 4:3, 3:4), num_images (1-4, only on models that list it in GET /v1/models/{id}), image_urls (editing or references), seed
Videoprompt, duration (seconds), resolution (480p, 720p, 1080p depending on the model), aspect_ratio, image_url (first frame), generate_audio
Audio / musicprompt, duration, lyrics, instrumental

Fields you omit take the default from the schema.

Image with wait

Images usually finish within a minute, so ?wait=60 gives you the result in one call.

bash
curl "https://jantaris.com/v1/generate?wait=60" \
  -H "Authorization: Bearer $JANTARIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-2-pro",
    "input": {
      "prompt": "Product photo of a ceramic mug on a concrete table, soft daylight",
      "aspect_ratio": "4:3"
    }
  }'
python
import os, requests

API = "https://jantaris.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['JANTARIS_API_KEY']}"}

r = requests.post(
    f"{API}/generate",
    params={"wait": 60},
    headers=HEADERS,
    json={
        "model": "flux-2-pro",
        "input": {
            "prompt": "Product photo of a ceramic mug on a concrete table, soft daylight",
            "aspect_ratio": "4:3",
        },
    },
    timeout=130,  # longer than wait
)
r.raise_for_status()
task = r.json()

if task["status"] == "succeeded":
    for item in task["output"]:
        print(item["url"], item.get("width"), item.get("height"))
elif task["status"] == "failed":
    print("failed:", task["error"])
else:
    print("still running, poll:", task["poll_url"])
javascript
const res = await fetch("https://jantaris.com/v1/generate?wait=60", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.JANTARIS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "flux-2-pro",
    input: {
      prompt: "Product photo of a ceramic mug on a concrete table, soft daylight",
      aspect_ratio: "4:3",
    },
  }),
});
const task = await res.json();
if (task.status === "succeeded") task.output.forEach((o) => console.log(o.url));
else console.log(task.status, task.poll_url ?? task.error);

Set your HTTP client timeout above the wait value, otherwise your client gives up before the server answers.

Image editing

Pass source images in image_urls if the model supports it (check input_schema):

bash
curl "https://jantaris.com/v1/generate?wait=60" \
  -H "Authorization: Bearer $JANTARIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nano-banana-pro",
    "input": {
      "prompt": "Replace the background with a snowy mountain landscape",
      "image_urls": ["https://example.com/photo.jpg"]
    }
  }'

Video with a webhook

Video takes longer, so start the task and let Jantaris notify you.

bash
curl https://jantaris.com/v1/generate \
  -H "Authorization: Bearer $JANTARIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2.5",
    "input": {
      "prompt": "Slow dolly shot through a neon-lit night market in the rain",
      "duration": 5,
      "resolution": "720p",
      "aspect_ratio": "16:9"
    },
    "webhook_url": "https://your-server.example.com/jantar-hook"
  }'
python
r = requests.post(
    f"{API}/generate",
    headers=HEADERS,
    json={
        "model": "seedance-2.5",
        "input": {
            "prompt": "Slow dolly shot through a neon-lit night market in the rain",
            "duration": 5,
            "resolution": "720p",
            "aspect_ratio": "16:9",
        },
        "webhook_url": "https://your-server.example.com/jantar-hook",
    },
    timeout=30,
)
task = r.json()
print(task["id"], task["estimated_cost"])
javascript
const res = await fetch("https://jantaris.com/v1/generate", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.JANTARIS_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "seedance-2.5",
    input: {
      prompt: "Slow dolly shot through a neon-lit night market in the rain",
      duration: 5,
      resolution: "720p",
      aspect_ratio: "16:9",
    },
    webhook_url: "https://your-server.example.com/jantar-hook",
  }),
});
const task = await res.json();
console.log(task.id, task.estimated_cost);

Most video models are billed per second of output: Seedance 2.5 costs from $0.261 per second, Kling 3.0 from $0.169 per second. Veo 3.1 (Quality) is billed per clip: from $2.53 per video, see billing units. To animate a still image, add image_url (first frame) if the model's schema has it.

Music

bash
curl https://jantaris.com/v1/generate \
  -H "Authorization: Bearer $JANTARIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mureka-v9",
    "input": {
      "prompt": "Lo-fi hip hop, warm vinyl crackle, mellow piano",
      "lyrics": "[Verse]\nCity lights are fading slow\n[Chorus]\nStay a little longer"
    }
  }'

lyrics is required: mark verses and the chorus with [Verse] and [Chorus], and write line breaks as \n in JSON. prompt sets the style. Check the model's input_schema for limits. Price: $0.124 per song.

Errors

Statuserror.typeWhen
401authentication_errorBad or missing key
402insufficient_balanceAvailable balance below estimated_cost
403permission_errorKey spend limit reached
404not_foundModel id does not exist
422invalid_requestinput does not match the schema
429rate_limitOver 60 requests per minute per key (default)
429rate_limit_error25 failed generations on the account in 24 hours, wait time in the Retry-After header

A task that fails after it started is not an HTTP error. It shows up as status: "failed" with an error object, see Tasks. Full list: Errors.