curl https://api.pipellm.ai/v1beta/models/gemini-3-pro-image:generateContent \
-H "x-goog-api-key: $PIPELLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{
"role": "user",
"parts": [{"text": "A red bicycle leaning against a cafe window, morning light"}]
}]
}'
import os
from google import genai
client = genai.Client(
api_key=os.environ["PIPELLM_API_KEY"],
http_options={"base_url": "https://api.pipellm.ai"},
)
response = client.models.generate_content(
model="gemini-3-pro-image",
contents="A red bicycle leaning against a cafe window, morning light",
)
for part in response.parts:
if part.inline_data is not None:
part.as_image().save("bicycle.png")
import fs from "node:fs";
import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({
apiKey: process.env.PIPELLM_API_KEY,
httpOptions: { baseUrl: "https://api.pipellm.ai" },
});
const response = await ai.models.generateContent({
model: "gemini-3-pro-image",
contents: "A red bicycle leaning against a cafe window, morning light",
});
for (const part of response.candidates[0].content.parts) {
if (part.inlineData) {
fs.writeFileSync("bicycle.png", Buffer.from(part.inlineData.data, "base64"));
}
}
{
"candidates": [{
"content": {
"role": "model",
"parts": [
{ "text": "Here is the bicycle scene you asked for." },
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAA..."
}
}
]
},
"finishReason": "STOP"
}],
"usageMetadata": {
"promptTokenCount": 12,
"candidatesTokenCount": 1290,
"totalTokenCount": 1302
}
}
Generate an image with Gemini
Generate and edit images with Gemini image models.
POST
/
v1beta
/
models
/
{model}
:generateContent
curl https://api.pipellm.ai/v1beta/models/gemini-3-pro-image:generateContent \
-H "x-goog-api-key: $PIPELLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{
"role": "user",
"parts": [{"text": "A red bicycle leaning against a cafe window, morning light"}]
}]
}'
import os
from google import genai
client = genai.Client(
api_key=os.environ["PIPELLM_API_KEY"],
http_options={"base_url": "https://api.pipellm.ai"},
)
response = client.models.generate_content(
model="gemini-3-pro-image",
contents="A red bicycle leaning against a cafe window, morning light",
)
for part in response.parts:
if part.inline_data is not None:
part.as_image().save("bicycle.png")
import fs from "node:fs";
import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({
apiKey: process.env.PIPELLM_API_KEY,
httpOptions: { baseUrl: "https://api.pipellm.ai" },
});
const response = await ai.models.generateContent({
model: "gemini-3-pro-image",
contents: "A red bicycle leaning against a cafe window, morning light",
});
for (const part of response.candidates[0].content.parts) {
if (part.inlineData) {
fs.writeFileSync("bicycle.png", Buffer.from(part.inlineData.data, "base64"));
}
}
{
"candidates": [{
"content": {
"role": "model",
"parts": [
{ "text": "Here is the bicycle scene you asked for." },
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAA..."
}
}
]
},
"finishReason": "STOP"
}],
"usageMetadata": {
"promptTokenCount": 12,
"candidatesTokenCount": 1290,
"totalTokenCount": 1302
}
}
Gemini image models do not use
Authenticate with
/v1/images/*. They generate images through the same generateContent endpoint as text models, and return the bytes as an inline part alongside any text the model produced.
For GPT image models, use POST /v1/images/generations instead.
See Nano Banana for worked text-to-image and image-to-image examples.
Model IDs change over time. List the ones available to your account with
GET /v1/models.Endpoint
| Mode | Path |
|---|---|
| Non-streaming | POST /v1beta/models/{model}:generateContent |
| Streaming | POST /v1beta/models/{model}:streamGenerateContent |
x-goog-api-key, as on every Gemini route. See Gemini overview.
array
required
Conversation turns. For text-to-image a single user turn with one text part is enough; for editing, add the source image as an inline part in the same turn.
object
Standard Gemini generation settings. Image models also accept sizing controls here; the accepted aspect ratios and resolutions differ per model, so check the model’s own card before relying on a value.
curl https://api.pipellm.ai/v1beta/models/gemini-3-pro-image:generateContent \
-H "x-goog-api-key: $PIPELLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{
"role": "user",
"parts": [{"text": "A red bicycle leaning against a cafe window, morning light"}]
}]
}'
import os
from google import genai
client = genai.Client(
api_key=os.environ["PIPELLM_API_KEY"],
http_options={"base_url": "https://api.pipellm.ai"},
)
response = client.models.generate_content(
model="gemini-3-pro-image",
contents="A red bicycle leaning against a cafe window, morning light",
)
for part in response.parts:
if part.inline_data is not None:
part.as_image().save("bicycle.png")
import fs from "node:fs";
import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({
apiKey: process.env.PIPELLM_API_KEY,
httpOptions: { baseUrl: "https://api.pipellm.ai" },
});
const response = await ai.models.generateContent({
model: "gemini-3-pro-image",
contents: "A red bicycle leaning against a cafe window, morning light",
});
for (const part of response.candidates[0].content.parts) {
if (part.inlineData) {
fs.writeFileSync("bicycle.png", Buffer.from(part.inlineData.data, "base64"));
}
}
{
"candidates": [{
"content": {
"role": "model",
"parts": [
{ "text": "Here is the bicycle scene you asked for." },
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAA..."
}
}
]
},
"finishReason": "STOP"
}],
"usageMetadata": {
"promptTokenCount": 12,
"candidatesTokenCount": 1290,
"totalTokenCount": 1302
}
}
array
Generated responses.
Show candidates[]
Show candidates[]
array
Interleaved output. A part carries either
text or inlineData, never both. A single response can hold several of each, so iterate over all parts rather than reading parts[0].string
For example
image/png.string
Base64-encoded image bytes.
string
STOP on success. A safety stop returns no image part.object
Token counts. Generated images are billed as image output tokens, which dominate the cost — see Pricing.
Reading the response
Two habits save trouble:- Iterate over every part. Ask for an illustrated explanation and you get text and image parts interleaved. Taking only the first part silently drops content.
- Expect a text-only response sometimes. If the prompt is refused on safety grounds the response still returns
200with a text part and noinlineData. Check for the image part before decoding.
Errors
Gemini routes return the Gemini error shape, not the OpenAI one. See Errors for the full mapping.400 INVALID_ARGUMENT
400 INVALID_ARGUMENT
Malformed
contents, an unsupported mimeType on an inline part, or a sizing value the model does not accept.401 UNAUTHENTICATED
401 UNAUTHENTICATED
Missing or wrong
x-goog-api-key.429 RESOURCE_EXHAUSTED
429 RESOURCE_EXHAUSTED
See Rate Limits.
Was this page helpful?