Zadania
Zadanie powstaje przy każdym wywołaniu POST /v1/generate. Jego stan odczytujesz przez GET /v1/tasks/{id} - w trakcie zobaczysz status, a po zakończeniu listę plików i końcowy koszt.
Wywołanie
curl https://kadraris.com/v1/tasks/task_8f2k... \
-H "Authorization: Bearer $KADRARIS_API_KEY"Nie musisz składać adresu sam - odpowiedź z /v1/generate ma gotowy link w polu poll_url.
Co zwraca serwer
{
"id": "task_8f2k...",
"status": "succeeded",
"model": "seedance-2.5",
"output": [
{ "type": "video", "url": "https://...",
"duration": 5, "width": 1280, "height": 720 }
],
"cost": { "amount": "3.10", "currency": "PLN" },
"error": null,
"created_at": "2026-09-27T10:15:02Z",
"completed_at": "2026-09-27T10:16:41Z"
}| Pole | Typ | Zawartość |
|---|---|---|
id | string | Identyfikator zadania |
status | string | queued, running, succeeded lub failed |
model | string | Identyfikator modelu |
output | array | Pliki z wynikiem; puste, dopóki nie ma succeeded |
cost | object | {amount, currency} - ile pobraliśmy; 0 przy failed |
error | object lub null | {type, message}, gdy zadanie się nie udało |
created_at | string | Czas utworzenia w ISO 8601 |
completed_at | string lub null | Czas zakończenia w ISO 8601 |
Cykl życia zadania
| Status | Co oznacza | Stan końcowy |
|---|---|---|
queued | Zadanie przyjęte, czeka w kolejce; środki już zamrożone | nie |
running | Model pracuje | nie |
succeeded | Wynik gotowy, pobrana kwota z cost | tak |
failed | Model nie dał rady; blokada zdjęta, koszt 0 | tak |
Pola w output
| Pole | Dla | Zawartość |
|---|---|---|
type | każdego pliku | image, video lub audio |
url | każdego pliku | Link do pobrania, ważny ograniczony czas |
width, height | obrazu i wideo | Wymiary w pikselach |
duration | wideo i audio | Długość w sekundach |
Tak wygląda zadanie, które się nie powiodło:
{
"id": "task_3xq1...",
"status": "failed",
"model": "kling-3.0",
"output": [],
"cost": { "amount": "0.00", "currency": "PLN" },
"error": { "type": "upstream_error",
"message": "The model could not complete this request." },
"created_at": "2026-09-27T10:20:00Z",
"completed_at": "2026-09-27T10:21:12Z"
}Pętla odpytywania
Pytaj o zadanie co kilka sekund i kończ, gdy status jest końcowy. Obraz zwykle zamyka się w kilku sekundach, wideo w kilku minutach, więc odstęp między pytaniami wydłużaj stopniowo.
import os, time, requests
API = "https://kadraris.com/v1"
HEADERS = {"Authorization":
f"Bearer {os.environ['KADRARIS_API_KEY']}"}
def wait_for_task(task_id, timeout=900):
delay, deadline = 2, time.time() + timeout
while time.time() < deadline:
task = requests.get(f"{API}/tasks/{task_id}",
headers=HEADERS, timeout=30).json()
if task["status"] in ("succeeded", "failed"):
return task
time.sleep(delay)
delay = min(delay * 1.5, 15) # najwyżej co 15 s
raise TimeoutError(f"{task_id}: brak wyniku po {timeout} s")
task = requests.post(
f"{API}/generate",
headers=HEADERS,
json={"model": "kling-3.0",
"input": {"prompt": "A lighthouse beam sweeping fog",
"duration": 5}},
timeout=30,
).json()
done = wait_for_task(task["id"])
print(done["status"], done["cost"], done["output"] or done["error"])const API = "https://kadraris.com/v1";
const headers = {
Authorization: `Bearer ${process.env.KADRARIS_API_KEY}`,
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function waitForTask(id, timeoutMs = 900_000) {
let delay = 2000;
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(`${API}/tasks/${id}`, { headers });
const task = await res.json();
if (task.status === "succeeded" || task.status === "failed") {
return task;
}
await sleep(delay);
delay = Math.min(delay * 1.5, 15000);
}
throw new Error(`${id}: wciąż bez wyniku`);
}
const res = await fetch(`${API}/generate`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
model: "kling-3.0",
input: { prompt: "A lighthouse beam sweeping fog", duration: 5 },
}),
});
const started = await res.json();
const done = await waitForTask(started.id);
console.log(done.status, done.cost, done.output ?? done.error);Kiedy zadań jest dużo albo trwają długo, wygodniej przejść na webhooki i w ogóle nie odpytywać.
Zapis plików u siebie
Adresy z output po czasie wygasają. Ściągnij pliki, które mają przetrwać, i przechowuj je we własnym magazynie.
import pathlib
def download_outputs(task, folder="out"):
ext = {"image": "png", "video": "mp4", "audio": "mp3"}
out = pathlib.Path(folder)
out.mkdir(exist_ok=True)
paths = []
for i, item in enumerate(task["output"]):
path = out / f"{task['id']}_{i}.{ext[item['type']]}"
with requests.get(item["url"], stream=True,
timeout=120) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
paths.append(path)
return pathsimport { writeFile, mkdir } from "node:fs/promises";
async function downloadOutputs(task, folder = "out") {
await mkdir(folder, { recursive: true });
for (const [i, item] of task.output.entries()) {
const res = await fetch(item.url);
const buf = Buffer.from(await res.arrayBuffer());
await writeFile(`${folder}/${task.id}_${i}`, buf);
}
}W przykładzie rozszerzenie wynika tylko z typu pliku. Jeżeli zależy ci na dokładnym formacie, sprawdź nagłówek Content-Type w odpowiedzi przy pobieraniu.
Błędy tego endpointu
| Kod | error.type | Sytuacja |
|---|---|---|
| 401 | authentication_error | Klucz błędny albo go brak |
| 404 | not_found | Zadania nie ma albo należy do innego konta |
Wszystkie kody: Błędy.























