---
title: "Avatar · Nano Banana Pro"
description: "Create a reusable character identity with an original full-body image and a matching three-panel reference sheet. One creation quote includes both stages and the individual panel crops. The saved avatar_id works with supported image, scene, video, UGC, and lipsync endpoints."
url: https://docs.roasos.com/models/avatar/nano-banana-pro
---

# Avatar · Nano Banana Pro

<Callout>
  Family 

  `avatar`

   · model 

  `nano-banana-pro`

   · catalog 

  `model-catalog@2026-10-06.v5`

   · pricing 

  `customer-pricing@2026-11-05.v15`

  .
</Callout>

## Endpoint

```http
POST https://api.roasos.com/v1/avatar/nano-banana-pro
```

Send your API key as a bearer token. The key needs the scope `api:write`. Every paid request also needs an `Idempotency-Key` header.

The envelope fields `quote_only`, `quote_id`, `webhook_url` and `wait_timeout_seconds` behave the same on each model endpoint. Refer to [Jobs](/concepts/jobs) and [Quotes and credits](/concepts/quotes-and-credits).

## Good at

Use this endpoint to create a consistent person for a campaign. Two sequential Nano Banana Pro calls generate a 4K vertical 2:3 original full-body image, then a matching 4K landscape 16:9 sheet using that original image as the reference. The original and sheet use a pure white seamless studio background. The sheet shows full-body front, full-body back, and a close-up frontal face. Realistic characters use natural facial texture and soft dimensional studio light; supplied character details and non-photographic styles are preserved. Cropping derives the individual views without additional image generation calls. Use manual mode with a description to invent a character; use reference mode with 1–5 photos to anchor the character's appearance. Supported image, scene, and reference-video models use the sheet as the identity reference. Kling identity elements pair the frontal crop with the sheet. Lipsync uses a single frontal crop. Provider face-reference restrictions still apply.

## Pricing

The unit is one **avatar**. Pricing key: `nano-banana-pro`.

| Request                 | Credits |
| ----------------------- | ------- |
| `4K_original_and_sheet` | 20      |

The credits come from the roasOS pricing table. No other field changes the price. To get the exact price of your request, send it with `quote_only: true` first.

## Modes

* `manual`
* `reference`

roasOS selects the mode from the media fields of your request. You do not send a mode.

Provider endpoints: `fal-ai/nano-banana-pro` · `fal-ai/nano-banana-pro/edit`.

## Request fields

| Field                  | Type      | Required | Default  | Rule                              |
| ---------------------- | --------- | -------- | -------- | --------------------------------- |
| `description`          | string    | yes      | —        | ≥ 1 character; ≤ 1,000 characters |
| `mode`                 | enum      | no       | `manual` | `manual`, `reference`             |
| `name`                 | string    | no       | —        | ≥ 1 character; ≤ 200 characters   |
| `quote_id`             | string    | no       | —        | UUID                              |
| `quote_only`           | boolean   | no       | `false`  | —                                 |
| `reference_images`     | string\[] | no       | —        | 1–5 items; only in `reference`    |
| `wait_timeout_seconds` | integer   | no       | `0`      | 0–60                              |
| `webhook_url`          | string    | no       | —        | https URL; ≤ 4,096 characters     |

The schema is strict. A field that is not in this table gets `400 unsupported_option`. The field names are the roasOS role names. One role has one name in every family.

## Media

This endpoint accepts these media roles:

* `reference_images` — the source images the generation is built from

| Role               | Items | Maximum size | Content types                           | Duration |
| ------------------ | ----- | ------------ | --------------------------------------- | -------- |
| `reference_images` | ≤ 5   | 10 MB        | `image/jpeg`, `image/png`, `image/webp` | —        |

Every other role is refused: `avatar_id`, `elements`, `product_id`, `product_images`, `references`, `image_url`, `end_image_url`, `scene_id`, `scene_image`, `video_urls`, `video_url`, `audio_urls`, `audio_url`, `voice_id`. A refused role gets `400 unsupported_option`.

