Zadania
Każde wywołanie POST /v1/generate tworzy zadanie. GET /v1/tasks/{id} zwraca jego bieżący stan, a po zakończeniu także pliki wynikowe i ostateczny koszt.
Zapytanie
curl https://jantaris.com/v1/tasks/task_8f2k... \
-H "Authorization: Bearer $JANTARIS_API_KEY"Pole poll_url z odpowiedzi /v1/generate zawiera już pełny adres.
Odpowiedź
{
"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 | Opis |
|---|---|---|
id | string | Id zadania |
status | string | queued, running, succeeded albo failed |
model | string | Id modelu |
output | array | Pliki wynikowe, wypełnione przy succeeded |
cost | object | {amount, currency} pobrane za zadanie. 0 przy failed |
error | object lub null | {type, message} przy failed |
created_at | string | Znacznik czasu ISO 8601 |
completed_at | string lub null | Znacznik czasu ISO 8601, ustawiany po zakończeniu |
Statusy
| Status | Znaczenie | Końcowy |
|---|---|---|
queued | Przyjęte, czeka na start. Środki są zablokowane | nie |
running | Generowanie trwa | nie |
succeeded | Wynik gotowy, cost pobrany | tak |
failed | Generowanie się nie udało, blokada zwolniona, płacisz 0 | tak |
Elementy output
| Pole | Występuje przy | Opis |
|---|---|---|
type | wszystkich | image, video albo audio |
url | wszystkich | Tymczasowy link do pobrania |
width, height | obraz, wideo | Piksele |
duration | wideo, audio | Sekundy |
Zadanie nieudane:
{
"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"
}Odpytywanie
Sprawdzaj zadanie co kilka sekund, aż status będzie końcowy. Obrazom zwykle wystarczy kilka sekund, wideo potrafi trwać kilka minut.
import os, time, requests
API = "https://jantaris.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['JANTARIS_API_KEY']}"}
def wait_for_task(task_id: str, timeout: int = 900) -> dict:
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) # wydłużaj odstęp do 15 s
raise TimeoutError(f"zadanie {task_id} trwa dłużej niż {timeout} s")
task = requests.post(
f"{API}/generate",
headers=HEADERS,
json={"model": "kling-3.0", "input": {"prompt": "A paper boat drifting down a rainy street", "duration": 5}},
timeout=30,
).json()
done = wait_for_task(task["id"])
print(done["status"], done["cost"], done["output"] or done["error"])const API = "https://jantaris.com/v1";
const headers = { Authorization: `Bearer ${process.env.JANTARIS_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 task = await (await fetch(`${API}/tasks/${id}`, { headers })).json();
if (task.status === "succeeded" || task.status === "failed") return task;
await sleep(delay);
delay = Math.min(delay * 1.5, 15000);
}
throw new Error(`zadanie ${id} nadal trwa`);
}
const started = await (await fetch(`${API}/generate`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ model: "kling-3.0", input: { prompt: "A paper boat drifting down a rainy street", duration: 5 } }),
})).json();
const done = await waitForTask(started.id);
console.log(done.status, done.cost, done.output ?? done.error);Przy długich zadaniach albo wielu zadaniach naraz webhooki zastępują pętlę odpytywania.
Pobieranie wyników
Linki w output są tymczasowe. Pobierz pliki, które chcesz zachować, i trzymaj je u siebie.
import pathlib
def download_outputs(task: dict, folder: str = "out") -> list[pathlib.Path]:
ext = {"image": "png", "video": "mp4", "audio": "mp3"}
paths = []
pathlib.Path(folder).mkdir(exist_ok=True)
for i, item in enumerate(task["output"]):
path = pathlib.Path(folder) / 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);
}
}Rozszerzenie pliku w przykładzie jest zgadywane po typie. Jeśli potrzebujesz dokładnego formatu, odczytaj nagłówek Content-Type pobieranego pliku.
Błędy
| Status | error.type | Kiedy |
|---|---|---|
| 401 | authentication_error | Zły albo brakujący klucz |
| 404 | not_found | Zadanie nie istnieje albo należy do innego konta |
Szczegóły: Błędy.









