Gemini Image
| Model (used in the path) | Positioning | Available imageSize |
|---|---|---|
gemini-3-pro-image | Quality tier | 1K / 2K / 4K |
gemini-3.1-flash-image | Speed / cost balance | 512px / 1K / 2K / 4K |
gemini-3.1-flash-lite-image | Lowest cost | 1K only |
The ModelGo gateway accepts image generation requests on the Gemini API generateContent protocol unchanged: contents + generationConfig, returning candidates[].content.parts[].inlineData synchronously. The gateway routes to a supplier and bills the call; which supplier renders the image is invisible to the caller. The model name in the URL path is authoritative — the body does not carry a model.
With the Google GenAI SDK, point
http_options.base_urlathttps://api.modelgo.aiand use your ModelGo API key asapi_key. Bothx-goog-api-key: $KEYandAuthorization: Bearer $KEYare accepted.
>
Google's newer Interactions API is not yet supported; use
generateContent.
1. Create an image
POST https://api.modelgo.ai/v1beta/models/{model}:generateContent (also accepted under /v1/models/...)
Text to image
curl "https://api.modelgo.ai/v1beta/models/gemini-3.1-flash-image:generateContent" \
-H "x-goog-api-key: $MODELGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"role": "user", "parts": [{"text": "A fluffy orange cat sitting on a sunny windowsill, film grain"}]}],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"}
}
}'Image to image
Put reference images into parts as inlineData (base64) or fileData (public URL):
curl "https://api.modelgo.ai/v1beta/models/gemini-3-pro-image:generateContent" \
-H "x-goog-api-key: $MODELGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"role": "user", "parts": [
{"text": "Replace the background with a neon night street, keep the cat's pose"},
{"inlineData": {"mimeType": "image/png", "data": "<base64>"}}
]}],
"generationConfig": {"responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "1:1", "imageSize": "1K"}}
}39;fileData form: {"fileData": {"fileUri": "https://example-cdn.com/cat.png"}} — must be a direct link the upstream can GET.
Search grounding (enabled per model route)
gemini-3-pro-image and gemini-3.1-flash-image support Google Search Grounding; gemini-3.1-flash-lite-image does not. The feature must be enabled explicitly on the model route and is off by default; while it is off the request returns 400 provider_unsupported without invoking a supplier or reserving any charge.
curl "https://api.modelgo.ai/v1beta/models/gemini-3.1-flash-image:generateContent" \
-H "x-goog-api-key: $MODELGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"parts": [{"text": "A stylized match report image for the Champions League game last night"}]}],
"tools": [{"google_search": {}}],
"generationConfig": {"responseModalities": ["IMAGE"]}
}'When grounding is enabled, the response carries searchEntryPoint, groundingChunks and webSearchQueries under candidates[0].groundingMetadata (image search grounding also reports imageSearchQueries).
Response
{
"candidates": [{
"content": {
"role": "model",
"parts": [{"inlineData": {"mimeType": "image/png", "data": "iVBORw0KGgo..."}}]
},
"finishReason": "STOP",
"index": 0
}],
"usageMetadata": {
"promptTokenCount": 18,
"candidatesTokenCount": 1680,
"totalTokenCount": 1698,
"promptTokensDetails": [{"modality": "TEXT", "tokenCount": 18}],
"candidatesTokensDetails": [{"modality": "IMAGE", "tokenCount": 1680}]
},
"modelVersion": "gemini-3.1-flash-image"
}- The image is in
parts[].inlineData.data(base64). WhenresponseModalitiesincludesTEXT,partsmay also contain a{"text": ...}part. modelVersionechoes the model name from the request path.
Save to a file:
curl -s "https://api.modelgo.ai/v1beta/models/gemini-3.1-flash-image:generateContent" \
-H "x-goog-api-key: $MODELGO_API_KEY" -H "Content-Type: application/json" \
-d '{"contents":[{"parts":[{"text":"a red ball on a wooden table"}]}],"generationConfig":{"responseModalities":["IMAGE"]}}' \
| python3 -c '
import sys, json, base64
for p in json.load(sys.stdin)["candidates"][0]["content"]["parts"]:
if "inlineData" in p:
open("out.png", "wb").write(base64.b64decode(p["inlineData"]["data"])); break'2. Parameters
| Field | Type | Description |
|---|---|---|
contents[].role | string | user |
contents[].parts[].text | string | The prompt. Describes the image; for image-to-image, describes the change |
contents[].parts[].inlineData | object | Reference image, {"mimeType": "image/png", "data": "<base64>"} |
contents[].parts[].fileData | object | Reference image URL, {"fileUri": "https://..."} |
systemInstruction | object | Optional system instruction, same as Gemini text calls |
generationConfig.responseModalities | string[] | ["IMAGE"] for image only; ["TEXT","IMAGE"] for interleaved text and image |
generationConfig.imageConfig.aspectRatio | string | Common values such as 1:1 / 16:9 / 9:16; see the official documentation for the full list |
generationConfig.imageConfig.imageSize | string | Resolution tier; per-model values in the first table, default 1K |
tools | object[] | Only [{"google_search": {}}] (Search Grounding). Supported by gemini-3-pro-image and gemini-3.1-flash-image, not by gemini-3.1-flash-lite-image; must be enabled on the model route (off by default) |
toolConfig | object | Grounding retrieval configuration, forwarded with tools |
cachedContent | string | Not supported: Google's image models do not offer context caching; sending it returns 400 |
candidateCount> 1 →400: Google's image models do not support multiple candidates (Google answersMultiple candidates is not enabled for this model); one image per request.tools/toolConfig: Search Grounding only, and model-specific — supported bygemini-3-pro-imageandgemini-3.1-flash-image, not bygemini-3.1-flash-lite-image. Availability also depends on the model route; while grounding is off the request returns400 provider_unsupportedwithout invoking a supplier or reserving any charge.cachedContent→400: Google's image models do not support context caching.responseSchema/responseMimeType→400: image models do not support structured output.:streamGenerateContent→400: image generation does not stream.- An
imageSizethe model does not offer (e.g.2Kongemini-3.1-flash-lite-image) is decided by the supplier; an unsupported value returns400. - Routing follows deliverable tiers. The gateway prefers suppliers that can deliver the declared tier; if none claims it, it still routes to an available supplier and lets it decide (a
400if unsupported).
Resolution tiers
| Tier | Pixel budget | Output (example) |
|---|---|---|
512px | ~0.25 MP | — |
1K (default) | ~1 MP | default → 1408×768 |
2K | ~4 MP | 16:9 → 2752×1536 |
4K | ~16 MP | 9:16 → 3072×5504 |
The tier sets the total pixel budget; exact width and height are determined by aspectRatio. Without an aspectRatio the default is the supplier's roughly 16:9 output.
3. Full example (Python, Google GenAI SDK)
import os
from google import genai
from google.genai import types
client = genai.Client(api_key=os.environ["MODELGO_API_KEY"],
http_options={"base_url": "https://api.modelgo.ai"})
resp = client.models.generate_content(
model="gemini-3.1-flash-image",
contents="A fluffy orange cat sitting on a sunny windowsill, film grain",
config=types.GenerateContentConfig(
response_modalities=["IMAGE"],
image_config=types.ImageConfig(aspect_ratio="16:9", image_size="2K"),
),
)
for part in resp.candidates[0].content.parts:
if part.inline_data:
open("cat.png", "wb").write(part.inline_data.data)
print(resp.usage_metadata.candidates_token_count)For image-to-image, pass a list as contents: ["Replace the background with a neon night street", types.Part.from_bytes(data=open("cat.png","rb").read(), mime_type="image/png")].
4. Billing
- Token-based: text in
promptTokensDetailsatinput, images atimage_input; incandidatesTokensDetails,IMAGEatimage_outputandTEXTatoutput. - Output image tokens grow with the tier; take the exact values from
usageMetadatain the response. - A reservation is frozen at request time and settled from the actual
usageMetadatavalues. - Grounding add-on: a Search Grounding call is billed per executed query, counted from
groundingMetadata.webSearchQueries/imageSearchQueriesin the response, under theweb_searchadd-on. A response withoutgroundingMetadataadds nothing. The add-on price is whatever the console configures. - Rejected requests (
400) and failed generations are never billed; the reservation is released automatically. - Prices are those shown in the console's model list.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
400 | imageSize not offered by the model; aspectRatio outside the table; candidateCount > 1 (Google's image models do not support multiple candidates); tools / toolConfig on an unsupported model or a route with grounding off; cachedContent (image models do not support caching); responseSchema / responseMimeType; :streamGenerateContent called; no supplier can currently produce the requested tier | Fix per the parameter table; use generateContent |
No inlineData in parts | responseModalities did not include IMAGE, or the prompt was blocked by safety policy (finishReason other than STOP) | Pass ["IMAGE"] explicitly; check finishReason |
| Reference image ignored | fileData.fileUri is not a public direct link (auth / redirect / 403) | Use inlineData or a direct link on your own CDN |
401 | Invalid API key or missing x-goog-api-key / Authorization header | Check the key |