> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.beatapi.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.beatapi.io/_mcp/server.

# Nano Banana 2

POST https://api.beatapi.io/v1/images/tasks
Content-Type: application/json

Generate or edit an image from text and up to 14 reference images, at 1K, 2K or 4K.- Asynchronous: the create call returns a task ID in a moment; the image arrives when the task succeeds.
- One image per task. To get several images, create several tasks.
- Text to image, or edit and combine up to 14 reference images given as public HTTPS links.
- Priced per image by `resolution`: aspect ratio, number of references and prompt length do not change the price.
- A failed task costs nothing: the reservation is returned in full.POST/v1/images/tasksTry itOpens this model in the API Explorer. Enter your BeatAPI API key to create a task: the call is real and is billed to your own balance.## Model IDsAll five models use this endpoint and the same fields; they differ in the values they accept. The values below come straight from the gateway contract.| Model ID             | `resolution`     | Max `images` | `output_format` | Choose it when                                                                                                                              |
| -------------------- | ---------------- | ------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **`nano-banana-2`**  | `1K`, `2K`, `4K` | 14           | `png`, `jpeg`   | You want 1K to 4K output with many references at a low price. The default choice.                                                           |
| `nano-banana-2.1`    | `1K`, `2K`, `4K` | 14           | `png`, `jpeg`   | You want Nano Banana 2.1. It has the same fields and the same prices as Nano Banana 2.                                                      |
| `nano-banana-2-lite` | `1K`             | 14           | `png`, `jpeg`   | You only need 1K images.                                                                                                                    |
| `nano-banana-pro`    | `1K`, `2K`, `4K` | 14           | `png`, `jpg`    | You want the Pro model: 1K, 2K or 4K, up to 14 references. It accepts `jpg`, not `jpeg`, and no banner ratios (`1:4`, `4:1`, `1:8`, `8:1`). |
| `nano-banana`        | `1K`             | 3            | `png`, `jpeg`   | You need the earlier generation; 1K only, at most 3 references.                                                                             |## Endpoint and authentication`POST https://api.beatapi.io/v1/images/tasks`Authorization
string
RequiredYour BeatAPI API key, sent as `Bearer <BEATAPI_API_KEY>`. Create keys in the [Dashboard](https://beatapi.io/dashboard). Keep keys on your server; never ship them in a browser or mobile app.Send the body as JSON with `Content-Type: application/json`. `Idempotency-Key` is optional: send a unique value (at most 255 characters) per logical request and reuse it only to retry that exact body. Within 24 hours a retry with the same key and body returns the first response again with `Idempotent-Replayed: true`; the same key with a different body returns `409 idempotency_conflict`. Responses other than 2xx are not stored, so a retry after a 400 or 402 runs again.## How a request works`POST` the parameters below to the endpoint. A `201` means the task was accepted and its price reserved from your balance; the work runs in the background. Save `data.id`.Call `GET /v1/tasks/{task_id}` and wait `data.poll_after_seconds` between calls (5 s for images, 8 s for videos) until `status` is `succeeded` or `failed`. Or register a [webhook](/webhooks) for `task.succeeded` and `task.failed`; keep polling as the fallback, because a task stopped by the processing timeout sends no event.On `succeeded`, download every `data.output.media[].url` and store the files you need; BeatAPI does not promise how long result URLs stay available. On `failed`, read `data.error_code` and `data.error_message`: the reserved amount was returned to your balance, and `data.retryable` says whether sending the same request again can succeed.## Input modes| Mode                   | Required inputs             | Notes                                                                                   |
| ---------------------- | --------------------------- | --------------------------------------------------------------------------------------- |
| Text to image          | `model`, `prompt`           | Leave out `images`.                                                                     |
| Edit or combine images | `model`, `prompt`, `images` | 1 to 14 public HTTPS image links. Describe in `prompt` what to change, keep or combine. |## Request parametersmodel
string
RequiredThe model to run.**Value**: `nano-banana-2`**If the value is wrong**- Missing: `"model" is required`
- Unknown model ID: `"model" nano-banana-9 is not catalogued`
- A video model sent to this endpoint: `"model" seedance-2.5 is served by POST /v1/videos/tasks, not this endpoint`prompt
string
RequiredWhat to draw, or what to change in the reference images. Plain language works best: subject, style, lighting, composition, and any text that must appear in the image.**Length**: 1–5000 characters- Length is counted in characters (a CJK character counts as one).
- When you send `images`, say which image does what, for example "Use the first image for the product and the second for the background".**If the value is wrong**- Missing: `"prompt" is required`
- Empty or longer than the limit: `"prompt" has an unsupported length`
- Not a string: `"prompt" must be a string`images
string[]
OptionalReference images to edit or combine. Sending this field switches the request from text-to-image to image editing.**Items**: 1–14  \
Each item is a public `https://` URL. For a local file, [upload it first](/quick-guide#upload-an-input-file) with `POST /v1/files` and pass the returned `data.url`.- Each link must return the image file itself (PNG, JPEG or WebP) over public HTTPS, at most 50 MB. The gateway downloads the first bytes of every link before creating the task.
- Links to web pages, login walls, expired signed URLs or hotlink-protected hosts are refused, because they do not return an image.
- Positions in error messages are 0-based: `images[0]` is the first link.**If the value is wrong**- More than the maximum: `"images" accepts at most 14 items`
- An empty array: `"images" requires at least 1 item`
- Not an https\:// link: `"images[0]" must be a public HTTPS URL`
- A private or unsafe address: `"images[0]" must be a publicly reachable HTTPS image URL`
- The host answers with a 4xx error: `"images[0]" could not be downloaded: the URL returned HTTP 404`
- The link returns something other than PNG, JPEG or WebP: `"images[0]" is not a PNG, JPEG or WebP image: the URL returned text/html`
- Larger than 50 MB: `"images[2]" is 63.4 MB; images must be 50 MB or smaller`**Example**```json
"images": [
  "https://media.beatapi.io/samples/smart-bottle.png",
  "https://media.beatapi.io/samples/neon-singer.png"
]
```aspect_ratio
string
OptionalDefault: 1:1Shape of the image as width:height.**Allowed values**: `1:1`, `1:4`, `1:8`, `2:3`, `3:2`, `3:4`, `4:1`, `4:3`, `4:5`, `5:4`, `8:1`, `9:16`, `16:9`, `21:9`, `auto`  \
**Default**: `1:1`- `auto` leaves the shape to the model. With `images` it normally follows the reference, but this is not guaranteed on every route; name the ratio if the shape matters.
- `1:4`, `4:1`, `1:8` and `8:1` are extra-tall and extra-wide banners; Nano Banana Pro and Nano Banana do not accept them.When you edit an image, `auto` usually keeps the shape of the reference. If you leave the field out, the default `1:1` is used and the result is square.**If the value is wrong**- A value not in the list: `"aspect_ratio" must be one of: [1:1 1:4 1:8 2:3 3:2 3:4 4:1 4:3 4:5 5:4 8:1 9:16 16:9 21:9 auto]`resolution
string
OptionalDefault: 1KSize class of the image. Higher classes give larger images and cost more; the exact pixel size depends on `aspect_ratio` and is decided by the model.**Allowed values**: `1K`, `2K`, `4K`  \
**Default**: `1K`- Use the capital K: `2k` is refused.**If the value is wrong**- A value not in the list: `"resolution" must be one of: [1K 2K 4K]`output_format
string
OptionalDefault: pngFile type of the result.**Allowed values**: `png`, `jpeg`  \
**Default**: `png`- If the model returns the other of the two types, BeatAPI converts the image before storing it, so the file normally has the format you asked for; if a conversion fails, the file is stored as delivered. Transparent areas become white in a JPEG. An image too large to convert (above 64 megapixels) is stored as delivered. `output.media[].mime_type` always states the stored type.
- Write `jpeg`, not `jpg` (only Nano Banana Pro uses `jpg`).**If the value is wrong**- A value not in the list (for example `jpg` or `webp`): `"output_format" must be one of: [png jpeg]`## Input media limitsThe gateway reads every input before the task is created and refuses one outside these limits with a `400 bad_request` that names the input. Nothing is charged.- Reference images: PNG, JPEG or WebP, recognised from the file content rather than the file name or `Content-Type`; at most 50 MB each (decimal megabytes; checked when the host states the size); 1 to 14 links. GIF, HEIC and SVG are refused.
- No pixel or aspect-ratio limit is checked for this model.
- If a host does not answer in time, the link is not refused at create; a link that still cannot be fetched later fails the task with `bad_request`, and the task is refunded.
- Have a local file? Upload it with `POST /v1/files` and pass the returned `data.url`.## Fields this model does not acceptThe image endpoint knows a few fields that other image models use. Sending one of them here is refused instead of being ignored, so you never pay for a setting that had no effect:- `quality`, `seed`, `background`, `size`, `enhance_prompt` → `field "quality" is not supported by model nano-banana-2` (with the field you sent).
- Any other unknown field, such as `n` → `unknown field: n`. There is no `n`: one task makes one image.## Request examples```bash title="cURL"
curl --request POST \
  --url https://api.beatapi.io/v1/images/tasks \
  --header "Authorization: Bearer $BEATAPI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "model": "nano-banana-2",
  "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
  "aspect_ratio": "16:9",
  "resolution": "2K"
}'
``````python title="Python"
import os
import requests

response = requests.post(
    "https://api.beatapi.io/v1/images/tasks",
    headers={"Authorization": f"Bearer {os.environ['BEATAPI_API_KEY']}"},
    json={
      "model": "nano-banana-2",
      "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
      "aspect_ratio": "16:9",
      "resolution": "2K"
    },
    timeout=60,
)
body = response.json()
if not response.ok:
    raise RuntimeError(f"{response.status_code} {body['error']['code']}: {body['error']['message']}")
task = body["data"]
print(task["id"], task["status"])
``````javascript title="JavaScript"
const response = await fetch('https://api.beatapi.io/v1/images/tasks', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.BEATAPI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "model": "nano-banana-2",
    "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
    "aspect_ratio": "16:9",
    "resolution": "2K"
  }),
});
const body = await response.json();
if (!response.ok) throw new Error(`${response.status} ${body.error.code}: ${body.error.message}`);
console.log(body.data.id, body.data.status);
```### Text to imageA 2K landscape image from a prompt.```bash
curl --request POST \
  --url https://api.beatapi.io/v1/images/tasks \
  --header "Authorization: Bearer $BEATAPI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "model": "nano-banana-2",
  "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
  "aspect_ratio": "16:9",
  "resolution": "2K"
}'
```### Edit one imageChange the background and keep the product. `auto` usually keeps the shape of the reference.```bash
curl --request POST \
  --url https://api.beatapi.io/v1/images/tasks \
  --header "Authorization: Bearer $BEATAPI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "model": "nano-banana-2",
  "prompt": "Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.",
  "images": [
    "https://media.beatapi.io/samples/smart-bottle.png"
  ],
  "aspect_ratio": "auto"
}'
```### Combine several imagesName each reference in the prompt by its position.```bash
curl --request POST \
  --url https://api.beatapi.io/v1/images/tasks \
  --header "Authorization: Bearer $BEATAPI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "model": "nano-banana-2",
  "prompt": "Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.",
  "images": [
    "https://media.beatapi.io/samples/smart-bottle.png",
    "https://media.beatapi.io/samples/neon-singer.png"
  ],
  "aspect_ratio": "1:1",
  "resolution": "1K"
}'
```### 4K JPEG posterA tall poster delivered as JPEG.```bash
curl --request POST \
  --url https://api.beatapi.io/v1/images/tasks \
  --header "Authorization: Bearer $BEATAPI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "model": "nano-banana-2",
  "prompt": "A retro travel poster of a coastal city at sunset, bold flat colours, large title text \"SEASIDE\" at the top",
  "aspect_ratio": "9:16",
  "resolution": "4K",
  "output_format": "jpeg"
}'
```## Responses### Create a task (201)The task object. `output` is `null` until the task succeeds. Fields marked “while running” disappear once the task finishes.```json
{
  "data": {
    "id": "task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ",
    "object": "task",
    "task_kind": "image",
    "capability_id": "nano-banana-2",
    "capability_version": 1,
    "media_type": "image",
    "model": "nano-banana-2",
    "status": "queued",
    "stage": "queued",
    "created_at": 1791993600,
    "updated_at": 1791993600,
    "completed_at": null,
    "poll_after_seconds": 5,
    "output": null,
    "usage": {
      "credits_reserved": 0.04,
      "credits_settled": 0,
      "credits_refunded": 0,
      "credits_charged": 0.04
    },
    "request_id": "req_5f0c2a9e4b7d4e31",
    "error_code": null,
    "error_message": null
  }
}
```| Field                                  | Type              | Description                                                                                                                              |
| -------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `data.id`                              | `string`          | Task ID: `task_` followed by 32 letters and digits. Use it to poll and to match webhook events.                                          |
| `data.object`                          | `string`          | Always `task`.                                                                                                                           |
| `data.task_kind`                       | `string`          | `image` or `video`.                                                                                                                      |
| `data.capability_id`                   | `string`          | The model ID you sent.                                                                                                                   |
| `data.capability_version`              | `integer`         | Contract version of the model, currently `1`.                                                                                            |
| `data.media_type`                      | `string`          | `image` or `video`.                                                                                                                      |
| `data.model`                           | `string`          | The model ID you sent (same as `capability_id`).                                                                                         |
| `data.status`                          | `string`          | `queued`, `processing`, `succeeded` or `failed`. Only the last two are final.                                                            |
| `data.stage`                           | `string`          | Repeats `status`; kept for older clients.                                                                                                |
| `data.created_at`                      | `integer`         | When the task was created, Unix seconds.                                                                                                 |
| `data.updated_at`                      | `integer`         | Last change, Unix seconds.                                                                                                               |
| `data.completed_at`                    | `integer \| null` | When the task finished, Unix seconds; `null` while it runs.                                                                              |
| `data.poll_after_seconds`              | `integer`         | While running: how long to wait before the next poll (5 for images, 8 for videos).                                                       |
| `data.typical_seconds`                 | `object`          | While running, when enough recent tasks exist: `p50` and `p90` completion times in seconds over the last `window`, from `samples` tasks. |
| `data.output`                          | `object \| null`  | The result; `null` until `status` is `succeeded`. See the output fields below.                                                           |
| `data.usage.credits_reserved`          | `number`          | USD held from your balance when the task was created.                                                                                    |
| `data.usage.credits_charged`           | `number`          | USD this task costs; `0` on a failed task.                                                                                               |
| `data.usage.credits_settled`           | `number`          | USD finally taken; set when the task succeeds, otherwise `0`.                                                                            |
| `data.usage.credits_refunded`          | `number`          | USD returned to your balance; equals the reservation on a failed task once the refund is recorded.                                       |
| `data.usage.billable_duration_seconds` | `integer`         | Video only: the seconds the price is based on. Absent when the model reports no duration.                                                |
| `data.request_id`                      | `string`          | Request ID. Include it when you contact support.                                                                                         |
| `data.error_code`                      | `string \| null`  | Failure code; `null` unless the task failed.                                                                                             |
| `data.error_message`                   | `string \| null`  | What went wrong and what to change; `null` unless the task failed.                                                                       |### Finished task`GET /v1/tasks/{task_id}` returns HTTP 200 with `status: "succeeded"` and the result in `data.output`.```json
{
  "data": {
    "id": "task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ",
    "object": "task",
    "task_kind": "image",
    "capability_id": "nano-banana-2",
    "capability_version": 1,
    "media_type": "image",
    "model": "nano-banana-2",
    "status": "succeeded",
    "stage": "succeeded",
    "created_at": 1791993600,
    "updated_at": 1791993690,
    "completed_at": 1791993690,
    "output": {
      "media": [
        {
          "type": "image",
          "url": "https://media.beatapi.io/outputs/task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ/0.png",
          "mime_type": "image/png"
        }
      ],
      "r2_url": "https://media.beatapi.io/outputs/task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ/0.png"
    },
    "usage": {
      "credits_reserved": 0.04,
      "credits_settled": 0.04,
      "credits_refunded": 0,
      "credits_charged": 0.04
    },
    "request_id": "req_5f0c2a9e4b7d4e31",
    "error_code": null,
    "error_message": null
  }
}
```#### Output fields| Field                      | Type       | Description                                                                                    |
| -------------------------- | ---------- | ---------------------------------------------------------------------------------------------- |
| `output.media`             | `object[]` | The generated files; one item for this model.                                                  |
| `output.media[].type`      | `string`   | Always `image`.                                                                                |
| `output.media[].url`       | `string`   | HTTPS link to the stored image. Download and keep it; no retention period is promised.         |
| `output.media[].mime_type` | `string`   | The stored file type, normally the one `output_format` asked for: `image/png` or `image/jpeg`. |
| `output.r2_url`            | `string`   | The first media URL again, for older clients. Read `media` instead.                            |### Failed taskA failed task is still HTTP 200 on `GET /v1/tasks/{task_id}`: the failure is in the body. The reservation is returned in full, so nothing is charged.```json
{
  "data": {
    "id": "task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ",
    "object": "task",
    "task_kind": "image",
    "capability_id": "nano-banana-2",
    "capability_version": 1,
    "media_type": "image",
    "model": "nano-banana-2",
    "status": "failed",
    "stage": "failed",
    "created_at": 1791993600,
    "updated_at": 1791993690,
    "completed_at": 1791993690,
    "output": null,
    "usage": {
      "credits_reserved": 0.04,
      "credits_settled": 0,
      "credits_refunded": 0.04,
      "credits_charged": 0
    },
    "retryable": true,
    "request_id": "req_5f0c2a9e4b7d4e31",
    "error_code": "processing_failed",
    "error_message": "The task failed during processing on our side. Retry; a failed task is refunded."
  }
}
```| Field                | Type      | Description                                                                                                                                                                                                            |
| -------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data.error_code`    | `string`  | One of `bad_request` and `content_policy_violation` (change the request) or `processing_unavailable`, `processing_timeout`, `processing_failed` and `result_transfer_failed` (try again later). See [Errors](/errors). |
| `data.error_message` | `string`  | A sentence that names the problem and the fix.                                                                                                                                                                         |
| `data.retryable`     | `boolean` | Present only on a failed task: `true` when the same request can succeed if you send it again.                                                                                                                          |
| `data.usage`         | `object`  | Once the refund is recorded (normally at the moment the task fails): `credits_charged` and `credits_settled` are `0`, and `credits_refunded` equals `credits_reserved`.                                                |## Complete exampleCreate a task, wait for it with the server’s polling hint, and print the result URLs. Set `BEATAPI_API_KEY` in your environment and install `requests`.```python
import os
import time
import uuid

