Authentication
All requests require an API key passed in the
Authorization header.
Authorization: Bearer YOUR_API_KEY
Idempotency: all generation endpoints accept an optional
Idempotency-Key header.
Replaying the same key returns the original job_id without creating a duplicate job or charging again.
Idempotency-Key: 9f1c8b1e-7c2a-4a86-9b6e-2c5fa0b7d8e1
Generate images by editing existing ones. Pass 1–14 reference images along with a prompt — the model will transform them according to your instructions. Use
aspect_ratio: "auto" on image-input endpoints to match the first input image.
POST
/api/v1/generations
Base64 images → edited result
Request Body (JSON)
Field
Type
Required
Description
input_images_base64array[string]
Yes
1–14 input images as base64 strings (raw or
data:image/...;base64, prefix)promptstring
Yes
Text description of the desired output image
aspect_ratiostring
Yes
One of:
1:1 16:9 9:16 4:3 3:4 3:2 2:3 5:4 4:5 21:9 1:4 4:1 1:8 8:1 autoresolutionstring
Yes
One of:
1K 2K 4Kcallback_urlstring
No
Webhook URL to notify when the job completes
metadataobject
No
Arbitrary JSON metadata, returned as-is in job status
Request Example
{
"input_images_base64": ["iVBORw0KGgoAAAANSUhEUg..."],
"prompt": "Transform into oil painting style",
"aspect_ratio": "1:1",
"resolution": "2K",
"callback_url": "https://yoursite.com/webhook",
"metadata": {"order_id": "abc123"}
}
Response 202 Accepted
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"status_url": "/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000"
}
Error Responses
402Insufficient funds
401Unauthorized — missing or invalid API key
422Validation error — check image count (max 14) or total size (max 40 MB decoded)
502Failed to upload input images to storage
503Project or NanoBanana Pro is paused — try again later
POST
/api/v1/url-generations
Image URLs → edited result
Requires URL endpoint permission. Returns
403 if not enabled. Contact support to request access.
Request Body (JSON)
Field
Type
Required
Description
input_images_urlsarray[string]
Yes
1–14 image URLs (must start with
http:// or https://). Images are downloaded server-side.promptstring
Yes
Text description of the desired output image
aspect_ratiostring
Yes
One of:
1:1 16:9 9:16 4:3 3:4 3:2 2:3 5:4 4:5 21:9 1:4 4:1 1:8 8:1 autoresolutionstring
Yes
One of:
1K 2K 4Kcallback_urlstring
No
Webhook URL to notify when the job completes
metadataobject
No
Arbitrary JSON metadata
Request Example
{
"input_images_urls": [
"https://example.com/photo.jpg"
],
"prompt": "Transform into oil painting style",
"aspect_ratio": "1:1",
"resolution": "2K",
"callback_url": "https://yoursite.com/webhook"
}
Response 202 Accepted
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"status_url": "/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000"
}
Error Responses
403URL endpoint not enabled for your account
422Invalid URL, fetch timeout, server returned non-200, or total image size exceeds 40 MB
402Insufficient funds
502Failed to upload fetched images to storage
503Project or NanoBanana Pro is paused — try again later
POST
/api/v1/text-generations
Prompt → generated image
Request Body (JSON)
Field
Type
Required
Description
promptstring
Yes
Text description of the image to generate
aspect_ratiostring
Yes
One of:
1:1 16:9 9:16 4:3 3:4 3:2 2:3 5:4 4:5 21:9 1:4 4:1 1:8 8:1 autoresolutionstring
Yes
One of:
1K 2K 4Kcallback_urlstring
No
Webhook URL to notify when the job completes
metadataobject
No
Arbitrary JSON metadata, returned as-is in job status
Request Example
{
"prompt": "A futuristic city at sunset, cyberpunk style, highly detailed",
"aspect_ratio": "16:9",
"resolution": "2K",
"callback_url": "https://yoursite.com/webhook",
"metadata": {"order_id": "abc123"}
}
Response 202 Accepted
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"status_url": "/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000"
}
Error Responses
402Insufficient funds
401Unauthorized — missing or invalid API key
422Validation error — check aspect_ratio or resolution values
503Project or NanoBanana Pro is paused — try again later
GET
/api/v1/balance
Current account balance
Response Fields
Field
Type
Description
balancestring (decimal)
Total deposited balance, as a decimal string
reservedstring (decimal)
Sum of reservations on jobs that haven't yet completed
availablestring (decimal)
balance - reservedRequest
GET /api/v1/balance Authorization: Bearer YOUR_API_KEY
Response 200
{
"balance": "100.00",
"reserved": "5.50",
"available": "94.50"
}
GET
/api/v1/jobs/{job_id}
Get job status and result
Path Parameters
Parameter
Type
Required
Description
job_idstring (UUID)
Yes
The
job_id returned by any generation endpointResponse Fields
Field
Type
Description
statusstring
queued → processing → done or failedcreated_atstring (ISO 8601)
When the job was created
started_atstring (ISO 8601) | null
When a worker picked up the job;
null while still queuedfinished_atstring (ISO 8601) | null
When the job reached
done or failedresult.image_urlstring
Presigned URL to the generated image (only when
status = done). Valid for 24 hours.errorstring
Error message (only when
status = failed)metadataobject
Your custom metadata passed at creation
Response — done 200
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "done",
"created_at": "2026-03-03T12:00:00Z",
"started_at": "2026-03-03T12:00:02Z",
"finished_at": "2026-03-03T12:00:18Z",
"result": {"image_url": "https://..."},
"error": null,
"metadata": {"order_id": "abc123"}
}
Response — failed 200
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"created_at": "2026-03-03T12:00:00Z",
"started_at": "2026-03-03T12:00:02Z",
"finished_at": "2026-03-03T12:00:05Z",
"result": null,
"error": "Provider returned no image",
"metadata": null
}
Content Safety Filter (NSFW)
If Google's safety filters block the generated content, the job will return
status: "failed"
with error: "Content was blocked by safety filters".
This is not a billing error — no charge is applied for blocked generations.
{
"job_id": "c5de4e58-7787-436b-b37a-20e2e08d70ca",
"status": "failed",
"created_at": "2026-03-14T17:24:05Z",
"started_at": "2026-03-14T17:24:08Z",
"finished_at": "2026-03-14T17:24:29Z",
"result": null,
"error": "Content was blocked by safety filters",
"metadata": null
}
Polling Strategy
Poll
GET /api/v1/jobs/{job_id} until status is done or failed.
Recommended interval: 2–5 seconds. Generation typically takes 10–30 seconds.
Alternatively, use callback_url for webhooks.
Webhooks
Real-time notifications when a job completes or fails
How it works
Pass a
callback_url when creating a job. When the job finishes (done or failed), we POST the result to that URL.
Your server must respond with HTTP 2xx. If it doesn't, we retry automatically.
POST
your callback_url
Delivered when a job finishes
Request Headers
Header
Description
Content-Typeapplication/jsonX-SignatureHMAC-SHA256 signature:
sha256=<hex>X-TimestampUnix timestamp of when the request was sent
Payload — done
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "done",
"result": {"image_url": "https://..."},
"error": null,
"metadata": {"order_id": "abc123"}
}
Payload — failed
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"result": null,
"error": "Provider returned no image",
"metadata": null
}
Signature Verification
Confirm the webhook came from us
Your Webhook Secret is shown on your Dashboard.
We compute:
HMAC-SHA256("{X-Timestamp}.{json_body}", secret) → X-Signature: sha256=<hex>.
Steps
1
Serialize JSON body with sorted keys:
json.dumps(payload, sort_keys=True, separators=(",",":"))
2
Build message:
"{X-Timestamp}.{serialized_body}"
3
Compute
HMAC-SHA256(message, webhook_secret) and hex-encode
4
Compare
"sha256={result}" with X-Signature. Also check |now - X-Timestamp| < 300.
Python example
import hmac, hashlib, json, time
WEBHOOK_SECRET = "your_secret_here"
def verify_webhook(body, x_signature, x_timestamp):
if abs(time.time() - int(x_timestamp)) > 300:
return False
payload = json.loads(body)
body_str = json.dumps(
payload, separators=(",", ":"), sort_keys=True)
message = f"{x_timestamp}.{body_str}"
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(),
message.encode(),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, x_signature)
Node.js example
const crypto = require("crypto");
const WEBHOOK_SECRET = "your_secret_here";
function verifyWebhook(rawBody, xSignature, xTimestamp) {
const age = Math.abs(Date.now()/1000 - Number(xTimestamp));
if (age > 300) return false;
const payload = JSON.parse(rawBody);
const bodyStr = JSON.stringify(
Object.fromEntries(Object.entries(payload).sort()),
null, 0
);
const message = `${xTimestamp}.${bodyStr}`;
const expected = "sha256=" + crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(message).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected), Buffer.from(xSignature)
);
}
Retry Schedule
If your server doesn't respond with HTTP 2xx, we retry with exponential backoff. Up to 6 retries over ~1 hour total.
Make sure your endpoint responds within 10 seconds.
1m
2m
5m
10m
15m
20m