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

# Image Generation

OpenRouter supports image generation via the [Chat Completions](/docs/api/api-reference/chat/send-chat-completion-request) and [Responses](/docs/api/reference/responses/overview) endpoints. You can find the supported models, their capabilities, and pricing by filtering our [model list by image output](https://openrouter.ai/models?output_modalities=image).

## Model Discovery

You can find image generation models in several ways:

### Via the API

Use the `output_modalities` query parameter on the [Models API](/docs/api-reference/models/get-models) to programmatically discover image generation models:

```bash
# List only image generation models
curl "https://openrouter.ai/api/v1/models?output_modalities=image"

# List models that support both text and image output
curl "https://openrouter.ai/api/v1/models?output_modalities=text,image"
```

See [Models - Query Parameters](/docs/guides/overview/models#query-parameters) for the full list of supported modality values.

### On the Models Page

Visit the [Models page](/models) and filter by output modalities to find models capable of image generation. Look for models that list `"image"` in their output modalities.

### In the Chatroom

When using the [Chatroom](/chat), click the **Image** button to automatically filter and select models with image generation capabilities. If no image-capable model is active, you'll be prompted to add one.

## API Usage

To generate images, send a request to the `/api/v1/chat/completions` endpoint with the `modalities` parameter. The value depends on the model's capabilities:

* **Models that output both text and images** (e.g., Gemini): Use `modalities: ["image", "text"]`
* **Models that only output images** (e.g., Sourceful, Flux): Use `modalities: ["image"]`

### Basic Image Generation

```typescript title="TypeScript SDK"
import { OpenRouter } from '@openrouter/sdk';

const openRouter = new OpenRouter({
  apiKey: '{{API_KEY_REF}}',
});

const result = await openRouter.chat.send({
  model: '{{MODEL}}',
  messages: [
    {
      role: 'user',
      content: 'Generate a beautiful sunset over mountains',
    },
  ],
  modalities: ['image', 'text'],
  stream: false,
});

// The generated image will be in the assistant message
if (result.choices) {
  const message = result.choices[0].message;
  if (message.images) {
    message.images.forEach((image, index) => {
      const imageUrl = image.imageUrl.url; // Base64 data URL
      console.log(`Generated image ${index + 1}: ${imageUrl.substring(0, 50)}...`);
    });
  }
}
```

```python
import requests
import json

url = "https://openrouter.ai/api/v1/chat/completions"
headers = {
    "Authorization": f"Bearer {API_KEY_REF}",
    "Content-Type": "application/json"
}

payload = {
    "model": "{{MODEL}}",
    "messages": [
        {
            "role": "user",
            "content": "Generate a beautiful sunset over mountains"
        }
    ],
    "modalities": ["image", "text"]
}

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

# The generated image will be in the assistant message
if result.get("choices"):
    message = result["choices"][0]["message"]
    if message.get("images"):
        for image in message["images"]:
            image_url = image["image_url"]["url"]  # Base64 data URL
            print(f"Generated image: {image_url[:50]}...")
```

```typescript title="TypeScript (fetch)"
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY_REF}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    messages: [
      {
        role: 'user',
        content: 'Generate a beautiful sunset over mountains',
      },
    ],
    modalities: ['image', 'text'],
  }),
});

const result = await response.json();

// The generated image will be in the assistant message
if (result.choices) {
  const message = result.choices[0].message;
  if (message.images) {
    message.images.forEach((image, index) => {
      const imageUrl = image.image_url.url; // Base64 data URL
      console.log(`Generated image ${index + 1}: ${imageUrl.substring(0, 50)}...`);
    });
  }
}
```

### Image Configuration Options

Some image generation models support additional configuration through the `image_config` parameter. The shared options below — aspect ratio and image size — work across many image models. Parameters that are specific to a single provider are grouped into dedicated [Recraft](#recraft-image-options) and [Sourceful](#sourceful-image-options) sections further down.

#### Aspect Ratio

Set `image_config.aspect_ratio` to request specific aspect ratios for generated images.

**Supported aspect ratios:**

* `1:1` → 1024×1024 (default)
* `2:3` → 832×1248
* `3:2` → 1248×832
* `3:4` → 864×1184
* `4:3` → 1184×864
* `4:5` → 896×1152
* `5:4` → 1152×896
* `9:16` → 768×1344
* `16:9` → 1344×768
* `21:9` → 1536×672

**Azure MAI Image aspect ratios** (supported by [`microsoft/mai-image-2.5`](/models/microsoft/mai-image-2.5)):

* `1:1` → 1024×1024 (default)
* `4:3` → 1024×768
* `3:4` → 768×1024
* `16:9` → 1365×768
* `9:16` → 768×1365
* `3:2` → 1152×768
* `2:3` → 768×1152

**Extended aspect ratios** (supported by [`google/gemini-3.1-flash-image-preview`](/models/google/gemini-3.1-flash-image-preview) only):

* `1:4` → Tall, narrow format ideal for scrolling carousels and vertical UI elements
* `4:1` → Wide, short format for hero banners and horizontal layouts
* `1:8` → Extra-tall format for notification headers and narrow vertical spaces
* `8:1` → Extra-wide format for wide-format banners and panoramic layouts

#### Image Size

Set `image_config.image_size` to control the resolution of generated images.

**Supported sizes:**

* `1K` → Standard resolution (default)
* `2K` → Higher resolution
* `4K` → Highest resolution
* `0.5K` → Lower resolution, optimized for efficiency (supported by [`google/gemini-3.1-flash-image-preview`](/models/google/gemini-3.1-flash-image-preview) only)

You can combine both `aspect_ratio` and `image_size` in the same request:

```python
import requests
import json

url = "https://openrouter.ai/api/v1/chat/completions"
headers = {
    "Authorization": f"Bearer {API_KEY_REF}",
    "Content-Type": "application/json"
}

payload = {
    "model": "{{MODEL}}",
    "messages": [
        {
            "role": "user",
            "content": "Create a picture of a nano banana dish in a fancy restaurant with a Gemini theme"
        }
    ],
    "modalities": ["image", "text"],
    "image_config": {
        "aspect_ratio": "16:9",
        "image_size": "4K"
    }
}

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

if result.get("choices"):
    message = result["choices"][0]["message"]
    if message.get("images"):
        for image in message["images"]:
            image_url = image["image_url"]["url"]
            print(f"Generated image: {image_url[:50]}...")
```

```typescript
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY_REF}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    messages: [
      {
        role: 'user',
        content: 'Create a picture of a nano banana dish in a fancy restaurant with a Gemini theme',
      },
    ],
    modalities: ['image', 'text'],
    image_config: {
      aspect_ratio: '16:9',
      image_size: '4K',
    },
  }),
});

const result = await response.json();

if (result.choices) {
  const message = result.choices[0].message;
  if (message.images) {
    message.images.forEach((image, index) => {
      const imageUrl = image.image_url.url;
      console.log(`Generated image ${index + 1}: ${imageUrl.substring(0, 50)}...`);
    });
  }
}
```

### Recraft Image Options

The parameters in this section are supported only by [Recraft](/models?q=Recraft) models. Where support is limited to a specific Recraft version (for example, V3 but not V4), that is noted on the parameter.

#### Strength

Set `image_config.strength` to control how much the output image differs from the input image during image-to-image generation. This parameter only applies when input images are provided in `messages`. Supported by all Recraft models (`recraft/recraft-v3`, `recraft/recraft-v4`, and `recraft/recraft-v4-pro`).

* **Range**: `0.0` to `1.0`
* **Default**: `0.2`
* Lower values produce outputs closer to the input image; higher values allow more creative deviation.

**Example:**

```json
{
  "image_config": {
    "strength": 0.7
  }
}
```

#### Text Layout (Recraft V3 only)

Use `image_config.text_layout` to place text at specific positions on the generated image. Each entry specifies the text to render and a bounding box defined by four corner points in normalized coordinates (0 to 1). This parameter is only supported by Recraft V3 (`recraft/recraft-v3`) for both text-to-image and image-to-image requests. Recraft V4 and V4 Pro do not support `text_layout`.

Each text layout entry is an object with:

* `text` (required): The text string to render
* `bbox` (required): Array of 4 `[x, y]` coordinate pairs defining the bounding box corners (top-left, top-right, bottom-right, bottom-left), with values from 0 to 1

**Example:**

```json
{
  "image_config": {
    "text_layout": [
      {
        "text": "Hello",
        "bbox": [[0.3, 0.45], [0.6, 0.45], [0.6, 0.55], [0.3, 0.55]]
      },
      {
        "text": "World",
        "bbox": [[0.35, 0.6], [0.65, 0.6], [0.65, 0.7], [0.35, 0.7]]
      }
    ]
  }
}
```

#### Style (Recraft V3 only)

Use `image_config.style` to apply a specific artistic style to the generated image. This parameter is only supported by Recraft V3 (`recraft/recraft-v3`). Recraft V4 and V4 Pro do not support styles.

See the [full list of available styles](https://www.recraft.ai/docs/api-reference/styles#list-of-styles) in Recraft's documentation. Note that vector styles are not supported.

**Example:**

```json
{
  "image_config": {
    "style": "Photorealism"
  }
}
```

#### RGB Colors

Use `image_config.rgb_colors` to specify a color palette that influences the generated image. Each color is a `[r, g, b]` array of three integers (0 to 255). Supported by all Recraft models (`recraft/recraft-v3`, `recraft/recraft-v4`, and `recraft/recraft-v4-pro`) for both text-to-image and image-to-image requests.

**Example:**

```json
{
  "image_config": {
    "rgb_colors": [
      [255, 0, 0],
      [0, 128, 0]
    ]
  }
}
```

#### Background RGB Color

Use `image_config.background_rgb_color` to set a specific background color for the generated image. The value is a `[r, g, b]` array of three integers (0 to 255). Supported by all Recraft models (`recraft/recraft-v3`, `recraft/recraft-v4`, and `recraft/recraft-v4-pro`) for both text-to-image and image-to-image requests.

**Example:**

```json
{
  "image_config": {
    "background_rgb_color": [0, 0, 255]
  }
}
```

You can combine `rgb_colors` and `background_rgb_color` in the same request:

```json
{
  "image_config": {
    "rgb_colors": [[255, 0, 0]],
    "background_rgb_color": [255, 255, 255]
  }
}
```

### Sourceful Image Options

The parameters in this section are supported only by [Sourceful](/models?q=Sourceful) models. Each parameter notes the Sourceful version (V2 or V2.5) that supports it.

#### Font Inputs (Riverflow V2 and newer)

Use `image_config.font_inputs` to render custom text with specific fonts in generated images. The text you want to render must also be included in your prompt for best results. Supported by Sourceful Riverflow V2 and newer — `sourceful/riverflow-v2-fast`, `sourceful/riverflow-v2-pro`, `sourceful/riverflow-v2.5-fast`, and `sourceful/riverflow-v2.5-pro`.

Each font input is an object with:

* `font_url` (required): URL to the font file
* `text` (required): Text to render with the font

**Limits:**

* Maximum 2 font inputs per request
* Additional cost: \$0.03 per font input

**Example:**

```json
{
  "image_config": {
    "font_inputs": [
      {
        "font_url": "https://example.com/fonts/custom-font.ttf",
        "text": "Hello World"
      }
    ]
  }
}
```

**Tips for best results:**

* Include the text in your prompt along with details about font name, color, size, and position
* The `text` parameter should match exactly what's in your prompt - avoid extra wording or quotation marks
* Use line breaks or double spaces to separate headlines and sub-headers when using the same font
* Works best with short, clear headlines and sub-headlines

#### Super Resolution References (Riverflow V2 only)

Use `image_config.super_resolution_references` to enhance low-quality elements in your input image using high-quality reference images. The output image will match the size of your input image, so use larger input images for better results. Supported by Sourceful V2 models (`sourceful/riverflow-v2-fast` and `sourceful/riverflow-v2-pro`) when using image-to-image generation (i.e., when input images are provided in `messages`).

**Limits:**

* Maximum 4 reference URLs per request
* Only works with image-to-image requests (ignored when there are no images in `messages`)
* Additional cost: \$0.20 per reference

**Example:**

```json
{
  "image_config": {
    "super_resolution_references": [
      "https://example.com/reference1.jpg",
      "https://example.com/reference2.jpg"
    ]
  }
}
```

**Tips for best results:**

* Supply an input image where the elements to enhance are present but low quality
* Use larger input images for better output quality (output matches input size)
* Use high-quality reference images that show what you want the enhanced elements to look like

#### Scoring Prompt (Riverflow V2.5 only)

Use `image_config.scoring_prompt` to give the model free-form guidance it uses to evaluate and refine its own output during generation. Supported by Sourceful V2.5 models (`sourceful/riverflow-v2.5-fast` and `sourceful/riverflow-v2.5-pro`).

**Example:**

```json
{
  "image_config": {
    "scoring_prompt": "Prefer realistic materials, crisp product edges, and even studio lighting."
  }
}
```

#### Scoring Rubric (Riverflow V2.5 only)

Use `image_config.scoring_rubric` to provide a structured set of weighted dimensions the model scores its output against. This is the structured complement to `scoring_prompt` — the two are independent and can be provided together in the same request (rubric for weighted dimension scoring, prompt for additional free-form guidance). Supported by Sourceful V2.5 models (`sourceful/riverflow-v2.5-fast` and `sourceful/riverflow-v2.5-pro`).

Each rubric entry is an object with:

* `key` (required): unique machine-readable identifier for the dimension
* `label` (required): human-readable name
* `description` (required): what this dimension evaluates
* `weight` (required): relative importance as a positive number
* `passing_score` (optional): minimum acceptable score
* `score_guidance` (optional): array of `{ "score": number, "description": string }` anchors

**Limits:**

* 1 to 8 dimensions per request

**Example:**

```json
{
  "image_config": {
    "scoring_rubric": [
      {
        "key": "lighting",
        "label": "Lighting quality",
        "description": "Even, professional studio lighting with no blown highlights.",
        "weight": 2,
        "passing_score": 7,
        "score_guidance": [
          { "score": 10, "description": "Flawless, even studio lighting." },
          { "score": 5, "description": "Acceptable but uneven lighting." }
        ]
      },
      {
        "key": "edges",
        "label": "Edge sharpness",
        "description": "Crisp, clean product edges without halos.",
        "weight": 1
      }
    ]
  }
}
```

#### Background Mode and Color (Riverflow V2.5 only)

Use `image_config.background_mode` to control how the generated image's background is handled, and `image_config.background_hex_color` to set the fill color for solid backgrounds. Supported by Sourceful V2.5 models (`sourceful/riverflow-v2.5-fast` and `sourceful/riverflow-v2.5-pro`).

* `background_mode`: one of `original` (default — keep the generated background), `transparent` (remove the background), or `solid` (composite onto a flat color).
* `background_hex_color`: a `#RRGGBB` hex string. Required when `background_mode` is `solid`.

**Behavior:**

* Passing only `background_hex_color` (without `background_mode`) is treated as `solid` with that color.
* `background_hex_color` is validated but then discarded for `original` and `transparent` modes — a malformed hex string always returns a 400 regardless of mode.

**Example (solid color):**

```json
{
  "image_config": {
    "background_mode": "solid",
    "background_hex_color": "#f6f1e8"
  }
}
```

**Example (transparent):**

```json
{
  "image_config": {
    "background_mode": "transparent"
  }
}
```

Recraft uses `background_rgb_color` (an `[r, g, b]` array) to set a solid background color, whereas Sourceful V2.5 uses `background_mode` together with `background_hex_color`.

### Streaming Image Generation

Image generation also works with streaming responses:

```python
import requests
import json

url = "https://openrouter.ai/api/v1/chat/completions"
headers = {
    "Authorization": f"Bearer {API_KEY_REF}",
    "Content-Type": "application/json"
}

payload = {
    "model": "{{MODEL}}",
    "messages": [
        {
            "role": "user",
            "content": "Create an image of a futuristic city"
        }
    ],
    "modalities": ["image", "text"],
    "stream": True
}

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

for line in response.iter_lines():
    if line:
        line = line.decode('utf-8')
        if line.startswith('data: '):
            data = line[6:]
            if data != '[DONE]':
                try:
                    chunk = json.loads(data)
                    if chunk.get("choices"):
                        delta = chunk["choices"][0].get("delta", {})
                        if delta.get("images"):
                            for image in delta["images"]:
                                print(f"Generated image: {image['image_url']['url'][:50]}...")
                except json.JSONDecodeError:
                    continue
```

```typescript
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY_REF}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    messages: [
      {
        role: 'user',
        content: 'Create an image of a futuristic city',
      },
    ],
    modalities: ['image', 'text'],
    stream: true,
  }),
});

const reader = response.body?.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value);
  const lines = chunk.split('\n');

  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const data = line.slice(6);
      if (data !== '[DONE]') {
        try {
          const parsed = JSON.parse(data);
          if (parsed.choices) {
            const delta = parsed.choices[0].delta;
            if (delta?.images) {
              delta.images.forEach((image, index) => {
                console.log(`Generated image ${index + 1}: ${image.image_url.url.substring(0, 50)}...`);
              });
            }
          }
        } catch (e) {
          // Skip invalid JSON
        }
      }
    }
  }
}
```

## Response Format

When generating images, the assistant message includes an `images` field containing the generated images:

```json
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "I've generated a beautiful sunset image for you.",
        "images": [
          {
            "type": "image_url",
            "image_url": {
              "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
            }
          }
        ]
      }
    }
  ]
}
```

### Image Format

* **Format**: Images are returned as base64-encoded data URLs
* **Types**: Typically PNG format (`data:image/png;base64,`)
* **Multiple Images**: Some models can generate multiple images in a single response
* **Size**: Image dimensions vary by model capabilities

## Model Compatibility

Not all models support image generation. To use this feature:

1. **Check Output Modalities**: Ensure the model has `"image"` in its `output_modalities`
2. **Set Modalities Parameter**: Use `["image", "text"]` for models that output both, or `["image"]` for image-only models
3. **Use Compatible Models**: Examples include:
   * `google/gemini-3.1-flash-image-preview` (supports extended aspect ratios and 0.5K resolution)
   * `google/gemini-2.5-flash-image`
   * `black-forest-labs/flux.2-pro`
   * `black-forest-labs/flux.2-flex`
   * `sourceful/riverflow-v2-standard-preview`
   * Other models with image generation capabilities

## Best Practices

* **Clear Prompts**: Provide detailed descriptions for better image quality
* **Model Selection**: Choose models specifically designed for image generation
* **Error Handling**: Check for the `images` field in responses before processing
* **Rate Limits**: Image generation may have different rate limits than text generation
* **Storage**: Consider how you'll handle and store the base64 image data

## Troubleshooting

**No images in response?**

* Verify the model supports image generation (`output_modalities` includes `"image"`)
* Ensure you've set the `modalities` parameter correctly: `["image", "text"]` for models that output both, or `["image"]` for image-only models
* Check that your prompt is requesting image generation

**Model not found?**

* Use the [Models page](/models) to find available image generation models
* Filter by output modalities to see compatible models