<Callout>
  Each media URL must be an immutable object on 

  `https://cdn.roasos.com`

   that your workspace owns. Any other URL gets 

  `400 reference_media_not_workspace_owned`

  . Refer to 

  [Reference media and uploads](/concepts/reference-media)

   to learn how to put a file there.
</Callout>

## Prompt

Describe one adult character in description, up to 1,000 characters. The worker adds the original-image and three-panel sheet instructions; you do not need to request separate photos or write image tags.

* Describe appearance, build, hair, skin tone, wardrobe, and visual style.
* Specify the wardrobe you want preserved across the sheet.
* Keep scene and product direction for later image or scene requests.
* In reference mode, let the supplied photos anchor identity and describe details they do not show.

## Not exposed

These catalog entries describe omitted inputs, pinned values and source constraints. They do not imply that the provider accepts every listed field.

* `output_format` — pinned to `jpeg`.
* `safety_tolerance` — pinned to `4`.
* `enable_web_search` — pinned to `false`.
* `limit_generations` — pinned to `true`.
* `system_prompt` — not accepted by this public endpoint.
* `sync_mode` — not accepted by this public endpoint.
* `aspect_ratio` — pinned to `2:3 original image; 16:9 reference sheet`.
* `resolution` — pinned to `4K`.
* `num_images` — pinned to `1 per stage; 2 sequential stages`.
* `seed` — pinned to `derived from the Idempotency-Key`.

## Result

The result kind is `avatar`. Always present: `avatar_id`, `images`. Present when the run makes it: `full_body_url`, `portrait_url`, `reference_sheet`.

```json
{
  "data": {
    "id": "gen_…",
    "kind": "avatar",
    "model": "nano-banana-pro",
    "status": "completed",
    "result": {
      "avatar_id": "00000000-0000-4000-8000-000000000000",
      "full_body_url": "https://cdn.roasos.com/workspaces/ws_01/full_body_url",
      "images": {},
      "portrait_url": "https://cdn.roasos.com/workspaces/ws_01/portrait_url",
      "reference_sheet": "https://cdn.roasos.com/workspaces/ws_01/reference_sheet"
    }
  }
}
```

The files are immutable objects on `https://cdn.roasos.com`. A result URL is a valid media input for your next request.

## Example

The three steps are always the same: quote, run, poll.

