Poll asynchronous generation jobs and track model training readiness.
Generation Jobs
Generation requests are asynchronous. The trigger request returns quickly, then the backend continues processing the job.
The common pattern is:
- Start work with
POST /images/generation,POST /images/upscaling, orPOST /videos/generation. - Save the returned
jobId. - Poll
GET /jobs/{id}until the status leaves the running state. - Read generated
images, generatedvideos, orerrorfrom the job payload.
Training note
The OpenAPI schema documents GET /models/{modelID}/train as returning a success string, not a
jobId. For training, track readiness by polling the model with GET /models/{modelID} until
status becomes ready. Training usually takes 20 to 25 minutes, so poll about every 5 minutes
instead of every second.
Job response
GET /jobs/{id} returns a result object with job metadata and any completed outputs.
| Field | Description |
|---|---|
jobId | Job identifier returned by the trigger request. |
status | Current backend state. |
images | Generated image records when the job produced images. |
videos | Generated video records when the job produced videos. |
error | Failure reason when the job did not complete. |
modelId | Related model ID when the job is model-scoped. |
createdAt | Job creation timestamp. |
Poll a job
Use a short delay between polling attempts. The current job status values are created,
queued, running, success, and failed.
const RUNNING_STATUSES = new Set(['created', 'queued', 'running'])
const SUCCESS_STATUS = 'success'
async function waitForJob(jobId, intervalMs = 5000) {
while (true) {
const response = await fetch(`${BASE_URL}/jobs/${jobId}`, {
headers,
})
if (!response.ok) {
throw new Error(await response.text())
}
const body = await response.json()
const job = body.result
const status = String(job.status ?? '').toLowerCase()
if (!RUNNING_STATUSES.has(status)) {
return job
}
await new Promise((resolve) => setTimeout(resolve, intervalMs))
}
}
const job = await waitForJob('<JOB_ID>')
const status = String(job.status ?? '').toLowerCase()
if (status !== SUCCESS_STATUS) {
throw new Error(job.error ?? `Job failed: ${job.status}`)
}import time
RUNNING_STATUSES = {"created", "queued", "running"}
SUCCESS_STATUS = "success"
def wait_for_job(job_id: str, interval_seconds: int = 5):
while True:
response = requests.get(f"{BASE_URL}/jobs/{job_id}", headers=headers)
response.raise_for_status()
job = response.json()["result"]
status = job.get("status", "").lower()
if status not in RUNNING_STATUSES:
return job
time.sleep(interval_seconds)
job = wait_for_job("<JOB_ID>")
if job.get("status", "").lower() != SUCCESS_STATUS:
raise RuntimeError(job.get("error") or f"Job failed: {job.get('status')}")Read outputs
Image generation and upscaling jobs return image records in images.
for (const image of job.images ?? []) {
console.log(image.id, image.url)
}for image in job.get("images", []):
print(image["id"], image["url"])Video generation jobs return video records in videos.
for (const video of job.videos ?? []) {
console.log(video.id, video.url)
}for video in job.get("videos", []):
print(video["id"], video["url"])