Lunos logoLunos

Multimodal Image Generation

Image understanding and image generation are different workflows:

  • Image understanding: POST /v1/chat/completions + image_url
  • Image generation: POST /v1/chat/completions + modalities

This page focuses on OpenRouter-style image generation through Lunos.

Model discovery

Use models that include image in outputModalities.

Via Models API (GET /v1/models)

Use the output filter directly:

curl -s "https://api.lunos.tech/v1/models?output=image"

You can combine filters when needed, for example image output with text input:

curl -s "https://api.lunos.tech/v1/models?input=text&output=image"

In dashboard

Use the Models page and pick models with image output capability.

API usage

Use chat completions with modalities:

  • Models that support both text and image output: modalities: ["image", "text"]
  • Image-only models: modalities: ["image"]

Endpoint:

POST /v1/chat/completions

Basic image generation

cURL

curl -X POST "https://api.lunos.tech/v1/chat/completions" \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openrouter/google/gemini-2.5-flash-image",
    "messages": [
      {
        "role": "user",
        "content": "Generate a beautiful sunset over mountains"
      }
    ],
    "modalities": ["image", "text"],
    "stream": false
  }'

Python

import requests

payload = {
    "model": "openrouter/google/gemini-2.5-flash-image",
    "messages": [
        {"role": "user", "content": "Generate a beautiful sunset over mountains"}
    ],
    "modalities": ["image", "text"],
    "stream": False,
}
response = requests.post(
    "https://api.lunos.tech/v1/chat/completions",
    headers={
        "Authorization": "Bearer YOUR_SECRET_KEY",
        "Content-Type": "application/json",
    },
    json=payload,
)
result = response.json()
print(result)
const response = await fetch("https://api.lunos.tech/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_SECRET_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "openrouter/google/gemini-2.5-flash-image",
    messages: [{ role: "user", content: "Generate a beautiful sunset over mountains" }],
    modalities: ["image", "text"],
    stream: false,
  }),
});
const result = await response.json();
console.log(result);

Image configuration options

Some models support extra tuning through image_config.

Aspect ratio (image_config.aspect_ratio)

Common values:

  • 1:1 (default)
  • 2:3, 3:2, 3:4, 4:3, 4:5, 5:4
  • 9:16, 16:9, 21:9

Some models support additional extreme ratios (1:4, 4:1, 1:8, 8:1).

Image size (image_config.image_size)

Common values:

  • 1K (default)
  • 2K
  • 4K

Some models also support 0.5K.

Combined config example

cURL

curl -X POST "https://api.lunos.tech/v1/chat/completions" \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openrouter/google/gemini-3-pro-image-preview",
    "messages": [
      {
        "role": "user",
        "content": "Create a high-end restaurant scene with a futuristic nano banana dish"
      }
    ],
    "modalities": ["image", "text"],
    "image_config": {
      "aspect_ratio": "16:9",
      "image_size": "4K"
    }
  }'
import requests

payload = {
    "model": "openrouter/google/gemini-3-pro-image-preview",
    "messages": [
        {"role": "user", "content": "Create a high-end restaurant scene with a futuristic nano banana dish"}
    ],
    "modalities": ["image", "text"],
    "image_config": {
        "aspect_ratio": "16:9",
        "image_size": "4K",
    },
}
response = requests.post(
    "https://api.lunos.tech/v1/chat/completions",
    headers={
        "Authorization": "Bearer YOUR_SECRET_KEY",
        "Content-Type": "application/json",
    },
    json=payload,
)
print(response.json())
const response = await fetch("https://api.lunos.tech/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_SECRET_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "openrouter/google/gemini-3-pro-image-preview",
    messages: [
      {
        role: "user",
        content: "Create a high-end restaurant scene with a futuristic nano banana dish",
      },
    ],
    modalities: ["image", "text"],
    image_config: {
      aspect_ratio: "16:9",
      image_size: "4K",
    },
  }),
});
console.log(await response.json());

Sourceful-only options

For Sourceful models, some advanced options may be available:

  • image_config.font_inputs (custom font rendering)
  • image_config.super_resolution_references (reference-based enhancement)

These options are provider/model specific and may include additional cost.

Streaming image generation

You can also stream responses and read image chunks from choices[].delta.images.

cURL

curl -N -X POST "https://api.lunos.tech/v1/chat/completions" \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openrouter/google/gemini-2.5-flash-image",
    "messages": [{"role": "user", "content": "Create an image of a futuristic city"}],
    "modalities": ["image", "text"],
    "stream": true
  }'
import json
import requests

payload = {
    "model": "openrouter/google/gemini-2.5-flash-image",
    "messages": [{"role": "user", "content": "Create an image of a futuristic city"}],
    "modalities": ["image", "text"],
    "stream": True,
}
response = requests.post(
    "https://api.lunos.tech/v1/chat/completions",
    headers={
        "Authorization": "Bearer YOUR_SECRET_KEY",
        "Content-Type": "application/json",
    },
    json=payload,
    stream=True,
)

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

TypeScript

const response = await fetch("https://api.lunos.tech/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_SECRET_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "openrouter/google/gemini-2.5-flash-image",
    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();

if (reader) {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const chunk = decoder.decode(value);
    for (const line of chunk.split("\n")) {
      if (!line.startsWith("data: ")) continue;
      const data = line.slice(6);
      if (data === "[DONE]") continue;
      try {
        const parsed = JSON.parse(data);
        const images = parsed?.choices?.[0]?.delta?.images ?? [];
        images.forEach((img: any) => console.log(img?.image_url?.url?.slice(0, 50)));
      } catch {}
    }
  }
}

Response format

Generated images appear under choices[].message.images:

{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "I generated an image for you.",
        "images": [
          {
            "type": "image_url",
            "image_url": {
              "url": "data:image/png;base64,iVBORw0KGgoAAA..."
            }
          }
        ]
      }
    }
  ]
}

Model compatibility checklist

  1. Model has image in outputModalities
  2. modalities is set correctly (["image"] or ["image", "text"])
  3. Prompt explicitly requests image output

Troubleshooting

No images in response

  • Verify model supports image output
  • Verify modalities is set
  • Verify prompt asks for image generation

Model not found or rejected

  • Re-check model ID from GET /v1/models
  • Ensure model is enabled in your Lunos environment

Poor quality or high cost

  • Improve prompt specificity
  • Lower image_size
  • Pick a model optimized for your target (speed vs quality)

Lunos checklist

  • Filter by outputModalities for image-capable models
  • Add prompt validation and abuse controls server-side
  • Handle both sync and stream response paths
  • Store base64 output safely (or convert/upload for CDN delivery)