import requests

API = "https://api.beatapi.io"
HEADERS = {"Authorization": f"Bearer {os.environ['BEATAPI_API_KEY']}"}


def create_task(body: dict) -> dict:
    # One Idempotency-Key per logical request: a retry with the same key cannot create a second task.
    headers = {**HEADERS, "Idempotency-Key": str(uuid.uuid4())}
    for attempt in range(3):
        response = requests.post(f"{API}/v1/images/tasks", headers=headers, json=body, timeout=60)
        if response.status_code in (429, 503) and attempt < 2:
            time.sleep(int(response.headers.get("Retry-After", 10)))
            continue
        break
    try:
        payload = response.json()
    except ValueError:  # an edge error page is not JSON
        response.raise_for_status()
        raise
    if not response.ok:
        error = payload["error"]
        raise RuntimeError(f"{response.status_code} {error['code']}: {error['message']} (request_id={error.get('request_id')})")
    return payload["data"]


def wait_for_task(task_id: str, timeout_seconds: int = 1800) -> dict:
    deadline = time.monotonic() + timeout_seconds
    while time.monotonic() < deadline:
        response = requests.get(f"{API}/v1/tasks/{task_id}", headers=HEADERS, timeout=30)
        if response.status_code == 429 or response.status_code >= 500:
            time.sleep(int(response.headers.get("Retry-After", 10)))
            continue
        response.raise_for_status()
        task = response.json()["data"]
        if task["status"] in ("succeeded", "failed"):
            return task
        time.sleep(task.get("poll_after_seconds", 5))
    raise TimeoutError(f"task {task_id} is still running; keep polling later")


