Skip to main content
RuAPI gives you two families of image models: Gemini Image (widely known as Nano Banana) and GPT Image. They are called in completely different ways — that is the one thing to understand before you start. Billed in USDT, no foreign card required.
Gemini image models do not use /v1/images/generations. They go through the regular chat endpoint /v1/chat/completions, and the generated image arrives inside the reply text. See “Method 1” below.

Available models

Model IDEndpointBillingWhen to use it
gemini-2.5-flash-imagechatper callCheapest. Especially good at editing existing images
gemini-3.1-flash-image-previewchatper callFaster, better quality than 2.5
gemini-3-pro-image-previewchatper callHighest quality (Nano Banana Pro)
gpt-image-2/v1/images/generationsper tokenOpenAI ecosystem compatibility
For exact prices, see the pricing page. The Gemini models are billed per call (one flat price per image); gpt-image-2 is billed per token (bigger and higher-quality images cost more).

Endpoint & auth

  • Base URL: https://www.ruapi.ai/v1
  • Auth: header Authorization: Bearer sk-YOUR_KEY (create one in the console under “Tokens”)

Method 1: Gemini Image — via chat

The Gemini image models live on the chat endpoint. You send a request exactly as you would to a text model, and an image comes back.
The image arrives as a Markdown data URI inlined in choices[0].message.content — there is no separate image field. The response looks like this:
Here is a ginger cat in a spacesuit against Saturn!
![image](data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...)
You have to pull the base64 out of the text yourself. Ready-made code below.

Text to image

curl https://www.ruapi.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-2.5-flash-image",
    "messages": [
      { "role": "user", "content": "Draw a ginger cat in a spacesuit against Saturn" }
    ]
  }'
import base64
import re

import requests

resp = requests.post(
    "https://www.ruapi.ai/v1/chat/completions",
    headers={"Authorization": "Bearer sk-YOUR_KEY"},
    json={
        "model": "gemini-2.5-flash-image",
        "messages": [
            {"role": "user", "content": "Draw a ginger cat in a spacesuit against Saturn"}
        ],
    },
)
content = resp.json()["choices"][0]["message"]["content"]

# Pull every image out of the Markdown data URIs
images = re.findall(r"!\[image\]\(data:(image/[^;]+);base64,([^)]+)\)", content)
for i, (mime, b64) in enumerate(images):
    ext = mime.split("/")[-1]
    with open(f"image_{i}.{ext}", "wb") as f:
        f.write(base64.b64decode(b64))

# The caption the model wrote next to the image
caption = re.sub(r"!\[image\]\([^)]*\)", "", content).strip()
print(caption, f"— saved {len(images)} image(s)")
A single response may contain several images, which is why the example uses findall rather than search. The model almost always adds a short caption alongside the picture.

Image to image (editing)

To edit an existing image, pass it the same way you would to a vision model: a content array with a text part and an image_url part. The url accepts either a public link or a data URI.
curl https://www.ruapi.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-2.5-flash-image",
    "messages": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "Make this image black and white. Change nothing else." },
          { "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
        ]
      }
    ]
  }'
import base64
import re
from pathlib import Path

import requests

# Option A — a public image URL
source = {"url": "https://example.com/photo.jpg"}

# Option B — a local file as a data URI
# raw = Path("photo.jpg").read_bytes()
# source = {"url": "data:image/jpeg;base64," + base64.b64encode(raw).decode()}

resp = requests.post(
    "https://www.ruapi.ai/v1/chat/completions",
    headers={"Authorization": "Bearer sk-YOUR_KEY"},
    json={
        "model": "gemini-2.5-flash-image",
        "messages": [
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "Make this image black and white. Change nothing else."},
                    {"type": "image_url", "image_url": source},
                ],
            }
        ],
    },
)
content = resp.json()["choices"][0]["message"]["content"]
mime, b64 = re.findall(r"!\[image\]\(data:(image/[^;]+);base64,([^)]+)\)", content)[0]
Path("edited.png").write_bytes(base64.b64decode(b64))
The aspect ratio of the input image is preserved — the result is not cropped to a square.

