Image

Retrieve a job

Image work is asynchronous. Submit a job, then poll this endpoint until it finishes.

GET/v1/images/generations/{id}
curl https://api.xkiro.com/v1/images/generations/f32a1796-15f4-43ba-8098-22d7dc2f66c1 \
  -H "Authorization: Bearer $XKIRO_API_KEY"
200 OK — finished
{
  "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#

FieldTypeDescription
processingstringStill running. Keep polling.
succeededstringFinished. data[].url holds CDN links.
failedstringSomething went wrong; error explains what. Not billed, and does not count against your daily allowance.
blockedstringThe 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#

GET/v1/images/generations

Recent jobs, newest first — enough to rebuild a gallery after a page reload without any client-side storage.

200 OK
{
  "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.