Image understanding and image generation are different workflows:
POST /v1/chat/completions + image_urlPOST /v1/chat/completions + modalitiesThis page focuses on OpenRouter-style image generation through Lunos.
Use models that include image in outputModalities.
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"
Use the Models page and pick models with image output capability.
Use chat completions with modalities:
modalities: ["image", "text"]modalities: ["image"]Endpoint:
POST /v1/chat/completions
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
}'
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);
Some models support extra tuning through image_config.
image_config.aspect_ratio)Common values:
1:1 (default)2:3, 3:2, 3:4, 4:3, 4:5, 5:49:16, 16:9, 21:9Some models support additional extreme ratios (1:4, 4:1, 1:8, 8:1).
image_config.image_size)Common values:
1K (default)2K4KSome models also support 0.5K.
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());
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.
You can also stream responses and read image chunks from choices[].delta.images.
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
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 {}
}
}
}
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..."
}
}
]
}
}
]
}
image in outputModalitiesmodalities is set correctly (["image"] or ["image", "text"])modalities is setGET /v1/modelsimage_sizeoutputModalities for image-capable modelsNo headings found on this page.
