Skip to content

SDK quickstart

Generate, edit, stream images, and call text models with the google-genai SDK. This guide uses Google’s official Python SDK. The only Moonez.ai-specific step is overriding base_url. See Gemini-compatible API for the concept.

You’ll need an active API key with permission for the model you call. See Authentication.

Terminal window
pip install google-genai

Point the client at Moonez.ai and pass your API key:

from google import genai
from google.genai import types
client = genai.Client(
api_key="YOUR_MOONEZ_API_KEY",
http_options=types.HttpOptions(
base_url="https://api.moonez.ai/api/gemini",
),
)

Any Gemini client works the same way — set the base URL to https://api.moonez.ai/api/gemini and send your key as x-goog-api-key.

Request the IMAGE modality and read the base64 image from the first candidate.

resp = client.models.generate_content(
model="gemini-2.5-flash-image",
contents="a red panda astronaut floating in a nebula",
config=types.GenerateContentConfig(
response_modalities=["IMAGE"],
image_config=types.ImageConfig(
aspect_ratio="16:9",
),
),
)
image = resp.candidates[0].content.parts[0].inline_data.data # raw PNG bytes
open("out.png", "wb").write(image)

The response is standard Gemini JSON. The image is base64 in candidates[0].content.parts[0].inlineData.data.

For gemini-3.1-flash-image and gemini-3-pro-image, select the output size with imageSize1K, 2K, 4K, plus 512px on gemini-3.1-flash-image:

config = types.GenerateContentConfig(
response_modalities=["IMAGE"],
image_config=types.ImageConfig(
aspect_ratio="1:1",
image_size="2K",
),
)

imageSize is validated by the model — supported values are 1K, 2K, 4K (plus 512px on gemini-3.1-flash-image); anything else returns 400 INVALID_ARGUMENT. gemini-2.5-flash-image (gemini-2.5-flash-image) outputs 1K only. Sizes below 1K (e.g. 512px) are billed at the 1K rate.

Pass one or more input images plus an instruction in contents:

from pathlib import Path
resp = client.models.generate_content(
model="gemini-3-pro-image",
contents=[
types.Part.from_bytes(
data=Path("input.png").read_bytes(),
mime_type="image/png",
),
"add a tiny astronaut helmet",
],
config=types.GenerateContentConfig(response_modalities=["IMAGE"]),
)
open("edited.png", "wb").write(resp.candidates[0].content.parts[0].inline_data.data)

streamGenerateContent returns real SSE frames — the same format the SDK consumes:

for chunk in client.models.generate_content_stream(
model="gemini-3.6-flash",
contents="Write a haiku about servers.",
):
if chunk.text:
print(chunk.text, end="")

The final chunk carries usageMetadata with the exact token counts your balance is charged against.

Text models are billed per token and called exactly like Google’s own API:

resp = client.models.generate_content(
model="gemini-3.5-flash",
contents="Explain quantum computing in one sentence.",
)
print(resp.text)

countTokens is proxied to Google for a real token count — no charge:

resp = client.models.count_tokens(
model="gemini-3.5-flash",
contents="How many tokens is this?",
)
print(resp.total_tokens)

Gemini-compatible errors use Google’s standard status envelope. The most common ones:

Status Code Meaning
400 INVALID_ARGUMENT Unsupported imageSize, malformed request, etc.
403 PERMISSION_DENIED Key lacks permission for the requested model
404 NOT_FOUND Unknown model ID
429 RESOURCE_EXHAUSTED Rate limit reached

Retry with backoff on transient failures; use the SDK’s built-in retry configuration for resilience.