Multimodal

Image generation

One endpoint for both text-to-image and image-to-image — POST /v1/images, matching OpenRouter's own /api/v1/images contract exactly (confirmed against their real OpenAPI spec — there's no separate "generate" vs. "edit" endpoint on their side either). Image-to-image happens on this same endpoint via input_references, not a separate multipart upload. Billed per image, with a real usage block — including cost — inside the JSON response body, not just a response header.

resp = httpx.post(
    "https://llmrouter.sh/v1/images",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "gpt-image-1",
        "prompt": "a red panda astronaut floating in space",
        "aspect_ratio": "16:9",
        "resolution": "2K",
    },
).json()
print(resp["data"][0]["b64_json"][:50], resp["usage"]["cost"])

Image-to-image: pass one or more reference images via input_references (URL or base64 data URL, same two shapes as image input) instead of uploading a file:

resp = httpx.post(
    "https://llmrouter.sh/v1/images",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "gemini-2.5-flash-image",
        "prompt": "make this scene look like a watercolor painting",
        "input_references": [
            {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
        ],
    },
).json()

Every model listed under Which models below, plus the chat-native image models (gemini-2.5-flash-image, gemini-3-pro-image-preview, gemini-3.1-flash-image-preview) work here — one catalog, one endpoint, whichever mechanism each model actually uses under the hood. Other parameters: size (an explicit "WxH" string overrides resolution/aspect_ratio when given), quality, output_format, background, and output_compression (all OpenAI-models-only — ignored elsewhere, same as OpenRouter's own "ignored by providers lacking X control" behavior), n for multiple images per call.

stream: true is not supported — rejected with a 400, not silently ignored. Non-square aspect_ratio on dall-e-2 is clamped to square (it only has one size tier per axis); DeepInfra/SiliconFlow models don't honor resolution/aspect_ratio yet.

Which models

Seven models across three providers, served direct (no cross-provider failover for this endpoint):

ModelProviderNotes
dall-e-3OpenAIdefault for model:"auto"
dall-e-2OpenAI
gpt-image-2OpenAIlatest OpenAI model; generation + edits, priced per token
imagen-4.0Google
imagen-4.0-fastGoogle
imagen-4.0-ultraGoogle
qwen-image-2.0Qwen
qwen-image-2.0-proQwen

size and quality are only forwarded for OpenAI models — Imagen and Qwen don't take them the same way and ignore/reject them.

Image output in chat completions

A second way to get an image back: pass modalities: ["image", "text"] to POST /v1/chat/completions itself — the same wire contract OpenRouter uses. The generated image comes back on the assistant message as message.images[0].image_url.url (a base64 data URL), alongside whatever text the model also returned.

resp = client.chat.completions.create(
    model="gemini-2.5-flash-image",
    messages=[{"role": "user", "content": "Generate a beautiful sunset over mountains"}],
    modalities=["image", "text"],
)
message = resp.choices[0].message
for image in (message.images or []):
    image_url = image["image_url"]["url"]  # base64 data URL
    print(f"Generated image: {image_url[:50]}...")

This works with an uploaded image too — combine an image_url content part (see Image understanding) in the same request with modalities: ["image", "text"] to send an image in and get an edited/re-imagined image back through the same endpoint.

Three models today, all Google:

ModelNotes
gemini-2.5-flash-imagereturns PNG
gemini-3-pro-image-previewreturns JPEG; highest quality, highest cost
gemini-3.1-flash-image-previewreturns JPEG

model: "auto" is not supported for this — pass the model explicitly. Billed per request off the response's own usage.completion_tokens_details split: image tokens (and, on gemini-3-pro-image-preview, reasoning tokens) bill at a materially higher per-token rate than plain text tokens (this is the provider's own pricing, not a platform markup), so cost varies with image size/complexity rather than being a flat per-call price like /v1/images above. A vague, templated prompt (e.g. "a tiny icon of a red circle") can trigger Gemini's own recitation safety filter and come back with no image at all — a descriptive, original prompt avoids this.

Grok, DeepSeek, and Meta's muse-spark-1.1 don't support this yet — each was tested directly, not assumed: Grok and OpenAI's gpt-5.1/gpt-5.2 reject the modalities parameter outright, DeepSeek silently ignores it and returns text only, and Muse Spark's own API rejects "image" as an invalid modality value (it supports image input, not output). We'll add a model here as soon as one of these — or another provider — has a real, verified image-output path.

Not yet supported

Generating multiple unprompted variations of an existing image (no edit instruction, just "more like this") has no equivalent here. Mask-based inpainting via input_references — restricting an edit to one region of the image — isn't supported either, only whole-image edits. stream: true is rejected outright (see above). Chat-native image output is scoped to three Google models for now.