> For the complete documentation index, see [llms.txt](https://docs.qolaba.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.qolaba.ai/api-platform/image-generation.md).

# Image Generation

Generate images from text prompts or source images using any supported model. Supports both **synchronous** (wait for result) and **asynchronous** (poll for result) processing modes.

## Image Generation API

**Endpoint:** `POST /api/v1/images/generate`

**Headers**

| Name          | Value              |
| ------------- | ------------------ |
| Content-Type  | `application/json` |
| Authorization | `Bearer <token>`   |

### Quick Start

```bash
curl -X POST https://api.platform.qolaba.ai/api/v1/images/generate \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "vertex/nano-banana-flash",
    "prompt": "A photorealistic cat sitting on the Eiffel Tower at sunset",
    "aspect_ratio": "16:9"
  }'
```

**Response `200 OK`:**

```json
{
  "task_id": "task_1705312200000_abc123",
  "prompt": "A photorealistic cat sitting on the Eiffel Tower at sunset",
  "images": [
    {
      "url": "https://storage.example.com/image.png",
      "width": 1280,
      "height": 720,
      "content_type": "image/png"
    }
  ],
  "usage": {
    "images_generated": 1,
    "cost_usd": 0.03,
    "cost_credits": 6
  }
}
```

***

### Processing Modes

The `celery` field controls how the request is processed.

| Mode                      | `celery` | HTTP Status    | Description                                                                                                               |
| ------------------------- | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Synchronous** (default) | `false`  | `200 OK`       | Waits for generation to complete and returns images directly. Best for low-latency use cases.                             |
| **Asynchronous**          | `true`   | `202 Accepted` | Returns a `task_id` immediately. Generation happens in the background. Poll `GET /api/v1/tasks/{task_id}` for the result. |

**When to use async (`celery: true`):**

* Generating large batches
* High-resolution images with longer generation times
* Background jobs / queue-based workflows
* Avoiding client timeout issues

***

### Request Body

```json
{
  "model": "string (required)",
  "prompt": "string",
  "celery": false
}
```

#### Core Fields

| Field    | Type                | Required | Description                                                                          |
| -------- | ------------------- | -------- | ------------------------------------------------------------------------------------ |
| `model`  | `string`            | Yes      | Model ID. See Supported Models.                                                      |
| `prompt` | `string` (max 4000) | Yes\*    | Text description of the image. \*Optional for image-to-image models (e.g. BiRefNet). |
| `celery` | `boolean`           | No       | Processing mode. `false` = sync (default), `true` = async.                           |

#### Media Inputs

| Field              | Type                                               | Description                                                                      |
| ------------------ | -------------------------------------------------- | -------------------------------------------------------------------------------- |
| `image_urls`       | `string[]`                                         | Source image URLs for image-to-image or conversational editing.                  |
| `reference_images` | `{ url: string, description?: string }[]` (max 14) | Reference images for multi-reference consistency (character, product, or style). |
| `mask_url`         | `string (URL)`                                     | Black/white mask indicating the inpainting area (FLUX Inpainting only).          |

#### Size & Resolution

| Field          | Type     | Description                                                                                                                       |
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `aspect_ratio` | `string` | Output aspect ratio. See valid values below. Not all models support all ratios.                                                   |
| `quality`      | `string` | Resolution/quality preset. Model-specific values — e.g. `"2K"`, `"4K"` for Vertex; `"low"`, `"medium"`, `"high"` for GPT Image 2. |

**Supported `aspect_ratio` 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
```

#### Generation Parameters

| Field                 | Type                | Range | Description                                                        |
| --------------------- | ------------------- | ----- | ------------------------------------------------------------------ |
| `num_images`          | `integer`           | 1–10  | Number of images to generate.                                      |
| `seed`                | `integer`           | —     | Random seed for reproducible results.                              |
| `num_inference_steps` | `integer`           | 1–150 | Denoising steps (FAL models). More steps = higher quality, slower. |
| `guidance_scale`      | `number`            | 0–30  | How closely to follow the prompt (FAL models).                     |
| `temperature`         | `number`            | 0–2   | Sampling temperature for creativity (Vertex models).               |
| `negative_prompt`     | `string` (max 2000) | —     | Text describing elements to exclude from the image.                |

***

### Response Schemas

#### 200 OK — Synchronous Result

Returned when `celery: false` (default). Generation is complete and images are included.

```json
{
  "task_id": "task_1705312200000_abc123",
  "prompt": "A cat on the Eiffel Tower",
  "seed": 42,
  "images": [
    {
      "url": "https://storage.example.com/image.png",
      "width": 1024,
      "height": 1024,
      "content_type": "image/png"
    }
  ],
  "usage": {
    "images_generated": 1,
    "cost_usd": 0.025,
    "cost_credits": 7
  }
}
```

| Field                    | Type            | Description                                                       |
| ------------------------ | --------------- | ----------------------------------------------------------------- |
| `task_id`                | `string`        | Unique identifier for this generation task.                       |
| `prompt`                 | `string`        | The prompt used for generation (echoed back).                     |
| `seed`                   | `integer`       | Seed used (if applicable). Pass back to reproduce the same image. |
| `images`                 | `ImageOutput[]` | Array of generated images.                                        |
| `images[].url`           | `string`        | Public URL of the generated image.                                |
| `images[].width`         | `integer`       | Image width in pixels.                                            |
| `images[].height`        | `integer`       | Image height in pixels.                                           |
| `images[].content_type`  | `string`        | MIME type, e.g. `image/png`.                                      |
| `usage`                  | `object`        | Billing information.                                              |
| `usage.images_generated` | `integer`       | Number of images generated.                                       |
| `usage.cost_usd`         | `number`        | Cost in USD.                                                      |
| `usage.cost_credits`     | `number`        | Cost in platform credits (1 credit = $0.005 USD).                 |

#### 202 Accepted — Async Task Created

Returned when `celery: true`. Poll the task endpoint to retrieve the result.

```json
{
  "task_id": "task_1705312200000_abc123",
  "status": "PENDING",
  "type": "image"
}
```

| Field     | Type     | Description                                     |
| --------- | -------- | ----------------------------------------------- |
| `task_id` | `string` | Use this to poll `GET /api/v1/tasks/{task_id}`. |
| `status`  | `string` | Initial status: always `PENDING`.               |
| `type`    | `string` | Always `image` for image generation tasks.      |

#### Error Responses

| Status | `error` field           | Description                                                |
| ------ | ----------------------- | ---------------------------------------------------------- |
| `400`  | `Bad Request`           | Invalid parameters or unsupported model/field combination. |
| `429`  | `Rate Limited`          | Too many requests. See `retryAfter` field.                 |
| `500`  | `Internal Server Error` | Unexpected server-side failure.                            |

```json
{
  "error": "Bad Request",
  "message": "aspect_ratio '21:9' is not supported by model 'vertex/nano-banana-pro'",
  "retryAfter": null
}
```

***

### Code Examples

#### JavaScript / TypeScript

```typescript
// Synchronous (default)
const response = await fetch('https://api.platform.qolaba.ai/api/v1/images/generate', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.API_KEY!,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'vertex/nano-banana-flash',
    prompt: 'A photorealistic cat sitting on the Eiffel Tower at sunset',
    aspect_ratio: '16:9',
    num_images: 1
  }),
});

