← Back to Blog
Workflows • Nov 2, 2026

Batch AI Image Editing Workflow: A Practical Python Guide

Build a reliable batch AI image editing workflow in Python, compare cloud and local processing, control image outputs, and fix common API failures.

This batch AI image editing workflow completes a creator job end to end: it sends each local image plus an edit instruction to Nano Banana’s hosted image API, saves one result per input, and keeps going after an individual request failure. It also gives you a focused way to fix AI image API errors instead of restarting the collection. The steps come from Google’s official image-generation examples and Nano Banana’s Python client; “local” describes the script and files, not the hosted model.

Turn one edit into batch AI image editing

The repository’s edit_image() helper provides a useful unit of automation. It checks the API key and input path, reads the file bytes, guesses a MIME type, base64-encodes the image, sends text plus the image through client.interactions.create, checks output_image, and decodes the returned data into an output file. A batch workflow keeps that contract for every asset but isolates a failed request so the remaining files can still run. The reference implementation provides the exact client structure used below.

  • Inputs: Put source images in one directory and preserve the originals outside the run.
  • Instruction: Start with one repeatable edit, such as a controlled background replacement.
  • Requests: Base64-encode each file and attach it to a text instruction in the same input array.
  • Outputs: Decode output_image.data and save a clearly named file for every successful edit.
  • Failures: Record the filename and full exception, continue with the next asset, and report a summary after the loop.

This separation is useful when a creator is processing a product set, portrait series, or campaign variations. Google’s official examples demonstrate individual images and multi-turn interactions, while the repository shows how to package one local file for the hosted API. The official documentation provides the underlying request and response format.

AI image API vs local model: choose the execution path

The provided client is careful about what “local” means. Its comment states that it sends an image and editing instruction to Google’s hosted Gemini image model and that the model itself does not run on the local machine. The request contains base64 image data, not a path that the hosted service can open. That is the central distinction in an AI image API vs local model decision. The Nano Banana client sends the request; it does not turn Gemini into an offline generator.

  • Cloud API path: The SDK client runs on your computer, but interactions.create sends the prompt and encoded image to Google’s hosted service. The response is decoded and written back to local storage.
  • Local open-weight path: A local workflow would use model weights and an inference runtime on the machine performing the edit. Neither supplied source implements that path, so output parity, dependencies, and hardware requirements remain unknown here.

For a cloud vs local AI image generator comparison, do not infer capabilities from the filename nano_api.py. If local execution is required, begin with the Nano Banana installation and troubleshooting guide. For the hosted path, use the Nano Banana quickstart. The supplied API client does not establish that a local model or offline procedure is available.

Prepare the batch without changing the API contract

Install the SDK and expose the key in the environment used to launch Python. The repository lists google-genai and accepts either GOOGLE_API_KEY or GEMINI_API_KEY. It reads those variables directly and does not load a key file automatically. The repository prerequisites show the supported setup.

python -m pip install -U google-genai
export GEMINI_API_KEY='your-key-here'

Keep inputs and outputs separate. For example:

inputs/
  portrait.jpg
  product.jpg
outputs/

The source client uses mimetypes.guess_type(), and the official examples show JPEG and PNG image inputs. Make extensions reflect the actual files rather than relying on the client’s PNG fallback for an unknown type. For the instruction itself, the Nano Banana prompt library provides a useful place to develop a precise, repeatable edit before applying it to a collection.

Run a fault-tolerant Python batch

Before copying code, note a difference between the supplied sources. Google’s official interaction example uses gemini-nano-banana-2.1, while the repository snapshot calls gemini-3.1-flash-image. The evidence does not explain compatibility between those identifiers. The sample below keeps the value shown in Google’s supplied example in a MODEL constant; confirm the identifier in the official Gemini documentation before a long run.

The code uses only the demonstrated API surface and sends one file at a time. It deliberately adds no automatic retry behavior because the supplied sources do not define a retry policy.

import base64
import mimetypes
import os
from pathlib import Path

from google import genai

MODEL = 'gemini-nano-banana-2.1'


