Generate an image (native Gemini, SSE streaming)
curl --request POST \
--url https://api.hairoute.ai/v1/models/{model}:streamGenerateContent \
--header 'Content-Type: application/json' \
--header 'x-goog-api-key: <x-goog-api-key>' \
--data '
{
"contents": [
{
"role": "user",
"parts": [
{
"text": "Draw an orange cat"
}
]
}
],
"generationConfig": {
"responseModalities": [
"TEXT",
"IMAGE"
],
"imageConfig": {
"aspectRatio": "1:1",
"imageSize": "1K"
}
}
}
'import requests
url = "https://api.hairoute.ai/v1/models/{model}:streamGenerateContent"
payload = {
"contents": [
{
"role": "user",
"parts": [{ "text": "Draw an orange cat" }]
}
],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"],
"imageConfig": {
"aspectRatio": "1:1",
"imageSize": "1K"
}
}
}
headers = {
"x-goog-api-key": "<x-goog-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-goog-api-key': '<x-goog-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
contents: [{role: 'user', parts: [{text: 'Draw an orange cat'}]}],
generationConfig: {
responseModalities: ['TEXT', 'IMAGE'],
imageConfig: {aspectRatio: '1:1', imageSize: '1K'}
}
})
};
fetch('https://api.hairoute.ai/v1/models/{model}:streamGenerateContent', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hairoute.ai/v1/models/{model}:streamGenerateContent",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'contents' => [
[
'role' => 'user',
'parts' => [
[
'text' => 'Draw an orange cat'
]
]
]
],
'generationConfig' => [
'responseModalities' => [
'TEXT',
'IMAGE'
],
'imageConfig' => [
'aspectRatio' => '1:1',
'imageSize' => '1K'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-goog-api-key: <x-goog-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hairoute.ai/v1/models/{model}:streamGenerateContent"
payload := strings.NewReader("{\n \"contents\": [\n {\n \"role\": \"user\",\n \"parts\": [\n {\n \"text\": \"Draw an orange cat\"\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"responseModalities\": [\n \"TEXT\",\n \"IMAGE\"\n ],\n \"imageConfig\": {\n \"aspectRatio\": \"1:1\",\n \"imageSize\": \"1K\"\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-goog-api-key", "<x-goog-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hairoute.ai/v1/models/{model}:streamGenerateContent")
.header("x-goog-api-key", "<x-goog-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"contents\": [\n {\n \"role\": \"user\",\n \"parts\": [\n {\n \"text\": \"Draw an orange cat\"\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"responseModalities\": [\n \"TEXT\",\n \"IMAGE\"\n ],\n \"imageConfig\": {\n \"aspectRatio\": \"1:1\",\n \"imageSize\": \"1K\"\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hairoute.ai/v1/models/{model}:streamGenerateContent")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-goog-api-key"] = '<x-goog-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"contents\": [\n {\n \"role\": \"user\",\n \"parts\": [\n {\n \"text\": \"Draw an orange cat\"\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"responseModalities\": [\n \"TEXT\",\n \"IMAGE\"\n ],\n \"imageConfig\": {\n \"aspectRatio\": \"1:1\",\n \"imageSize\": \"1K\"\n }\n }\n}"
response = http.request(request)
puts response.read_body"data: {\"candidates\":[{\"content\":{\"role\":\"model\",\"parts\":[{\"inlineData\":{\"mimeType\":\"image/png\",\"data\":\"<BASE64_IMAGE_DATA>\"}}]},\"finishReason\":\"STOP\"}],\"usageMetadata\":{\"totalTokenCount\":1302}}\n\n"{
"error": {
"code": 123,
"message": "<string>",
"status": "<string>"
}
}Gemini native image generation (streaming)
Receive native Gemini streamGenerateContent image results over SSE, for Google/Gemini image models only
POST
/
v1
/
models
/
{model}
:streamGenerateContent
Generate an image (native Gemini, SSE streaming)
curl --request POST \
--url https://api.hairoute.ai/v1/models/{model}:streamGenerateContent \
--header 'Content-Type: application/json' \
--header 'x-goog-api-key: <x-goog-api-key>' \
--data '
{
"contents": [
{
"role": "user",
"parts": [
{
"text": "Draw an orange cat"
}
]
}
],
"generationConfig": {
"responseModalities": [
"TEXT",
"IMAGE"
],
"imageConfig": {
"aspectRatio": "1:1",
"imageSize": "1K"
}
}
}
'import requests
url = "https://api.hairoute.ai/v1/models/{model}:streamGenerateContent"
payload = {
"contents": [
{
"role": "user",
"parts": [{ "text": "Draw an orange cat" }]
}
],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"],
"imageConfig": {
"aspectRatio": "1:1",
"imageSize": "1K"
}
}
}
headers = {
"x-goog-api-key": "<x-goog-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-goog-api-key': '<x-goog-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
contents: [{role: 'user', parts: [{text: 'Draw an orange cat'}]}],
generationConfig: {
responseModalities: ['TEXT', 'IMAGE'],
imageConfig: {aspectRatio: '1:1', imageSize: '1K'}
}
})
};
fetch('https://api.hairoute.ai/v1/models/{model}:streamGenerateContent', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hairoute.ai/v1/models/{model}:streamGenerateContent",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'contents' => [
[
'role' => 'user',
'parts' => [
[
'text' => 'Draw an orange cat'
]
]
]
],
'generationConfig' => [
'responseModalities' => [
'TEXT',
'IMAGE'
],
'imageConfig' => [
'aspectRatio' => '1:1',
'imageSize' => '1K'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-goog-api-key: <x-goog-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hairoute.ai/v1/models/{model}:streamGenerateContent"
payload := strings.NewReader("{\n \"contents\": [\n {\n \"role\": \"user\",\n \"parts\": [\n {\n \"text\": \"Draw an orange cat\"\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"responseModalities\": [\n \"TEXT\",\n \"IMAGE\"\n ],\n \"imageConfig\": {\n \"aspectRatio\": \"1:1\",\n \"imageSize\": \"1K\"\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-goog-api-key", "<x-goog-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hairoute.ai/v1/models/{model}:streamGenerateContent")
.header("x-goog-api-key", "<x-goog-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"contents\": [\n {\n \"role\": \"user\",\n \"parts\": [\n {\n \"text\": \"Draw an orange cat\"\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"responseModalities\": [\n \"TEXT\",\n \"IMAGE\"\n ],\n \"imageConfig\": {\n \"aspectRatio\": \"1:1\",\n \"imageSize\": \"1K\"\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hairoute.ai/v1/models/{model}:streamGenerateContent")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-goog-api-key"] = '<x-goog-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"contents\": [\n {\n \"role\": \"user\",\n \"parts\": [\n {\n \"text\": \"Draw an orange cat\"\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"responseModalities\": [\n \"TEXT\",\n \"IMAGE\"\n ],\n \"imageConfig\": {\n \"aspectRatio\": \"1:1\",\n \"imageSize\": \"1K\"\n }\n }\n}"
response = http.request(request)
puts response.read_body"data: {\"candidates\":[{\"content\":{\"role\":\"model\",\"parts\":[{\"inlineData\":{\"mimeType\":\"image/png\",\"data\":\"<BASE64_IMAGE_DATA>\"}}]},\"finishReason\":\"STOP\"}],\"usageMetadata\":{\"totalTokenCount\":1302}}\n\n"{
"error": {
"code": 123,
"message": "<string>",
"status": "<string>"
}
}Use the native Gemini
For a real request, do not reconstruct or trim the prior
Parse each SSE
streamGenerateContent endpoint to receive results in chunks. For image models, this endpoint is supported only for Google/Gemini image models. Use the appropriate OpenAI image endpoint for other image models. Native streaming for Gemini chat models is outside the scope of this image reference.
Features
- Use the native Gemini
streamGenerateContentendpoint to receive image results as SSE frames - Use the same request body as the non-streaming endpoint for text-to-image, image editing, multiple references, or multi-turn editing
- Configure image output with
generationConfig.responseModalitiesandgenerationConfig.imageConfig; nostream: truefield is needed - Receive text and Base64 images in
candidates[].content.parts[], not OpenAI image URLs or named events
Authentication
POST https://api.hairoute.ai/v1/models/{model}:streamGenerateContent
{model} is an available Google/Gemini image model from the model list, not a request-body field. Use your HaiRoute API key in x-goog-api-key: YOUR_API_KEY or Authorization: Bearer YOUR_API_KEY. The body can include contents, generationConfig.responseModalities, and optionally generationConfig.imageConfig; this route always responds with text/event-stream.
Supported image models
This endpoint is only for Google/Gemini image models.| Model identifier | Model type | Description |
|---|---|---|
gemini-3.1-flash-lite-image | Image generation and editing | Gemini 3.1 Flash Lite Image; configured output tier: 1K. |
gemini-3.1-flash-image | Image generation and editing | Gemini 3.1 Flash Image; configured output tiers: 512 (about 0.5K), 1K, 2K, 4K. |
gemini-3-pro-image | Image generation and editing | Gemini 3 Pro Image; configured output tiers: 1K, 2K, 4K. |
Quick example
curl -N -X POST 'https://api.hairoute.ai/v1/models/YOUR_GEMINI_IMAGE_MODEL:streamGenerateContent' \
-H 'x-goog-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
-d '{
"contents": [{"role": "user", "parts": [{"text": "Draw an orange cat walking on the moon"}]}],
"generationConfig": {"responseModalities": ["TEXT", "IMAGE"]}
}'
Generation modes
These examples show request bodies only. All four modes use this page’s endpoint and authentication; no separatemode parameter is needed. Replace every Base64 placeholder with actual raw Base64 image data.
Text-to-image
Send a text prompt without an input image; the Quick example above is ready to adapt. Local changes and style adjustments are prompt instructions, not separate API parameters.Single-image editing
Put one image and an editing instruction in the sameuser message. This example replaces the background; you can also ask to preserve the subject, change an element, or adjust the style.
{
"contents": [{"role": "user", "parts": [
{"inlineData": {"mimeType": "image/png", "data": "<BASE64_INPUT_IMAGE>"}},
{"text": "Replace the background with a sunset beach and keep the subject"}
]}],
"generationConfig": {"responseModalities": ["TEXT", "IMAGE"]}
}
Multiple-reference composition
Put multiple reference images in oneuser message’s parts and describe the role of each image in the text. Input count, size limits, and results depend on the selected model and channel.
{
"contents": [{"role": "user", "parts": [
{"inlineData": {"mimeType": "image/png", "data": "<BASE64_SUBJECT_IMAGE>"}},
{"inlineData": {"mimeType": "image/jpeg", "data": "<BASE64_BACKGROUND_IMAGE>"}},
{"text": "Use the person in the first image as the subject and the scene in the second as the background; make a natural composite"}
]}],
"generationConfig": {"responseModalities": ["TEXT", "IMAGE"]}
}
Multi-turn editing
Send the previoususer request, the actual previous candidates[0].content as a model message, and your new instruction in that order within contents. This body illustrates the structure only:
{
"contents": [
{"role": "user", "parts": [{"text": "Draw an orange cat wearing a scarf"}]},
{"role": "model", "parts": [
{"inlineData": {"mimeType": "image/png", "data": "<BASE64_FROM_PREVIOUS_RESPONSE>"}}
]},
{"role": "user", "parts": [{"text": "Keep the cat and scarf, but replace the background with a snowy landscape"}]}
],
"generationConfig": {"responseModalities": ["TEXT", "IMAGE"]}
}
model content. If the prior response was streamed, collect content.parts for the same candidate from all data: frames in order, then replay the complete content as the model message, preserving images, text, and thoughtSignature when present. The last SSE frame alone is not enough. Multiple references and multi-turn results depend on the selected image model.
Streaming response
The server sends SSE frames. Eachdata: line contains one complete Gemini GenerateContentResponse JSON chunk. The following frames are illustrative; the Base64 string is a placeholder:
data: {"candidates":[{"content":{"role":"model","parts":[{"text":"Generating image"}]}}]}
data: {"candidates":[{"content":{"role":"model","parts":[{"inlineData":{"mimeType":"image/png","data":"<BASE64_IMAGE_DATA>"}}]},"finishReason":"STOP"}],"usageMetadata":{"totalTokenCount":1302}}
data frame and inspect candidates[].content.parts[]: text contains text; inlineData.data contains a Base64 image and inlineData.mimeType identifies its format. Some frames may omit these fields; usage usually arrives in a later chunk. This stream has no OpenAI-style named events or [DONE] marker. Do not treat a frame or Base64 image as an image URL.
Troubleshooting
Check the HTTP status and theerror in the response body first; for streaming requests, inspect every received SSE data: frame. Never paste API keys or complete image Base64 in logs or support requests.
| Symptom | What to check |
|---|---|
| Authentication fails (for example, 401/403) | Use a HaiRoute API key in either x-goog-api-key or Authorization: Bearer; do not use a Google API key here. Check that the key is valid and allowed to access the model. |
| Model not found or unavailable | Check that {model} in the URL is an available Google/Gemini image model in your account’s model list. Do not put the model name in the request body or use a different image model on this endpoint. |
| Invalid request or input image | Check contents[].parts, generationConfig.responseModalities (["TEXT", "IMAGE"] for image output), and the selected model’s supported imageConfig. inlineData.data must be raw Base64 without a data:image/...;base64, prefix; mimeType must match the input image format. |
| Request succeeds but no image appears | Inspect all candidates[].content.parts[].inlineData; the image is not in data[].url. If absent, inspect finishReason, promptFeedback, and returned text before adjusting the prompt or request. HTTP 200 alone does not prove an image was generated. |
| No image in streaming output or SSE parsing fails | Split SSE events at blank lines and parse the JSON in each data: line. An image may appear in candidates[].content.parts[].inlineData in any frame; do not read only the last one. This endpoint has no OpenAI-style named events or [DONE]. If the connection drops early, text or usage alone does not prove an image was generated. |
| Multi-turn edit fails or ignores earlier output | Replay the prior user message, the complete previous candidates[0].content (including thoughtSignature if returned), and the new user instruction in order; sending only the image Base64 is insufficient. |
Next steps
- See the non-streaming endpoint for a full non-streaming response example
- See the model list to choose an available image model
请求头
HaiRoute API key for the native request. Alternatively, use Authorization: Bearer YOUR_API_KEY instead (choose one).
路径参数
An available Google/Gemini image model in HaiRoute
请求体
application/json
Required native Gemini messages. Use a user text part for text-to-image; add inlineData for image-to-image.
Minimum array length:
1Show child attributes
Show child attributes
Show child attributes
Show child attributes
Native Gemini generation settings; availability depends on the image model.
Show child attributes
Show child attributes
响应
SSE data stream: each data frame contains a Gemini GenerateContentResponse JSON chunk; no named events or [DONE].
The response is of type string.