Image
Retrieve a job
Image work is asynchronous. Submit a job, then poll this endpoint until it finishes.
/v1/images/generations/{id}curl https://api.xkiro.com/v1/images/generations/f32a1796-15f4-43ba-8098-22d7dc2f66c1 \
-H "Authorization: Bearer $XKIRO_API_KEY"{
"id": "f32a1796-15f4-43ba-8098-22d7dc2f66c1",
"object": "image.generation.job",
"status": "succeeded",
"created": 1785734400,
"model": "gpt-image",
"prompt": "A lighthouse on a rocky shore at dawn, long exposure",
"aspect_ratio": "1:1",
"style": null,
"data": [
{ "url": "https://cdn.xkiro.com/images/01JQZ8K3M7N2P4R6S8T0V2W4X6-0.png" }
]
}Status values#
| Field | Type | Description |
|---|---|---|
processing | string | Still running. Keep polling. |
succeeded | string | Finished. data[].url holds CDN links. |
failed | string | Something went wrong; error explains what. Not billed, and does not count against your daily allowance. |
blocked | string | The provider refused the prompt on content grounds. Reword it — retrying the same prompt will not help. This does count against the allowance, because the attempt consumed upstream capacity. |
A finished job never carries a stale error
When a job succeeds, any earlier errorCode is cleared. A record that is both succeeded and carrying an error would make an SDK branching on if (res.error) throw away a delivered image.
Polling correctly#
async function waitForImages(jobId: string) {
// Always bound the wait. A job stuck in "processing" would otherwise poll
// forever and hold the caller open.
const deadline = Date.now() + 5 * 60_000;
let waitMs = 2_000;
while (Date.now() < deadline) {
await new Promise((r) => setTimeout(r, waitMs));
// Back off gently: nothing changes faster than a couple of seconds, and
// aggressive polling counts against your rate limit.
waitMs = Math.min(waitMs * 1.5, 10_000);
const job = await fetch(
`https://api.xkiro.com/v1/images/generations/${jobId}`,
{ headers: { Authorization: `Bearer ${process.env.XKIRO_API_KEY}` } },
).then((r) => r.json());
if (job.status === "succeeded") return job.data.map((d: { url: string }) => d.url);
if (job.status === "failed" || job.status === "blocked") {
throw new Error(job.error?.message ?? job.status);
}
}
throw new Error("Image generation timed out");
}Listing your jobs#
/v1/images/generationsRecent jobs, newest first — enough to rebuild a gallery after a page reload without any client-side storage.
{
"object": "list",
"data": [ /* job objects, newest first */ ],
"next_before": "2026-08-03T15:12:01.094Z"
}Pagination
Pass the previous page's next_before as the before query parameter. An empty next_before means there is nothing older.
curl "https://api.xkiro.com/v1/images/generations?before=2026-08-03T15:12:01.094Z" \
-H "Authorization: Bearer $XKIRO_API_KEY"The job carries its own prompt
prompt, aspect_ratio and style come back with every job, so a gallery can show how each image was made without you storing anything.
To create jobs, see Generate images and Edit images.
