Images

Generate thousands of images in one batch with batch.images — the same job handle, base64 or hosted images per request.

Submit an image batch

batch.images() mirrors batch(): pass an image model and a list of prompts, and it returns the same BatchJob handle immediately. Each request produces one or more images, correlated by customId.

import { batch } from "batchwork";
import { openai } from "@ai-sdk/openai";

const job = await batch.images({
  model: openai.image("gpt-image-2"),
  requests: [
    { customId: "a", prompt: "A red bicycle against a brick wall." },
    { customId: "b", prompt: "A watercolor painting of a sleeping cat." },
  ],
});

const results = await job.wait().then(() => job.collect());
for (const r of results) {
  for (const image of r.images ?? []) {
    // Inline base64 (`image.data` + `image.mediaType`), or a hosted
    // `image.url` for providers that return one (e.g. xAI batch).
    console.log(r.customId, image.mediaType ?? image.url);
  }
}

Everything on the job handle works unchanged — wait(), poll(), results(), collect(), and cancel() — as does rehydration with getBatch / getBatchResults / cancelBatch.

You can also pass a "provider/model" string (e.g. "openai/gpt-image-2"), though the model object is recommended.

Supported providers

Batch image generation is available for the providers whose batch API accepts an image-generation endpoint:

ProviderExample modelNotes
OpenAIgpt-image-2/v1/images/generations batch endpoint. Inline base64.
Google Geminigemini-2.5-flash-imageGemini image models via :batchGenerateContent. Inline base64.
xAIgrok-imagine-image-quality/v1/images/generations. Signed URLs that expire ~1h.

Anthropic, Groq, and Mistral expose no image model; Together AI's batch API is chat/audio only; and Google's Imagen models (:predict) aren't batch-supported. Calling batch.images() with any of them throws UnsupportedProviderError before any network request.

This is generation only — image editing (input images, masks) and video aren't supported by the batch APIs.

Request shape

Each request is a text prompt plus an optional customId and a few generation knobs:

FieldNotes
promptThe text describing the image to generate.
customIdCorrelates the result. Auto-generated as request-<index> if omitted; must be unique within a batch.
nNumber of images to generate. Defaults to 1.
sizeImage size as "{width}x{height}" (e.g. OpenAI image models, "1024x1024").
aspectRatioAspect ratio as "{width}:{height}" (e.g. Gemini image models, "16:9").
providerOptionsProvider-specific options (e.g. { openai: { quality: "high" } }). See below.
seedSeed for deterministic generation, where the provider supports it.

limits and metadata work exactly as in batch().

Sizing, count, and provider options

size and aspectRatio are two ways providers express dimensions — OpenAI image models take a size like "1024x1024", while Gemini image models take an aspectRatio like "16:9". Pass whichever your model expects. Use n to request more than one image per prompt, and providerOptions (keyed by provider) for anything model-specific, the same shape the AI SDK's generateImage uses:

// OpenAI — square 1024px, two variations, high quality
await batch.images({
  model: openai.image("gpt-image-2"),
  requests: [
    {
      customId: "a",
      prompt: "A red bicycle against a brick wall.",
      size: "1024x1024",
      n: 2,
      providerOptions: { openai: { quality: "high" } },
    },
  ],
});

// Google Gemini — widescreen aspect ratio
await batch.images({
  model: google.image("gemini-2.5-flash-image"),
  requests: [
    { customId: "a", prompt: "A sunlit forest path.", aspectRatio: "16:9" },
  ],
});

defaults are merged into every request (request-level values win), exactly as in batch() — handy for applying one size or providerOptions across the whole batch.

Results

Images reuse the normalized BatchResult. Generated images land on result.images, an array of { data?, mediaType?, url? }:

import { writeFile } from "node:fs/promises";

for await (const result of job.results()) {
  if (result.status === "succeeded") {
    for (const [i, image] of (result.images ?? []).entries()) {
      if (image.data) {
        // Inline base64 (OpenAI, Google) — no `data:` prefix.
        await writeFile(
          `${result.customId}-${i}.png`,
          Buffer.from(image.data, "base64")
        );
      } else if (image.url) {
        // Hosted URL (xAI batch) — fetch promptly; signed links expire ~1h.
        await fetch(image.url);
      }
    }
  } else if (result.status === "errored") {
    console.error(result.customId, result.error?.message);
  }
}

OpenAI and Google return inline base64 on image.data (with image.mediaType, e.g. "image/png"); xAI batch returns signed image.urls that expire ~1h after completion, so download them promptly. text and embedding are undefined for image batches.

usage is normalized to { inputTokens, totalTokens } where the provider reports it, billed at the batch rate (~50% off).

How it's built

Like batch(), batch.images() derives each provider request body by running the AI SDK's generateImage() through a capturing fetch that records the serialized body and aborts before any network call. Each request maps to a single image-generation call, correlated by customId.

On this page