def edit_batch(input_dir, output_dir, prompt):
    api_key = os.getenv('GOOGLE_API_KEY') or os.getenv('GEMINI_API_KEY')
    if not api_key:
        raise RuntimeError(
            'Set GOOGLE_API_KEY or GEMINI_API_KEY before running this script.'
        )

    client = genai.Client(api_key=api_key)
    input_dir = Path(input_dir)
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)
    failures = []

    for input_file in sorted(input_dir.iterdir()):
        if not input_file.is_file():
            continue

        mime_type = mimetypes.guess_type(input_file.name)[0] or 'image/png'
        if not mime_type.startswith('image/'):
            failures.append(input_file.name)
            print(f'[!] Unsupported input type: {mime_type}')
            continue

        image_bytes = input_file.read_bytes()
        try:
            interaction = client.interactions.create(
                model=MODEL,
                input=[
                    {'type': 'text', 'text': prompt},
                    {
                        'type': 'image',
                        'data': base64.b64encode(image_bytes).decode('utf-8'),
                        'mime_type': mime_type,
                    },
                ],
                response_format={
                    'type': 'image',
                    'mime_type': 'image/png',
                },
            )
        except Exception as error:
            failures.append(input_file.name)
            print(f'[!] API Error for {input_file.name}: {error}')
            continue

        if not interaction.output_image:
            failures.append(input_file.name)
            print('[!] No image returned. Check the prompt and safety filters.')
            continue

        output_path = output_dir / f'{input_file.stem}_edited.png'
        output_path.write_bytes(
            base64.b64decode(interaction.output_image.data)
        )
        print(f'[+] Saved: {output_path}')

    if failures:
        raise RuntimeError(
            f'{len(failures)} image(s) failed: {failures}'
        )


edit_batch(
    'inputs',
    'outputs',
    'Replace the background with a neon cyberpunk city while preserving subject lighting.',
)

The final call applies the same instruction to every image. A request exception is recorded against its filename, a missing output_image is reported, and successful data is decoded to the matching PNG output. The summary error appears only after all files have been attempted, so a single bad request does not silently remove other outputs.

Control AI image output formats and revisions

Google’s official examples demonstrate a response_format with type image, MIME types image/jpeg and image/png, an aspect ratio of 16:9, and an image size of 2K. The supplied extract does not publish the complete allowed matrix, so treat these as demonstrated values rather than an exhaustive compatibility list. Check the current official image-generation documentation before changing format, ratio, or size.

  • Keep the requested MIME type and file extension aligned; the example requests PNG and writes a .png file.
  • Decode output_image.data before writing it rather than saving the base64 text as an image.
  • Record the model, prompt, and input filename with each result so a later correction can be reproduced deliberately.

For follow-up changes, Google describes multi-turn conversation as the recommended way to iterate on an image. Preserve interaction.id with the source filename, then pass it as previous_interaction_id in the next request:

revision = client.interactions.create(
    model=MODEL,
    input='Update this infographic to be in Spanish. Do not change other elements.',
    previous_interaction_id=interaction.id,
    response_format={
        'type': 'image',
        'mime_type': 'image/png',
        'aspect_ratio': '16:9',
        'image_size': '2K',
    },
)

This ties a correction to the generated interaction instead of blindly rerunning the original source image. The same base64 decoding step can save the revised output.

Fix AI image API errors without restarting

The repository’s checks and error messages provide a concrete troubleshooting sequence:

  1. Missing API key: If Python raises Set GOOGLE_API_KEY or GEMINI_API_KEY before running this script., set one of those variables in the process launching the script. Do not place the key in source code or diagnostic output.
  2. Input file not found: The reference helper raises FileNotFoundError with the missing path. Check that the path is relative to the directory from which Python was launched, not necessarily the directory containing the script.
  3. Unsupported input type: The helper rejects a detected MIME type that does not begin with image/. If detection returns nothing, the source falls back to image/png; do not use that fallback to disguise an unknown or incorrectly named file.
  4. No returned image: When interaction.output_image is absent, the repository says to check the prompt and safety filters. Make the edit instruction more specific and test that single file before rerunning the collection.
  5. Other API exception: The repository catches the exception and prints [!] API Error followed by its message. Preserve that complete text. Then reproduce the issue with one file using the reference edit_image() helper: a working single-file request points toward batch logic, while the same failure in both paths points toward the request itself.
  6. Python image API proxy error: The supplied nano_api.py is an SDK client, not a documented proxy. The evidence therefore cannot identify a proxy-specific cause. If another Python wrapper sits in front of it, retain the wrapper’s complete exception and diagnostic text, then compare a direct request with the proxied request before deciding which layer failed.

The supplied sources do not provide status-specific remedies, request limits, or retry rules, so avoid inventing them from a generic error message. Use the current official API documentation for those cases. For setup failures before a request is sent, the installation and troubleshooting guide is the right reference; for a valid request that returns no image, revisit the prompt library and isolate the instruction on one known-good input.

Sources