Multi-turn editing

You can feed the model’s reply straight back into messages as an assistant message — the Markdown data URI is recognised and the image becomes input for the next turn. That is how you chain “make it black and white” → “now add a border”.
messages = [{"role": "user", "content": "Draw a ginger cat"}]

for instruction in ["Make the image black and white", "Add a white border"]:
    resp = requests.post(URL, headers=HEADERS, json={"model": MODEL, "messages": messages}).json()
    reply = resp["choices"][0]["message"]["content"]
    messages.append({"role": "assistant", "content": reply})   # the image stays in context
    messages.append({"role": "user", "content": instruction})
Every turn is a separate billable call. Also, a history full of base64 images inflates the request body fast — do not carry more than the last two or three frames in context.

Supported input image formats

image/png, image/jpeg, image/webp, image/heic, image/heif. Anything else is rejected with mime type is not supported by Gemini.

Native Gemini endpoint

If you already write against the Google GenAI SDK, the same key works with the native format:
curl "https://www.ruapi.ai/v1beta/models/gemini-2.5-flash-image:generateContent" \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contents": [{ "parts": [{ "text": "Draw a ginger cat in a spacesuit" }] }] }'
Here the image comes back structured — in candidates[0].content.parts[].inlineData.data (base64), with no Markdown to parse. If you do not need OpenAI SDK compatibility, this path is simpler.

Method 2: GPT Image — via /v1/images/generations

gpt-image-2 is the classic OpenAI image generation endpoint. No messages, just a prompt.
curl https://www.ruapi.ai/v1/images/generations \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "A ginger cat in a spacesuit against Saturn",
    "size": "1024x1024",
    "quality": "low",
    "n": 1
  }'
import base64

from openai import OpenAI

client = OpenAI(api_key="sk-YOUR_KEY", base_url="https://www.ruapi.ai/v1")

result = client.images.generate(
    model="gpt-image-2",
    prompt="A ginger cat in a spacesuit against Saturn",
    size="1024x1024",
    quality="low",
    n=1,
)

with open("image.png", "wb") as f:
    f.write(base64.b64decode(result.data[0].b64_json))

Parameters

ParameterValuesNotes
size1024x1024, 1536x1024, 1024x1536, …Both dimensions must be multiples of 16
qualitylow, medium, highDrives the output token count, and therefore the price
n1 or moreEach image is billed separately

Response

{
  "data": [
    {
      "b64_json": "iVBORw0KGgoAAAANSUhEUg...",
      "url": "https://.../image.png",
      "revised_prompt": "A ginger cat in a spacesuit against Saturn"
    }
  ],
  "usage": { "input_tokens": 17, "output_tokens": 425, "total_tokens": 442 }
}
Use b64_json, not url. The url field points at external storage; its lifetime is not guaranteed and it may stop resolving at any time. b64_json is the image itself — once you have it, it is yours.
usage.output_tokens counts image tokens, and that is what you pay for. Dropping quality to low cuts it several-fold — handy for drafts and tests.

FAQ

Because in OpenAI’s terms it is not an image-generation model — it is a multimodal chat model that happens to emit images. Call it through /v1/chat/completions; see “Method 1”.
That is the image. When served through the OpenAI-compatible chat endpoint, the picture is inlined into the reply as ![image](data:image/png;base64,...). Extract it with the regex above, or switch to the native Gemini endpoint where the image is a separate field.
gemini-2.5-flash-image gives the best price-to-quality ratio for edits. For maximum detail, use gemini-3-pro-image-preview. gpt-image-2 can edit too, but through a different endpoint, /v1/images/edits.
The Gemini models store nothing — the base64 comes straight back in the response. gpt-image-2 includes a url field, but do not rely on it; save the b64_json.
No. Gemini image models are billed per call: one flat price per image. The usage field is informational and does not affect what you are charged. Only gpt-image-2 is billed per token.