task = create_task({
  "model": "nano-banana-2",
  "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
  "aspect_ratio": "16:9",
  "resolution": "2K"
})
print("created", task["id"])
task = wait_for_task(task["id"])
if task["status"] == "failed":
    raise SystemExit(f"{task['error_code']}: {task['error_message']}")
for item in task["output"]["media"]:
    print(item["url"])
```## Billing- One image per task, priced by `resolution` only (table below). Aspect ratio, number of references and prompt length do not change the price.
- The price is reserved when the task is created, from your balance and from the key’s quota if it has one. A request refused before a task exists costs nothing.
- A succeeded task keeps the reserved amount. A failed task returns it in full: `credits_refunded` then equals `credits_reserved`.Current list prices, the same as on [Pricing](https://beatapi.io/pricing). An account with a negotiated discount pays less; `data.usage` on the task always shows the amount actually charged.| Option       | List price (USD) |
| ------------ | ---------------- |
| 1 image · 1K | \$0.02           |
| 1 image · 2K | \$0.04           |
| 1 image · 4K | \$0.06           |## Common errors| Error or symptom                                                               | Cause                                                                                                                             | Fix                                                                                 |
| ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| 400 `"resolution" must be one of: [1K 2K 4K]`                                  | Lower-case `2k`, or a pixel size such as `2048x2048`.                                                                             | Send `1K`, `2K` or `4K`.                                                            |
| 400 `"output_format" must be one of: [png jpeg]`                               | `jpg` or `webp` sent to Nano Banana 2.                                                                                            | Send `jpeg` (only Nano Banana Pro takes `jpg`).                                     |
| 400 `unknown field: n`                                                         | Asking for several images in one task.                                                                                            | Remove `n` and create one task per image.                                           |
| 400 `field "seed" is not supported by model nano-banana-2`                     | A field from another image model (`seed`, `quality`, `size`, `background`, `enhance_prompt`).                                     | Remove the field.                                                                   |
| 400 `"images[0]" must be a public HTTPS URL`                                   | An `http://` link, a `data:` URI or a local path.                                                                                 | Host the file on HTTPS, or upload it with `POST /v1/files` and use `data.url`.      |
| 400 `"images[1]" is not a PNG, JPEG or WebP image: the URL returned text/html` | The link opens a web page, a login page or an expired signed URL instead of the file.                                             | Link directly to the image file.                                                    |
| 400 `"images[0]" could not be downloaded: the URL returned HTTP 403`           | The host blocks downloads from servers (hotlink protection or private bucket).                                                    | Make the file public, or upload it with `POST /v1/files`.                           |
| An edited image comes back square                                              | `aspect_ratio` was left out, so the default `1:1` was used.                                                                       | Name the ratio you want. `auto` usually keeps the shape of the reference.           |
| Task `failed` with `content_policy_violation`                                  | The prompt, a reference image or the generated image was refused by moderation. An image that shows a real person can be refused. | Change the prompt or the image. Retrying unchanged fails again; nothing is charged. |
| Task `failed` with `processing_timeout`                                        | The image did not finish within the processing window.                                                                            | Retry; the failed task was refunded.                                                |
| 403 `forbidden` on an account that never topped up                             | Free credit covers only some models.                                                                                              | Top up once, or use a model available on free credit.                               |## HTTP errors at a glanceErrors before a task exists arrive as a non-2xx response, and nothing is charged. Branch on `error.code` or `error.retryable`; `message` names the problem. The full list and retry policy are on [Errors](/errors).```json
{
  "error": {
    "code": "bad_request",
    "message": "\"prompt\" is required",
    "retryable": false,
    "request_id": "req_5f0c2a9e4b7d4e31"
  }
}
```| HTTP status | `error.code`                                                                                  | What to do                                                                                                            |
| ----------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| 400         | `bad_request`, `content_policy_violation`                                                     | Fix the request; the message names the field. Do not retry unchanged.                                                 |
| 401         | `missing_api_key`, `invalid_api_key` (older clients may see `unauthorized`)                   | Send a valid key as `Authorization: Bearer <key>`.                                                                    |
| 402         | `insufficient_credits`                                                                        | Top up, or raise the key’s quota, then send it again.                                                                 |
| 403         | `forbidden`                                                                                   | The key or account may not use this model (for example, free credit only covers some models). Do not retry unchanged. |
| 404         | `not_found`                                                                                   | On `GET /v1/tasks/{task_id}`: the task does not exist for this key’s account. Check the ID.                           |
| 409         | `idempotency_conflict`                                                                        | Use a new `Idempotency-Key`, or wait for the first request with this key to finish.                                   |
| 429         | `rate_limit_exceeded`                                                                         | Wait `Retry-After` seconds (also `error.retry_after_seconds`), then retry.                                            |
| 500         | `internal_error`                                                                              | Retry with backoff; contact support with `request_id` if it continues.                                                |
| 503         | `processing_unavailable`, `processing_timeout`, `processing_failed`, `result_transfer_failed` | Nothing was charged. Retry with exponential backoff.                                                                  |

Reference: https://docs.beatapi.io/api-reference/image-api/nano-banana-2

## Authentication

- `Authorization` header (bearer token, required) — Send your API key as `Authorization: Bearer <BEATAPI_API_KEY>`.

## Servers

- `https://api.beatapi.io` (Direct, default)
- `https://api.beatapi.io` (API)

