# AI Image Generator & Bulk Editor – Nano Banana, GPT Image (`gazidev/bulk-ai-image-editor`) Actor

AI image generator and bulk editor with Nano Banana (Gemini 2.5 Flash Image), Nano Banana Pro and GPT Image: text-to-image from prompts, background swap, restyle, product photos and variations from URLs, CSV or datasets. No API key.

- **URL**: https://apify.com/gazidev/bulk-ai-image-editor.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 image generateds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bulk AI Image Editor – Nano Banana & GPT Image, No API Key

**Edit or generate images in bulk** with **Nano Banana** (Google Gemini 2.5 Flash Image), **Nano Banana 2 / Pro** (Gemini 3 image models) and **GPT Image** (OpenAI). Swap product-photo backgrounds, restyle photos, create lifestyle scenes, make variations or generate new images from text, for **hundreds of images at once** from a list of URLs, a **CSV / Google Sheet** or an **Apify dataset**.

Each result is saved as a PNG (or JPEG/WebP) with a **public link**, and every image gets **one dataset row**: input image, prompt, result URL, size and model cost. You can export it as CSV/Excel or send it to Google Sheets.

**No API key needed.** The model is called through [Apify's own OpenRouter Actor](https://apify.com/apify/openrouter) with your run's Apify token.

**$0.003 per saved image + model cost** (Nano Banana is about $0.039 per image). Failed images and refusals are free.

### How you pay (two parts, both on your Apify bill)

1. **This Actor's fee: $0.003 per image saved** (plus a $0.00005 start fee). Images that fail to download, model errors and safety refusals are not charged.
2. **Model cost** for each call, billed by Apify to **your Apify account** through Apify's OpenRouter Actor (`apify/openrouter`) at OpenRouter's prices. Approximate costs per image on paid plans: Nano Banana (Gemini 2.5 Flash Image) **~$0.039**, Nano Banana 2 ~$0.04-0.08, Nano Banana Pro ~$0.13 (1K/2K) to ~$0.24 (4K), GPT Image models are priced per token by OpenAI. Each row shows the exact `modelCostUsd`. Note: Apify bills OpenRouter usage on the **free plan at 10x** the OpenRouter price, so this Actor is best on a paid Apify plan.

To cap your spend, set a maximum cost per run in the run options. The Actor stops cleanly before it goes over.

### What people use it for

- **Product photo background swap**: put every product of a catalogue on a white studio backdrop, a marble counter, a beach or a seasonal scene.
- **Lifestyle and ad creatives**: 3 prompts × 50 products = 150 scene variants in one run.
- **Restyle**: watercolor, flat illustration, anime, vintage film, brand color palette.
- **E-commerce listings**: consistent backgrounds and aspect ratios (1:1, 4:5, 16:9) for Etsy, Shopify, Amazon or marketplace feeds.
- **Text-to-image in bulk**: a CSV with a `prompt` column (or `{{title}}` placeholders) produces one image per row, e.g. blog headers, social posts, mockups or thumbnails.
- **Scraper + editor pipelines**: run a product scraper, pass its dataset (or `{{resource.defaultDatasetId}}` in an integration), and every product image is edited automatically.

### Input

| Field | Meaning |
|---|---|
| `prompts` | One or more prompts. With input images: each image × each prompt. Without: each prompt generates images. `{{column}}` placeholders are filled from CSV/dataset rows |
| `imageUrls` | Input image URLs (JPG, PNG, WebP, GIF...) |
| `datasetIds` | Apify datasets with image URLs (column auto-detected: `imageUrl`, `image`, `photo`, `thumbnail`, `displayUrl`, `images`...) |
| `fileUrls` | CSV / XLSX / JSON files (Google Sheets, Drive, Dropbox, GitHub links work) |
| `items` | Or paste JSON rows like `{"imageUrl": "...", "prompt": "..."}` |
| `imageColumn` / `promptColumn` | Optional column names. A row's own `prompt` column overrides `prompts` |
| `referenceImageUrls` | Up to 4 images sent with every job: logo, style or background reference |
| `model` / `customModel` | Nano Banana (default), Gemini 3.1 Flash (Lite) Image, Nano Banana 2 / 2.1 / Pro, GPT-5 Image (mini), GPT-5.4 Image 2, or any OpenRouter image model ID |
| `aspectRatio`, `imageSize` | 1:1 … 21:9; 1K/2K/4K for Gemini 3 models |
| `imagesPerPrompt` | 1-4 variations per job |
| `outputFormat` | PNG (default), JPEG, WebP |
| `maxImages`, `concurrency` | Limits (default 20 images, 4 in parallel) |

#### Example: white background for a product list

```json
{
  "imageUrls": ["https://example.com/products/mug.jpg", "https://example.com/products/lamp.jpg"],
  "prompts": ["Replace the background with a clean white studio backdrop and a soft natural shadow. Keep the product exactly as it is."],
  "aspectRatio": "1:1"
}
```

#### Example: 3 scenes per product from a Google Sheet

```json
{
  "fileUrls": ["https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit"],
  "imageColumn": "photo",
  "prompts": [
    "Place this {{title}} on a sunlit wooden kitchen table, photorealistic",
    "Place this {{title}} on a marble bathroom counter with plants",
    "Place this {{title}} in a cozy Christmas scene with warm lights"
  ],
  "aspectRatio": "4:5",
  "maxImages": 300
}
```

#### Example: text-to-image from prompts

```json
{ "prompts": ["Flat illustration of a robot watering plants, pastel colors"], "imagesPerPrompt": 4, "aspectRatio": "16:9" }
```

### Output

One row per image. The **Images** view shows the input and result as thumbnails.

```json
{
  "jobIndex": 1,
  "imageUrl": "https://api.apify.com/v2/key-value-stores/<run store>/records/img-00001-place-this-exact-mug-on-a-sunlit-2c41a9.png",
  "inputImageUrl": "https://upload.wikimedia.org/.../1280px-Coffee_Mug_%28Unsplash%29.jpg",
  "prompt": "Place this exact mug on a sunlit wooden kitchen table ...",
  "variant": 1,
  "model": "google/gemini-2.5-flash-image",
  "width": 1344, "height": 768, "format": "png", "bytes": 1650231,
  "promptTokens": 1301, "completionTokens": 1290, "modelCostUsd": 0.0393,
  "status": "ok", "error": null
}
```

Failed jobs have `status: "error"` and a readable `error` (e.g. `Input image: HTTP 404`, `The model returned no image (safety refusal)`), and they are not charged. The `OUTPUT` record has the run summary: images, errors, tokens and total model cost.

### Pricing compared

| | This Actor | Typical Nano Banana Actors on Apify |
|---|---|---|
| Fee per image | **$0.003 + model cost** (~$0.042 total with Nano Banana) | $0.04 to $0.26 all-in |
| API key | not needed | some need your own key |
| Batch input | URLs, **CSV / Google Sheets**, Apify datasets, JSON | usually one prompt / a few URLs |
| Prompt × image matrix, `{{column}}` placeholders | yes | rarely |
| Models | Nano Banana, Nano Banana 2 / Pro, GPT Image, any OpenRouter image model | usually one |
| Failed images / refusals | free | varies |

### Responsible use: important

- **Only edit images you own or have the right to edit** (your product photos, licensed or generated images).
- **No likeness / deepfake edits of real people.** Do not use this Actor to create or alter images of real, identifiable people without their consent, to impersonate anyone, or to make sexual, deceptive or harmful content. The model providers' usage policies (Google, OpenAI, OpenRouter) apply, and prohibited requests are refused by the models.
- Images generated by Gemini models carry Google's invisible SynthID watermark.

### Use with AI agents (Apify MCP)

Add the Actor to Claude, ChatGPT, Cursor or any MCP client through the [Apify MCP server](https://mcp.apify.com). An agent can pass image URLs plus a prompt and get public image links back, for example: *"Put these 10 product photos on a white background in 1:1 and give me the links."*

### FAQ

**Do I need a Google, OpenAI or OpenRouter key?** No. The Actor uses your run's Apify token to call Apify's OpenRouter Actor, and the model cost appears on your Apify bill.

**Which model should I use?** Start with Nano Banana (`google/gemini-2.5-flash-image`), which is fast, cheap and good at keeping the product identical. Use Nano Banana Pro for the best detail, text rendering and 2K/4K output, and GPT Image for OpenAI's style.

**How long are the result links valid?** Images are stored in the run's key-value store and follow your Apify data retention. Download them or copy them to your own storage if you need them long-term.

**Can I combine two images?** Yes. Add the second image as a reference image and describe the combination ("put the product from the first image on the background of the second").

**Why did a job fail without charge?** The input could not be downloaded or read, the model returned no image (often a safety refusal), or the model API failed after 4 retries. Note that model tokens already used by a refused call can still be billed by OpenRouter.

**How fast is it?** About 5-15 s per image per parallel slot. With the default 4 parallel requests, 100 images take about 5 minutes.

### Related Actors

- [AI Background Remover](https://apify.com/gazidev/background-remover)
- [Bulk AI Dataset Enricher](https://apify.com/gazidev/bulk-ai-dataset-enricher)
- [Bulk File Downloader](https://apify.com/gazidev/bulk-file-downloader)

# Actor input Schema

## `prompts` (type: `array`):

What the model should do. With input images, each image is edited with each prompt (e.g. 3 prompts = 3 background variants per product). Without input images, each prompt generates new images. Use {{column}} placeholders (e.g. {{title}}) to fill in values from CSV/dataset rows. Only edit images you own or have the right to edit.

## `imageUrls` (type: `array`):

Public image URLs (JPG, PNG, WebP, GIF...). One job per image per prompt. Large images are downscaled to 2048 px before sending.

## `datasetIds` (type: `array`):

E.g. the output of a product or Instagram scraper. The image column is detected automatically (imageUrl, image, photo, thumbnail, displayUrl, images...) or set it below. In an integration use {{resource.defaultDatasetId}}.

## `fileUrls` (type: `array`):

Public CSV, XLSX or JSON files with one row per job: an image URL column and/or a prompt column. Google Sheets ('Anyone with the link'), Drive, Dropbox and GitHub links work.

## `items` (type: `array`):

E.g. \[{"imageUrl": "https://.../shoe.jpg", "prompt": "white studio background"}, {"prompt": "a red sneaker on a beach"}]. A row's own prompt overrides the prompts above.

## `imageColumn` (type: `string`):

Column with the input image URL in CSV/dataset rows. Empty = auto-detect. Dot paths work (e.g. images.0).

## `promptColumn` (type: `string`):

Column with a per-row prompt. Empty = use a 'prompt' column if present, otherwise the prompts above.

## `referenceImageUrls` (type: `array`):

Optional, up to 4: a logo, a style or background reference, a second product... Attached after the main image, so you can write prompts like 'put the product from the first image into the scene of the second image'.

## `model` (type: `string`):

Called through Apify's OpenRouter Actor; the model cost is billed to your Apify account (no API key; Apify free plan: 10x). Nano Banana costs about $0.039 per image on paid plans.

## `customModel` (type: `string`):

Optional: any OpenRouter model with image output (openrouter.ai/models?output_modalities=image). Overrides the select above.

## `aspectRatio` (type: `string`):

Output aspect ratio (supported by Gemini image models).

## `imageSize` (type: `string`):

Only Gemini 3 image models support 2K/4K; others ignore it.

## `imagesPerPrompt` (type: `integer`):

Generate several variants of each job. Each saved image is charged.

## `outputFormat` (type: `string`):

Format of the saved images.

## `maxImages` (type: `integer`):

Stop after this many jobs (0 = no limit). You pay per saved image, so start small.

## `concurrency` (type: `integer`):

How many images are generated at the same time. Rate limits are retried automatically.

## `maxInputImageMb` (type: `integer`):

Bigger input files are skipped (not charged).

## `omitSourceRow` (type: `boolean`):

By default each output row includes the original CSV/dataset row as sourceRow.

## Actor input object example

```json
{
  "prompts": [
    "Place this exact mug on a sunlit wooden kitchen table with soft morning light and a blurred plant in the background. Keep the mug's shape, color and logo unchanged. Photorealistic product photo."
  ],
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Coffee_Mug_%28Unsplash%29.jpg/1280px-Coffee_Mug_%28Unsplash%29.jpg"
  ],
  "model": "google/gemini-2.5-flash-image",
  "aspectRatio": "auto",
  "imageSize": "auto",
  "imagesPerPrompt": 1,
  "outputFormat": "png",
  "maxImages": 1,
  "concurrency": 4,
  "maxInputImageMb": 25,
  "omitSourceRow": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `files` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "prompts": [
        "Place this exact mug on a sunlit wooden kitchen table with soft morning light and a blurred plant in the background. Keep the mug's shape, color and logo unchanged. Photorealistic product photo."
    ],
    "imageUrls": [
        "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Coffee_Mug_%28Unsplash%29.jpg/1280px-Coffee_Mug_%28Unsplash%29.jpg"
    ],
    "maxImages": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/bulk-ai-image-editor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "prompts": ["Place this exact mug on a sunlit wooden kitchen table with soft morning light and a blurred plant in the background. Keep the mug's shape, color and logo unchanged. Photorealistic product photo."],
    "imageUrls": ["https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Coffee_Mug_%28Unsplash%29.jpg/1280px-Coffee_Mug_%28Unsplash%29.jpg"],
    "maxImages": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("gazidev/bulk-ai-image-editor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "prompts": [
    "Place this exact mug on a sunlit wooden kitchen table with soft morning light and a blurred plant in the background. Keep the mug'\''s shape, color and logo unchanged. Photorealistic product photo."
  ],
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Coffee_Mug_%28Unsplash%29.jpg/1280px-Coffee_Mug_%28Unsplash%29.jpg"
  ],
  "maxImages": 1
}' |
apify call gazidev/bulk-ai-image-editor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gazidev/bulk-ai-image-editor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hfcpV2bkurj7G17Xv/builds/M7HUTcWm2yz8c6NSE/openapi.json