<CodeBlockTabs defaultValue="cURL">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="cURL">
      cURL
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="JavaScript">
      JavaScript
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Python">
      Python
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="cURL">
    ```bash
    # 1. Quote. Nothing is spent.
    curl -X POST https://api.roasos.com/v1/avatar/nano-banana-pro \
      -H "Authorization: Bearer roasos_sk_live_..." \
      -H "Idempotency-Key: 0f1c9e2a-nano-banana-pro-0001" \
      -H "Content-Type: application/json" \
      -d '{"quote_only":true,"description":"A woman in her late twenties, shoulder-length dark hair, plain grey t-shirt, neutral expression."}'
    # 200 { "data": { "kind": "quote", "quote_id": "q_7Hk...", "credits": 20, "expires_at": "...",
    #        "confirmation": { "model": "nano-banana-pro", "example": "4K_original_and_sheet" } } }

    # 2. Run. Same body, same Idempotency-Key, quote_id in place of quote_only.
    curl -X POST https://api.roasos.com/v1/avatar/nano-banana-pro \
      -H "Authorization: Bearer roasos_sk_live_..." \
      -H "Idempotency-Key: 0f1c9e2a-nano-banana-pro-0001" \
      -H "Content-Type: application/json" \
      -d '{"quote_id":"q_7Hk...","description":"A woman in her late twenties, shoulder-length dark hair, plain grey t-shirt, neutral expression."}'
    # 201 { "data": { "id": "gen_...", "status": "queued", "credits_reserved": 20 } }

    # 3. Poll, then read the result.
    curl https://api.roasos.com/v1/jobs/gen_.../status -H "Authorization: Bearer roasos_sk_live_..."
    curl https://api.roasos.com/v1/jobs/gen_.../result -H "Authorization: Bearer roasos_sk_live_..."
    ```
  </CodeBlockTab>

  <CodeBlockTab value="JavaScript">
    ```javascript
    const key = process.env.ROASOS_API_KEY;
    const url = "https://api.roasos.com/v1/avatar/nano-banana-pro";
    const idempotencyKey = "0f1c9e2a-nano-banana-pro-0001";
    const input = {
      "description": "A woman in her late twenties, shoulder-length dark hair, plain grey t-shirt, neutral expression."
    };

    const post = (payload) =>
      fetch(url, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${key}`,
          "Idempotency-Key": idempotencyKey,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      }).then((response) => response.json());

    // 1. Quote. Nothing is spent.
    const quote = await post({ quote_only: true, ...input });
    console.log(quote.data.credits);

    // 2. Run.
    const job = await post({ quote_id: quote.data.quote_id, ...input });

    // 3. Poll, then read the result.
    let status = "queued";
    while (status !== "completed" && status !== "failed" && status !== "canceled") {
      await new Promise((done) => setTimeout(done, 3000));
      const poll = await fetch(`${url.split("/v1")[0]}/v1/jobs/${job.data.id}/status`, {
        headers: { Authorization: `Bearer ${key}` },
      }).then((response) => response.json());
      status = poll.data.status;
    }

    const result = await fetch(`${url.split("/v1")[0]}/v1/jobs/${job.data.id}/result`, {
      headers: { Authorization: `Bearer ${key}` },
    }).then((response) => response.json());
    console.log(result.data.result);
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Python">
    ```python
    import os, time, requests

    key = os.environ["ROASOS_API_KEY"]
    url = "https://api.roasos.com/v1/avatar/nano-banana-pro"
    headers = {
        "Authorization": f"Bearer {key}",
        "Idempotency-Key": "0f1c9e2a-nano-banana-pro-0001",
    }
    payload = {
        "description": "A woman in her late twenties, shoulder-length dark hair, plain grey t-shirt, neutral expression."
    }

    # 1. Quote. Nothing is spent.
    quote = requests.post(url, headers=headers, json={"quote_only": True, **payload}).json()
    print(quote["data"]["credits"])

    # 2. Run.
    job = requests.post(url, headers=headers, json={"quote_id": quote["data"]["quote_id"], **payload}).json()
    job_id = job["data"]["id"]

    # 3. Poll, then read the result.
    jobs = "https://api.roasos.com/v1/jobs"
    status = "queued"
    while status not in ("completed", "failed", "canceled"):
        time.sleep(3)
        status = requests.get(f"{jobs}/{job_id}/status", headers=headers).json()["data"]["status"]

    result = requests.get(f"{jobs}/{job_id}/result", headers=headers).json()
    print(result["data"]["result"])
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Today

POST /v1/avatar/nano-banana-pro is the creation endpoint. Request quote\_only first, then submit the same fields with quote\_id and the same Idempotency-Key. The response is an avatar job. Its completed result exposes avatar\_id, reference\_sheet, portrait\_url, and full\_body\_url. The full\_body\_url is the original image; portrait\_url is the frontal face crop. Poll GET /v1/avatars/\{avatar\_id} until the saved avatar is available; publication may finish shortly after rendering. GET /v1/avatars lists saved identities and DELETE /v1/avatars/\{id} removes one. One quote covers both 10-credit generation stages, for 20 credits total. There is no separate sheet purchase step. Existing avatars remain usable with their original reference images.

## See also

* [`llms.txt` for this model](https://docs.roasos.com/models/avatar/nano-banana-pro/llms.txt) — the same facts as one plain-text file for an agent.
* `GET https://api.roasos.com/v1/catalog/nano-banana-pro` — the machine-readable form: enums, defaults, limits and prices. Refer to the [API reference](/api-reference/overview).
* [Jobs](/concepts/jobs) — how to poll a job and read its result.
* Provider endpoints: `fal-ai/nano-banana-pro` · `fal-ai/nano-banana-pro/edit`.