const result = await response.json();
console.log(result.images[0].url);
```

```typescript
// Asynchronous
const initRes = await fetch('https://api.platform.qolaba.ai/api/v1/images/generate', {
  method: 'POST',
  headers: { 'X-API-Key': process.env.API_KEY!, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: 'vertex/nano-banana-flash',
    prompt: 'A highly detailed oil painting of a dragon',
    celery: true,
  }),
});

const { task_id } = await initRes.json(); // 202 Accepted

// Poll until done
const result = await pollTask(task_id);
```

#### cURL

```bash
# Sync - wait for result
curl -X POST https://api.platform.qolaba.ai/api/v1/images/generate \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "vertex/nano-banana-flash",
    "prompt": "A product photo of wireless headphones",
    "quality": "high",
    "background": "transparent"
  }'

# Async - fire and forget
curl -X POST https://api.platform.qolaba.ai/api/v1/images/generate \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "vertex/nano-banana-flash",
    "prompt": "Ultra-detailed fantasy landscape",
    "celery": true
  }'
```

***

### Async Polling Flow

When using `celery: true`, poll the task endpoint until `status` is `SUCCESS` or `FAILED`.

#### Poll Endpoint

```
GET /api/v1/tasks/{task_id}
```

#### Task Status Values

| Status       | Description                                               |
| ------------ | --------------------------------------------------------- |
| `PENDING`    | Task is queued, not yet started.                          |
| `PROCESSING` | Generation is in progress.                                |
| `SUCCESS`    | Complete. `output` field contains the images.             |
| `FAILED`     | Generation failed. See `error` and `error_detail` fields. |

#### Polling Example

```typescript
async function pollTask(taskId: string, intervalMs = 2000, timeoutMs = 300000) {
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const res = await fetch(`https://api.platform.qolaba.ai/api/v1/tasks/${taskId}`, {
      headers: { 'X-API-Key': process.env.API_KEY! },
    });
    const task = await res.json();

    if (task.status === 'SUCCESS') {
      return task.output; // ImageGenerationResponse
    }

    if (task.status === 'FAILED') {
      throw new Error(`Task failed: ${task.error_detail?.message ?? task.error}`);
    }

    await new Promise(resolve => setTimeout(resolve, intervalMs));
  }

  throw new Error('Task polling timed out');
}
```

#### Task Response Shape

```json
{
  "task_id": "task_1705312200000_abc123",
  "status": "SUCCESS",
  "type": "image",
  "output": {
    "images": [{ "url": "...", "width": 1024, "height": 1024 }],
    "prompt": "...",
    "usage": { "images_generated": 1, "cost_usd": 0.06, "cost_credits": 12 }
  },
  "time_required_ms": 8543,
  "logs": [
    { "timestamp": "2024-01-15T10:30:00Z", "message": "Processing started" },
    { "timestamp": "2024-01-15T10:30:08Z", "message": "Generation complete" }
  ]
}
```
