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:
| Provider | Example model | Notes |
|---|---|---|
| OpenAI | gpt-image-2 | /v1/images/generations batch endpoint. Inline base64. |
| Google Gemini | gemini-2.5-flash-image | Gemini image models via :batchGenerateContent. Inline base64. |
| xAI | grok-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:
| Field | Notes |
|---|---|
prompt | The text describing the image to generate. |
customId | Correlates the result. Auto-generated as request-<index> if omitted; must be unique within a batch. |
n | Number of images to generate. Defaults to 1. |
size | Image size as "{width}x{height}" (e.g. OpenAI image models, "1024x1024"). |
aspectRatio | Aspect ratio as "{width}:{height}" (e.g. Gemini image models, "16:9"). |
providerOptions | Provider-specific options (e.g. { openai: { quality: "high" } }). See below. |
seed | Seed 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.