Gemini API
Google Gemini-compatible endpoints — generateContent, streamGenerateContent, countTokens.
/v1beta/models/{model}:generateContentA chat endpoint compatible with the Google Gemini API request body. The model name and action live in the path ({model}:generateContent); authenticate with the x-goog-api-key header.
Google's official ?key= query parameter is not supported — the key must be in the x-goog-api-key header (the Google GenAI SDK uses that header by default).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
contents | array | Yes | Conversation content: each item has a role (user/model) and parts (text or inline media). |
systemInstruction | object | No | System prompt ({ "parts": [{ "text": … }] }). |
generationConfig | object | No | Generation parameters: temperature, maxOutputTokens, etc. |
curl "https://soleapi.com/v1beta/models/gemini-3.7-flash:generateContent" \
-H "x-goog-api-key: $SOLEAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"parts": [{"text": "Write a haiku about the sea"}]}]
}'Response
{
"candidates": [
{
"content": {
"parts": [{ "text": "Waves fold into foam..." }],
"role": "model"
},
"finishReason": "STOP"
}
],
"usageMetadata": {
"promptTokenCount": 14,
"candidatesTokenCount": 23,
"totalTokenCount": 37
}
}Streaming generation
/v1beta/models/{model}:streamGenerateContentAppend ?alt=sse to stream incrementally over SSE — each data: line is a response fragment; without alt=sse you get one complete JSON array. See Streaming.
Count tokens
/v1beta/models/{model}:countTokenscurl "https://soleapi.com/v1beta/models/gemini-3.7-flash:countTokens" \
-H "x-goog-api-key: $SOLEAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"parts": [{"text": "Write a haiku about the sea"}]}]
}'
# => { "totalTokens": 14 }Image generation (Nano Banana)
Gemini image models (gemini-2.5-flash-image, gemini-3-pro-image, gemini-3.1-flash-image, gemini-3.1-flash-lite-image) have no separate image endpoint — they use the same generateContent / streamGenerateContent calls shown above. The request body is identical to a text conversation; just declare that you want images in generationConfig.responseModalities. Generated images come back as inlineData (base64) inside parts.
| Field | Type | Description |
|---|---|---|
generationConfig.responseModalities | array | ["TEXT", "IMAGE"]; use ["IMAGE"] for image-only output. |
generationConfig.imageConfig.aspectRatio | string | 1:1, 3:2, 2:3, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9. |
generationConfig.imageConfig.imageSize | string | 1K (default), 2K, 4K; gemini-3.1-flash-image also accepts 0.5K, gemini-3.1-flash-lite-image only 1K, and gemini-2.5-flash-image does not support this field. |
curl "https://soleapi.com/v1beta/models/gemini-3.1-flash-image:generateContent" \
-H "x-goog-api-key: $SOLEAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"parts": [{"text": "A watercolor lighthouse at dawn"}]}],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"],
"imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"}
}
}'{
"candidates": [
{
"content": {
"parts": [
{ "text": "Here is the lighthouse." },
{ "inlineData": { "mimeType": "image/png", "data": "iVBORw0KGgo..." } }
],
"role": "model"
},
"finishReason": "STOP"
}
],
"usageMetadata": {
"promptTokenCount": 9,
"candidatesTokenCount": 1694,
"candidatesTokensDetails": [
{ "modality": "TEXT", "tokenCount": 14 },
{ "modality": "IMAGE", "tokenCount": 1680 }
],
"totalTokenCount": 1703
}
}Editing an image: put the reference image(s) as inlineData in contents[].parts (up to 14) together with the text instruction; the rest of the request is unchanged.
{
"contents": [{
"parts": [
{ "text": "Turn the sky into a starry night" },
{ "inlineData": { "mimeType": "image/jpeg", "data": "<base64>" } }
]
}],
"generationConfig": { "responseModalities": ["TEXT", "IMAGE"] }
}Images are billed by output tokens (the modality = IMAGE entry in usageMetadata.candidatesTokensDetails); see the model page for rates. A 1K image is roughly 1,120–1,290 tokens, 2K about 1,680, 4K about 2,000–2,520. Streaming works exactly as for text (streamGenerateContent?alt=sse); the image arrives whole in the final chunk.