API Cookbook

Product-image recipe

Use one stable requestId per intended output. Store the returned imageUrl with your own product record and retry the same request id after a network timeout instead of creating a new generation.

const request = {
  prompt:
    "A clean ecommerce product photo of a matte black water bottle on a stone pedestal, soft studio light, empty space for a headline",
  model: "nano-banana-fast",
  ratio: "1:1",
  style: "studio",
  requestId: `catalog-${productId}-hero-v1`,
};

const response = await fetch(
  "https://nanobananafree.app/api/nano-banana/generate",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.NANO_BANANA_API_KEY}`,
    },
    body: JSON.stringify(request),
  }
);

if (!response.ok) throw new Error(await response.text());
const result = await response.json();
await saveProductHero(productId, result.imageUrl, result.requestId);

Safe retry pattern

  1. Generate a deterministic request id from your business object and version.
  2. Persist the successful imageUrl and requestId together.
  3. On timeout, retry the same payload and request id.
  4. Treat HTTP 409 as “already processing or failed”; inspect generation history before creating a new request.
  5. Treat HTTP 402 as a credit reservation failure; do not retry in a loop.

For up to four variations, use the synchronous batch endpoint. It returns partial results with HTTP 207 when some items fail; each item has its own request id and credit/refund history. For asynchronous terminal callbacks, create a webhook subscription in the signed-in Webhooks console or through POST /api/webhooks.

const batch = await fetch("https://nanobananafree.app/api/nano-banana/batch", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.NANO_BANANA_API_KEY}`,
  },
  body: JSON.stringify({
    prompt: "a premium banana product poster in a clean studio",
    model: "nano-banana-fast",
    count: 3,
    batchRequestId: "product-poster-v1",
  }),
});
const payload = await batch.json();
const successfulImages = payload.results.filter(
  (item) => item.status === "succeeded"
);

Reusable production setup

When the same prompt and settings will be used repeatedly, save them as a named workflow in the signed-in studio. This preserves the generation context without copying image bytes into the workflow record. Generated image URLs still need to be stored in your own asset system if you require long-term retention.

Reference-image remix

Send a base64 data URL in image when the input is a product or subject you own or have permission to edit. Keep the original asset in your own storage; the API response is the generated result, not a promise of permanent hosting.