## Request

### Headers

- `Idempotency-Key` (string, optional) — Send the body as JSON with `Content-Type: application/json`. `Idempotency-Key` is optional: send a unique value (at most 255 characters) per logical request and reuse it only to retry that exact body. Within 24 hours a retry with the same key and body returns the first response again with `Idempotent-Replayed: true`; the same key with a different body returns `409 idempotency_conflict`. Responses other than 2xx are not stored, so a retry after a 400 or 402 runs again.

### Body (application/json)

This endpoint expects a NanoBanana2ImageRequest.

- `model` (enum, required) — The model to run. **Value**: `nano-banana-2` **If the value is wrong** - Missing: `"model" is required` - Unknown model ID: `"model" nano-banana-9 is not catalogued` - A video model sent to this endpoint: `"model" seedance-2.5 is served by POST /v1/videos/tasks, not this endpoint`
  - Allowed values: `nano-banana-2`
- `prompt` (string, required) — What to draw, or what to change in the reference images. Plain language works best: subject, style, lighting, composition, and any text that must appear in the image. **Length**: 1–5000 characters - Length is counted in characters (a CJK character counts as one). - When you send `images`, say which image does what, for example "Use the first image for the product and the second for the background". **If the value is wrong** - Missing: `"prompt" is required` - Empty or longer than the limit: `"prompt" has an unsupported length` - Not a string: `"prompt" must be a string`
- `aspect_ratio` (enum, optional, default: 1:1) — Shape of the image as width:height. **Allowed values**: `1:1`, `1:4`, `1:8`, `2:3`, `3:2`, `3:4`, `4:1`, `4:3`, `4:5`, `5:4`, `8:1`, `9:16`, `16:9`, `21:9`, `auto` **Default**: `1:1` - `auto` leaves the shape to the model. With `images` it normally follows the reference, but this is not guaranteed on every route; name the ratio if the shape matters. - `1:4`, `4:1`, `1:8` and `8:1` are extra-tall and extra-wide banners; Nano Banana Pro and Nano Banana do not accept them. **Notes**: When you edit an image, `auto` usually keeps the shape of the reference. If you leave the field out, the default `1:1` is used and the result is square. **If the value is wrong** - A value not in the list: `"aspect_ratio" must be one of: [1:1 1:4 1:8 2:3 3:2 3:4 4:1 4:3 4:5 5:4 8:1 9:16 16:9 21:9 auto]`
  - Allowed values: `1:1`, `1:4`, `1:8`, `2:3`, `3:2`, `3:4`, `4:1`, `4:3`, `4:5`, `5:4`, `8:1`, `9:16`, `16:9`, `21:9`, `auto`
