DEV Community

Sherwood
Sherwood

Posted on

Bulk-generate images from a list of prompts with one API call (FLUX, no API key)

Generating one image is easy: open any web playground, type a prompt, download the file. Generating two hundred images from inside a pipeline is a different job. The typical cases: product visuals for a catalog where each row of a spreadsheet needs its own picture, illustrations for a batch of blog posts or slides, placeholder art for a game or an app, creative variations for ads where you want four takes per concept.

What you want there is boring: a list of prompts in, a list of image URLs out, the size and the seed for each so you can reproduce it, and a clear signal for the ones that failed. This post compares the usual ways to batch-generate images, then shows how to do it with one API call from curl, Python, JavaScript, n8n and an AI agent.

Why batch image generation gets annoying

Self-hosting FLUX or Stable Diffusion. Full control, no per-image fee, any LoRA you like. In exchange, you need a GPU with enough VRAM, a queue, a way to serve the files, and someone to keep it running. It pays off at high, steady volume, and not before.

Calling an inference API (fal, Replicate, Together) directly. Close to the metal and fast, but now your pipeline owns an API key, a retry policy, per-image polling, and a place to store the output files. Multiply that by every workflow that needs images.

The big-model APIs (OpenAI, Google). Great models, but you still sign up for a key, and the bill is per account, not per run. For a no-code tool or an agent, "go create an account and wire a secret" is where the automation dies.

Looping over a single-image endpoint. Most hosted generators take one prompt per call. Two hundred prompts means two hundred calls, your own concurrency control, and your own bookkeeping of which output belongs to which prompt.

The pattern that removes all of this: one call that takes the whole list, runs the model, stores the files, and returns one record per image with its URL. That is what an Apify Actor does, and the rest of this post uses one I maintain: Bulk AI Image Generator (FLUX.2 Klein 9B by default, FLUX Schnell in fast mode).

A batch image generator behind one API call

Step 1: get a token

Create a free Apify account and copy your API token from Settings → Integrations. There is no image-model key to manage: the model runs inside the Actor.

Step 2: the input

The input is a JSON object. Everything is optional; the default input generates two sample images so you can see the output shape before sending your own prompts.

{
  "prompts": [
    "A ceramic coffee mug on a wooden table, soft morning light, product photography",
    "Minimal flat illustration of a paper plane flying over pastel mountains"
  ],
  "imageSize": "square_hd",
  "quality": "standard",
  "imagesPerPrompt": 1,
  "outputFormat": "png"
}
Enter fullscreen mode Exit fullscreen mode
  • prompts — the list. A single string in prompt, or the aliases texts and queries, work too; everything is merged and deduplicated.
  • imageSize — square_hd (1024×1024), landscape_4_3, landscape_16_9, portrait_4_3, portrait_16_9, square; or exact width and height in pixels (256 to 2048).
  • quality — standard is FLUX.2 Klein 9B; fast is FLUX Schnell, cheaper and quicker, for drafts and placeholders.
  • imagesPerPrompt — up to 4 variations per prompt, each with its own seed.
  • seed — set it to reproduce a run; every result carries the seed it used either way.
  • maxPrompts — hard cap per run (default 100, up to 1,000), so a malformed input cannot surprise you.

Step 3: call it with curl

curl -s -X POST \
  "https://api.apify.com/v2/actors/sherwood~image-generator-batch/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompts": ["A ceramic coffee mug on a wooden table, product photography"]}'
Enter fullscreen mode Exit fullscreen mode

The call blocks until the run finishes and returns the dataset items directly.

Step 4: Python

from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("sherwood/image-generator-batch").call(run_input={
    "prompts": [
        "A ceramic coffee mug on a wooden table, product photography",
        "Minimal flat illustration of a paper plane over pastel mountains",
    ],
    "imageSize": "landscape_16_9",
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["status"] == "ok":
        print(item["prompt"][:40], "->", item["outputUrl"])
    else:
        print(item["prompt"][:40], "FAILED:", item["error"])
Enter fullscreen mode Exit fullscreen mode

Step 5: JavaScript

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('sherwood/image-generator-batch').call({
    prompts: ['A ceramic coffee mug on a wooden table, product photography'],
    imagesPerPrompt: 4,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items) {
    console.log(item.status, item.variant, item.outputUrl ?? item.error);
}
Enter fullscreen mode Exit fullscreen mode

What comes back

One dataset item per generated image:

{
  "index": 0,
  "variant": 0,
  "prompt": "A ceramic coffee mug on a wooden table, product photography",
  "outputUrl": "https://api.apify.com/v2/key-value-stores/abc123/records/image-0000-0.png",
  "width": 1024,
  "height": 1024,
  "seed": 1545164682,
  "model": "fal-ai/flux-2/klein/9b",
  "format": "png",
  "durationMs": 1666,
  "status": "ok",
  "error": null
}
Enter fullscreen mode Exit fullscreen mode

index is the position of the prompt in your input and variant the variation number, so matching outputs back to your own rows is a sort, not a guess. seed makes any image reproducible: send the same prompt, size and seed, get the same image.

Failed prompts come back as items, and are not charged

A prompt the safety checker refuses, or an upstream error, produces an item with "status": "error" and the reason in error. It costs nothing, and nothing is silently dropped: you always get as many answers as questions. The safety checker is always on; this Actor is not a way around model content rules.

Using it from n8n

The wiring is three nodes around the official Apify node (install "Apify" from the community nodes if you don't have it). Read a prompt column from a Google Sheet; a Code node collects the non-empty prompts into one list; the Apify node runs sherwood~image-generator-batch with that list as prompts; a last Code node sorts the results by index to put each image next to its prompt, and a Google Sheets node writes image_url, width, height, seed and status back to each row. Because the whole sheet goes in one Actor call, the workflow runs once per batch, not once per row.

Using it from an AI agent (MCP)

Add the Apify MCP server to your agent and it can call the Actor like any tool: the input above, as arguments; the dataset items, as the result. Nothing in the input is required, so a one-line instruction ("generate an image for each of these ten product names") maps cleanly onto one call.

The Actor is also eligible for agentic payments (x402, Skyfire, MPP): an agent can run it and pay per request without an Apify account at all.

Pricing at the time of writing

  • Image generated — $0.01 per image delivered, 1024×1024 included. Larger sizes add $0.01 per started extra megapixel.
  • Failed or safety-flagged prompts: free.
  • No subscription, no minimum. Apify's free plan is enough to try it.

Two hundred catalog images cost about $2. Set a maximum cost per run in Apify and the Actor stops cleanly at the limit, reporting what remains instead of failing.

Limits

  • Output files live in the run's key-value store and follow your Apify account's data retention; copy them out if you need them long-term.
  • imagesPerPrompt tops out at 4, sizes at 2048 px per side.
  • The model is FLUX.2 Klein 9B (or Schnell). If your brief needs a specific aesthetic from another model, this is not that.

When I wouldn't use it

For one image now and then, a free web playground is simpler. For a style that depends on a particular model or a custom LoRA, self-hosting or a direct inference API gives you the control this Actor deliberately hides. And at very high, steady volume — tens of thousands of images a month, every month — a rented GPU beats any per-image price. Everywhere between those: a list in, a list out, one call.

Top comments (0)