<!-- Generated from openapi/v2.json (md/images.md) — do not edit. Run: npm run generate -->

[Interactive reference](https://api-docs.syllaby.io/) · [OpenAPI 3.1 spec](https://api-docs.syllaby.io/openapi/v2.json) · [Docs index (llms.txt)](https://api-docs.syllaby.io/llms.txt)

# Images

Standalone image generation: social graphics, thumbnails and ads from a prompt — pick an aspect ratio such as 4:5, pass a logo or product shot as a reference image, and poll each image to a finished url. Charged per image from your plan credits, refunded on failure.

## GET /images/options

**Get image options** (operationId: `getImageOptions`)

**Step 1 of 4 · Read the options.**

Everything `POST /images` accepts in one call, like `GET /faceless/options` for videos. Settings differ per model, so each entry in `models` lists the `aspect_ratio`, `resolution` and `quality` options it offers (e.g. `4:5`), how many reference images it takes (`max_references`) and its pricing; a `null` setting is not supported by that model. `quantity`, `prompt` and `reference` hold the limits shared by every model. Anything not listed is rejected with `422`.

→ **Next:** Step 2 — *(optional)* estimate the cost with `GET /images/estimate`, or skip to Step 3 — generate with `POST /images`.

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "models": [
      {
        "id": 12,
        "slug": "nano-banana-2",
        "name": "Nano Banana 2",
        "description": "Fast, high-quality images with strong text rendering.",
        "aspect_ratio": {
          "options": [
            "1:1",
            "4:5",
            "9:16",
            "16:9"
          ],
          "default": "16:9"
        },
        "resolution": {
          "options": [
            "1K",
            "2K",
            "4K"
          ],
          "default": "2K"
        },
        "quality": null,
        "max_references": 4,
        "pricing": [
          {
            "resolution": "1K",
            "credits_per_image": 2
          },
          {
            "resolution": "2K",
            "credits_per_image": 3
          },
          {
            "resolution": "4K",
            "credits_per_image": 4
          }
        ]
      }
    ],
    "quantity": {
      "min": 1,
      "max": 3
    },
    "prompt": {
      "max_length": 2500
    },
    "reference": {
      "types": [
        "jpg",
        "jpeg",
        "png",
        "webp"
      ],
      "max_size_mb": 10
    }
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `429` — Rate limit exceeded — requests are limited per wallet (each Agency workspace has its own allowance; an owner's other keys share one; 30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /images/estimate

**Estimate image credits** (operationId: `estimateImageCredits`)

**Step 2 of 4 · Estimate** *(optional)*.

Dry-run credit estimate for `POST /images`, compared against your available balance. Charges nothing.

**The estimate equals the charge.** Omitted settings fall back to the model's defaults exactly as the create call applies them, and the same pricing runs: the model's per-image price for the chosen `resolution` or `quality`, plus any reference images beyond the model's free allowance, times `quantity`.

Settings are **flat query keys** (`?model=nano-banana-2&resolution=4K&quantity=2&references=1`). `references` is a count — only how many reference images you send affects the price.

Responds with `422` when the `model` is unknown or inactive.

→ **Next:** Step 3 — generate with `POST /images`.

### Parameters

- `model` (query, string, required) — Model `slug` from `GET /images/options`. Example: `nano-banana-2`
- `quantity` (query, integer) — Images per request, 1–3 (default 1). Example: `2`
- `aspect_ratio` (query, string) — Aspect ratio you will send. Does not change the price on any current model. Example: `4:5`
- `resolution` (query, string) — Resolution you will send, for models with resolution tiers. Defaults to the model's `resolution` default. Example: `4K`
- `quality` (query, string) — Quality you will send, for models with quality tiers (e.g. `high` on `gpt-image-2`). Defaults to the model's `quality` default; ignored by models without quality tiers. Example: `high`
- `references` (query, integer) — How many reference images you will send (default 0). Some models charge for references beyond a free allowance. Example: `1`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "required": 8,
    "available": 4960,
    "sufficient": true
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `422` — The `model` is unknown or inactive.
- `429` — Rate limit exceeded — requests are limited per wallet (each Agency workspace has its own allowance; an owner's other keys share one; 30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /images

**List images** (operationId: `listImages`)

Lists your generated images, newest first. Paginated. Poll individual in-flight images with `GET /images/{id}`.

### Parameters

- `page` (query, integer) — Page number to fetch. Example: `1`
- `per_page` (query, integer) — Items per page (default 12, max 50). Example: `12`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": [
    {
      "id": 501,
      "status": "success",
      "prompt": "Bold launch-day graphic for a SaaS product, clean layout, brand logo top-left",
      "model": "nano-banana-2",
      "url": "https://cdn.syllaby.dev/images/501/image.png",
      "error": null,
      "created_at": "2026-10-16T12:00:00Z",
      "updated_at": "2026-10-16T12:00:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 12,
    "total": 1
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `429` — Rate limit exceeded — requests are limited per wallet (each Agency workspace has its own allowance; an owner's other keys share one; 30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /images

**Generate images** (operationId: `createImages`)

**Step 3 of 4 · Generate.**

💳 **Charges credits per image** at the model's price (see `pricing` on `GET /images/options`, or get the exact figure from `GET /images/estimate`), from your plan balance. The response `meta.credits` reports the exact charge and your remaining balance.

Generates 1–3 standalone images from `prompt` — social graphics, thumbnails, ads. Set `modifiers.aspect_ratio` (e.g. `4:5` for a feed post) and pass a logo or product shot as a public url in `modifiers.reference`. Returns `202` immediately with one **`processing`** entry per image.

**To track progress:** poll `GET /images/{id}` with each `id` from this response, every 3–5 seconds. If an image fails, its charge is refunded automatically.

Insufficient balance is rejected with `402` before anything is created. An unknown model, a setting the model does not offer, or a reference url that cannot be downloaded is a `422` with the field named under `errors` — nothing is charged. The example's logo url is a placeholder: send a public image url of your own.

→ **Next:** Step 4 — poll `GET /images/{id}` with each `id` from this response.

### Request body

Required. The model, prompt, and optional settings and reference images.

- `model` (string, required) — Model `slug` from `GET /images/options`.
- `prompt` (string, required) — What the image should show — up to 2,500 characters.
- `quantity` (integer) — How many images to generate, 1–3 (default 1). Each image is charged.
- `modifiers` (object) — Optional generation settings. Every value is validated against the chosen model's entry in `GET /images/options`; omitted values use the model default.
  - `aspect_ratio` (string) — One of the model's `aspect_ratio` options — e.g. `4:5` for a feed post, `1:1`, `9:16`.
  - `resolution` (string) — One of the model's `resolution` options, when it has them (e.g. `2K`).
  - `quality` (string) — One of the model's `quality` options, when it has them.
  - `reference` (array of string) — Public jpg/png/webp image urls (max 10 MB each) used as reference images — a logo, a product shot, a style sample. Up to the model's `max_references` (`0` means the model takes none). Urls are fetched and checked before anything is charged.

Example request:

```json
{
  "model": "nano-banana-2",
  "prompt": "Bold launch-day graphic for a SaaS product, clean layout, brand logo top-left",
  "quantity": 1,
  "modifiers": {
    "aspect_ratio": "4:5",
    "resolution": "2K",
    "reference": [
      "https://example.com/brand/logo.png"
    ]
  }
}
```

### Response `202`

```json
{
  "message": "Success.",
  "status": 202,
  "data": [
    {
      "id": 501,
      "status": "processing",
      "prompt": "Bold launch-day graphic for a SaaS product, clean layout, brand logo top-left",
      "model": "nano-banana-2",
      "url": null,
      "error": null,
      "created_at": "2026-10-16T12:00:00Z",
      "updated_at": "2026-10-16T12:00:00Z"
    }
  ],
  "meta": {
    "credits": {
      "cost": 3,
      "remaining": 97
    }
  }
}
```

### Errors

- `401` — Unauthenticated
- `402` — Insufficient credits — `error.required` and `error.available` carry the figures. Nothing is created.
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per wallet (each Agency workspace has its own allowance; an owner's other keys share one; 30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /images/{id}

**Get an image** (operationId: `getImage`)

**Step 4 of 4 · Track progress.**

Returns a single generated image you own — this is the polling endpoint. `status` is the field to branch on: `processing` → terminal `success` (`url` set) or `failed` (`error` set, charge refunded).

Responds with a uniform `404` when the id does not exist, belongs to another account, is not a generated image, or is not numeric.

→ **Done** when `data.status` is `success` — the image is at `data.url`. On `failed`, the charge is auto-refunded.

### Parameters

- `id` (path, integer, required) — Identifier of the image — an `id` from the create response. Example: `501`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 501,
    "status": "success",
    "prompt": "Bold launch-day graphic for a SaaS product, clean layout, brand logo top-left",
    "model": "nano-banana-2",
    "url": "https://cdn.syllaby.dev/images/501/image.png",
    "error": null,
    "created_at": "2026-10-16T12:00:00Z",
    "updated_at": "2026-10-16T12:00:00Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `404` — Not found — returned uniformly when the id does not exist, belongs to another account, is not a generated image, or is not numeric.
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `429` — Rate limit exceeded — requests are limited per wallet (each Agency workspace has its own allowance; an owner's other keys share one; 30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).