- `images` (list of string, optional) — Reference images to edit or combine. Sending this field switches the request from text-to-image to image editing. **Items**: 1–14 Each item is a public `https://` URL. For a local file, [upload it first](/quick-guide#upload-an-input-file) with `POST /v1/files` and pass the returned `data.url`. - Each link must return the image file itself (PNG, JPEG or WebP) over public HTTPS, at most 50 MB. The gateway downloads the first bytes of every link before creating the task. - Links to web pages, login walls, expired signed URLs or hotlink-protected hosts are refused, because they do not return an image. - Positions in error messages are 0-based: `images[0]` is the first link. **If the value is wrong** - More than the maximum: `"images" accepts at most 14 items` - An empty array: `"images" requires at least 1 item` - Not an https:// link: `"images[0]" must be a public HTTPS URL` - A private or unsafe address: `"images[0]" must be a publicly reachable HTTPS image URL` - The host answers with a 4xx error: `"images[0]" could not be downloaded: the URL returned HTTP 404` - The link returns something other than PNG, JPEG or WebP: `"images[0]" is not a PNG, JPEG or WebP image: the URL returned text/html` - Larger than 50 MB: `"images[2]" is 63.4 MB; images must be 50 MB or smaller` **Example** ```json "images": [ "https://media.beatapi.io/samples/smart-bottle.png", "https://media.beatapi.io/samples/neon-singer.png" ] ```
- `output_format` (enum, optional, default: png) — File type of the result. **Allowed values**: `png`, `jpeg` **Default**: `png` - If the model returns the other of the two types, BeatAPI converts the image before storing it, so the file normally has the format you asked for; if a conversion fails, the file is stored as delivered. Transparent areas become white in a JPEG. An image too large to convert (above 64 megapixels) is stored as delivered. `output.media[].mime_type` always states the stored type. - Write `jpeg`, not `jpg` (only Nano Banana Pro uses `jpg`). **If the value is wrong** - A value not in the list (for example `jpg` or `webp`): `"output_format" must be one of: [png jpeg]`
  - Allowed values: `png`, `jpeg`
- `resolution` (enum, optional, default: 1K) — Size class of the image. Higher classes give larger images and cost more; the exact pixel size depends on `aspect_ratio` and is decided by the model. **Allowed values**: `1K`, `2K`, `4K` **Default**: `1K` - Use the capital K: `2k` is refused. **If the value is wrong** - A value not in the list: `"resolution" must be one of: [1K 2K 4K]`
  - Allowed values: `1K`, `2K`, `4K`

## Response

### 201

The task object. `output` is `null` until the task succeeds. Fields marked “while running” disappear once the task finishes.

- `map from string to any`

## Errors

### 400 Bad Request Error

`bad_request`, `content_policy_violation` Fix the request; the message names the field. Do not retry unchanged.

- `map from string to any`

### 401 Unauthorized Error

`missing_api_key`, `invalid_api_key` (older clients may see `unauthorized`) Send a valid key as `Authorization: Bearer <key>`.

- `map from string to any`

### 402 Payment Required Error

`insufficient_credits` Top up, or raise the key’s quota, then send it again.

- `map from string to any`

### 403 Forbidden Error

`forbidden` The key or account may not use this model (for example, free credit only covers some models). Do not retry unchanged.

- `map from string to any`

### 409 Conflict Error

`idempotency_conflict` Use a new `Idempotency-Key`, or wait for the first request with this key to finish.

- `map from string to any`

### 429 Too Many Requests Error

`rate_limit_exceeded` Wait `Retry-After` seconds (also `error.retry_after_seconds`), then retry.

- `map from string to any`

### 500 Internal Server Error

`internal_error` Retry with backoff; contact support with `request_id` if it continues.

- `map from string to any`

### 503 Service Unavailable Error

`processing_unavailable`, `processing_timeout`, `processing_failed`, `result_transfer_failed` Nothing was charged. Retry with exponential backoff.

- `map from string to any`

## Examples

### Text to image

**Request**

```json
{
  "model": "nano-banana-2",
  "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
  "aspect_ratio": "16:9",
  "resolution": "2K"
}
```

**Response**

```json
{
  "data": {
    "capability_id": "nano-banana-2",
    "capability_version": 1,
    "completed_at": null,
    "created_at": 1791993600,
    "error_code": null,
    "error_message": null,
    "id": "task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ",
    "media_type": "image",
    "model": "nano-banana-2",
    "object": "task",
    "output": null,
    "poll_after_seconds": 5,
    "request_id": "req_5f0c2a9e4b7d4e31",
    "stage": "queued",
    "status": "queued",
    "task_kind": "image",
    "updated_at": 1791993600,
    "usage": {
      "credits_charged": 0.04,
      "credits_refunded": 0,
      "credits_reserved": 0.04,
      "credits_settled": 0
    }
  }
}
```

**SDK Code**

```python Text to image
import requests

url = "https://api.beatapi.io/v1/images/tasks"

payload = {
    "model": "nano-banana-2",
    "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
    "aspect_ratio": "16:9",
    "resolution": "2K"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Text to image
const url = 'https://api.beatapi.io/v1/images/tasks';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"model":"nano-banana-2","prompt":"A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field","aspect_ratio":"16:9","resolution":"2K"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Text to image
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.beatapi.io/v1/images/tasks"

	payload := strings.NewReader("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field\",\n  \"aspect_ratio\": \"16:9\",\n  \"resolution\": \"2K\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Text to image
require 'uri'
require 'net/http'

url = URI("https://api.beatapi.io/v1/images/tasks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field\",\n  \"aspect_ratio\": \"16:9\",\n  \"resolution\": \"2K\"\n}"

response = http.request(request)
puts response.read_body
```

```java Text to image
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.beatapi.io/v1/images/tasks")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field\",\n  \"aspect_ratio\": \"16:9\",\n  \"resolution\": \"2K\"\n}")
  .asString();
```

```php Text to image
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.beatapi.io/v1/images/tasks', [
  'body' => '{
  "model": "nano-banana-2",
  "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
  "aspect_ratio": "16:9",
  "resolution": "2K"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Text to image
using RestSharp;

var client = new RestClient("https://api.beatapi.io/v1/images/tasks");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field\",\n  \"aspect_ratio\": \"16:9\",\n  \"resolution\": \"2K\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Text to image
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "model": "nano-banana-2",
  "prompt": "A minimalist product photo of a glass water bottle on wet black stone, soft rim light, shallow depth of field",
  "aspect_ratio": "16:9",
  "resolution": "2K"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.beatapi.io/v1/images/tasks")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Edit one image

**Request**

```json
{
  "model": "nano-banana-2",
  "prompt": "Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.",
  "aspect_ratio": "auto",
  "images": [
    "https://media.beatapi.io/samples/smart-bottle.png"
  ]
}
```

**Response**

```json
{
  "data": {
    "capability_id": "nano-banana-2",
    "capability_version": 1,
    "completed_at": null,
    "created_at": 1791993600,
    "error_code": null,
    "error_message": null,
    "id": "task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ",
    "media_type": "image",
    "model": "nano-banana-2",
    "object": "task",
    "output": null,
    "poll_after_seconds": 5,
    "request_id": "req_5f0c2a9e4b7d4e31",
    "stage": "queued",
    "status": "queued",
    "task_kind": "image",
    "updated_at": 1791993600
  }
}
```

**SDK Code**

```python Edit one image
import requests

url = "https://api.beatapi.io/v1/images/tasks"

payload = {
    "model": "nano-banana-2",
    "prompt": "Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.",
    "aspect_ratio": "auto",
    "images": ["https://media.beatapi.io/samples/smart-bottle.png"]
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Edit one image
const url = 'https://api.beatapi.io/v1/images/tasks';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"model":"nano-banana-2","prompt":"Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.","aspect_ratio":"auto","images":["https://media.beatapi.io/samples/smart-bottle.png"]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Edit one image
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.beatapi.io/v1/images/tasks"

	payload := strings.NewReader("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.\",\n  \"aspect_ratio\": \"auto\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\"\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Edit one image
require 'uri'
require 'net/http'

url = URI("https://api.beatapi.io/v1/images/tasks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.\",\n  \"aspect_ratio\": \"auto\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\"\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java Edit one image
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.beatapi.io/v1/images/tasks")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.\",\n  \"aspect_ratio\": \"auto\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\"\n  ]\n}")
  .asString();
```

```php Edit one image
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.beatapi.io/v1/images/tasks', [
  'body' => '{
  "model": "nano-banana-2",
  "prompt": "Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.",
  "aspect_ratio": "auto",
  "images": [
    "https://media.beatapi.io/samples/smart-bottle.png"
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Edit one image
using RestSharp;

var client = new RestClient("https://api.beatapi.io/v1/images/tasks");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.\",\n  \"aspect_ratio\": \"auto\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\"\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Edit one image
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "model": "nano-banana-2",
  "prompt": "Keep the bottle exactly as it is. Replace the background with a sunlit kitchen counter and add a soft reflection under the bottle.",
  "aspect_ratio": "auto",
  "images": ["https://media.beatapi.io/samples/smart-bottle.png"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.beatapi.io/v1/images/tasks")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Combine several images

**Request**

```json
{
  "model": "nano-banana-2",
  "prompt": "Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.",
  "aspect_ratio": "1:1",
  "images": [
    "https://media.beatapi.io/samples/smart-bottle.png",
    "https://media.beatapi.io/samples/neon-singer.png"
  ],
  "resolution": "1K"
}
```

**Response**

```json
{
  "data": {
    "capability_id": "nano-banana-2",
    "capability_version": 1,
    "completed_at": null,
    "created_at": 1791993600,
    "error_code": null,
    "error_message": null,
    "id": "task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ",
    "media_type": "image",
    "model": "nano-banana-2",
    "object": "task",
    "output": null,
    "poll_after_seconds": 5,
    "request_id": "req_5f0c2a9e4b7d4e31",
    "stage": "queued",
    "status": "queued",
    "task_kind": "image",
    "updated_at": 1791993600
  }
}
```

**SDK Code**

```python Combine several images
import requests

url = "https://api.beatapi.io/v1/images/tasks"

payload = {
    "model": "nano-banana-2",
    "prompt": "Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.",
    "aspect_ratio": "1:1",
    "images": ["https://media.beatapi.io/samples/smart-bottle.png", "https://media.beatapi.io/samples/neon-singer.png"],
    "resolution": "1K"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Combine several images
const url = 'https://api.beatapi.io/v1/images/tasks';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"model":"nano-banana-2","prompt":"Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.","aspect_ratio":"1:1","images":["https://media.beatapi.io/samples/smart-bottle.png","https://media.beatapi.io/samples/neon-singer.png"],"resolution":"1K"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Combine several images
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.beatapi.io/v1/images/tasks"

	payload := strings.NewReader("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.\",\n  \"aspect_ratio\": \"1:1\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\",\n    \"https://media.beatapi.io/samples/neon-singer.png\"\n  ],\n  \"resolution\": \"1K\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Combine several images
require 'uri'
require 'net/http'

url = URI("https://api.beatapi.io/v1/images/tasks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.\",\n  \"aspect_ratio\": \"1:1\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\",\n    \"https://media.beatapi.io/samples/neon-singer.png\"\n  ],\n  \"resolution\": \"1K\"\n}"

response = http.request(request)
puts response.read_body
```

```java Combine several images
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.beatapi.io/v1/images/tasks")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.\",\n  \"aspect_ratio\": \"1:1\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\",\n    \"https://media.beatapi.io/samples/neon-singer.png\"\n  ],\n  \"resolution\": \"1K\"\n}")
  .asString();
```

```php Combine several images
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.beatapi.io/v1/images/tasks', [
  'body' => '{
  "model": "nano-banana-2",
  "prompt": "Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.",
  "aspect_ratio": "1:1",
  "images": [
    "https://media.beatapi.io/samples/smart-bottle.png",
    "https://media.beatapi.io/samples/neon-singer.png"
  ],
  "resolution": "1K"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Combine several images
using RestSharp;

var client = new RestClient("https://api.beatapi.io/v1/images/tasks");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.\",\n  \"aspect_ratio\": \"1:1\",\n  \"images\": [\n    \"https://media.beatapi.io/samples/smart-bottle.png\",\n    \"https://media.beatapi.io/samples/neon-singer.png\"\n  ],\n  \"resolution\": \"1K\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Combine several images
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "model": "nano-banana-2",
  "prompt": "Place the bottle from the first image on a neon-lit stage like the one in the second image. Keep the bottle label readable.",
  "aspect_ratio": "1:1",
  "images": ["https://media.beatapi.io/samples/smart-bottle.png", "https://media.beatapi.io/samples/neon-singer.png"],
  "resolution": "1K"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.beatapi.io/v1/images/tasks")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### 4K JPEG poster

**Request**

```json
{
  "model": "nano-banana-2",
  "prompt": "A retro travel poster of a coastal city at sunset, bold flat colours, large title text \"SEASIDE\" at the top",
  "aspect_ratio": "9:16",
  "output_format": "jpeg",
  "resolution": "4K"
}
```

**Response**

```json
{
  "data": {
    "capability_id": "nano-banana-2",
    "capability_version": 1,
    "completed_at": null,
    "created_at": 1791993600,
    "error_code": null,
    "error_message": null,
    "id": "task_7Qm2Xc9LpR4vT8sKa1Nf6YbH3eWdJ0uZ",
    "media_type": "image",
    "model": "nano-banana-2",
    "object": "task",
    "output": null,
    "poll_after_seconds": 5,
    "request_id": "req_5f0c2a9e4b7d4e31",
    "stage": "queued",
    "status": "queued",
    "task_kind": "image",
    "updated_at": 1791993600
  }
}
```

**SDK Code**

```python 4K JPEG poster
import requests

url = "https://api.beatapi.io/v1/images/tasks"

payload = {
    "model": "nano-banana-2",
    "prompt": "A retro travel poster of a coastal city at sunset, bold flat colours, large title text \"SEASIDE\" at the top",
    "aspect_ratio": "9:16",
    "output_format": "jpeg",
    "resolution": "4K"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript 4K JPEG poster
const url = 'https://api.beatapi.io/v1/images/tasks';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"model":"nano-banana-2","prompt":"A retro travel poster of a coastal city at sunset, bold flat colours, large title text \"SEASIDE\" at the top","aspect_ratio":"9:16","output_format":"jpeg","resolution":"4K"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go 4K JPEG poster
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.beatapi.io/v1/images/tasks"

	payload := strings.NewReader("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A retro travel poster of a coastal city at sunset, bold flat colours, large title text \\\"SEASIDE\\\" at the top\",\n  \"aspect_ratio\": \"9:16\",\n  \"output_format\": \"jpeg\",\n  \"resolution\": \"4K\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby 4K JPEG poster
require 'uri'
require 'net/http'

url = URI("https://api.beatapi.io/v1/images/tasks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A retro travel poster of a coastal city at sunset, bold flat colours, large title text \\\"SEASIDE\\\" at the top\",\n  \"aspect_ratio\": \"9:16\",\n  \"output_format\": \"jpeg\",\n  \"resolution\": \"4K\"\n}"

response = http.request(request)
puts response.read_body
```

```java 4K JPEG poster
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.beatapi.io/v1/images/tasks")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A retro travel poster of a coastal city at sunset, bold flat colours, large title text \\\"SEASIDE\\\" at the top\",\n  \"aspect_ratio\": \"9:16\",\n  \"output_format\": \"jpeg\",\n  \"resolution\": \"4K\"\n}")
  .asString();
```

```php 4K JPEG poster
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.beatapi.io/v1/images/tasks', [
  'body' => '{
  "model": "nano-banana-2",
  "prompt": "A retro travel poster of a coastal city at sunset, bold flat colours, large title text \\"SEASIDE\\" at the top",
  "aspect_ratio": "9:16",
  "output_format": "jpeg",
  "resolution": "4K"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp 4K JPEG poster
using RestSharp;

var client = new RestClient("https://api.beatapi.io/v1/images/tasks");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"model\": \"nano-banana-2\",\n  \"prompt\": \"A retro travel poster of a coastal city at sunset, bold flat colours, large title text \\\"SEASIDE\\\" at the top\",\n  \"aspect_ratio\": \"9:16\",\n  \"output_format\": \"jpeg\",\n  \"resolution\": \"4K\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift 4K JPEG poster
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "model": "nano-banana-2",
  "prompt": "A retro travel poster of a coastal city at sunset, bold flat colours, large title text \"SEASIDE\" at the top",
  "aspect_ratio": "9:16",
  "output_format": "jpeg",
  "resolution": "4K"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.beatapi.io/v1/images/tasks")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```