/v1/models/{model}:generateContent/v1/models/{model}:generateContent
Gemini REST-style generateContent and streamGenerateContent routes are supported under both /v1 and /v1beta (the Google GenAI SDK default). Authenticate with x-goog-api-key — the SDK's default.
Routes#
{model} is an id from GET /v1/models — for example gemini-3-5-flash-lite.
Authentication#
All three forms work; use whichever your client sends. See Authentication.
Request#
curl "https://api.atptoken.ai/v1/models/<model>:generateContent" \
-H "x-goog-api-key: atp-..." \
-H "Content-Type: application/json" \
-d '{
"contents": [{ "parts": [{ "text": "hi" }] }]
}'contents, systemInstruction and generationConfig (for example maxOutputTokens, temperature) use the Gemini field names.
Response#
Response
{
"candidates": [
{
"content": {
"parts": [{ "text": "Hello there, friend!" }],
"role": "model"
},
"finishReason": "STOP"
}
],
"createTime": "2026-10-07T08:32:26.357216Z",
"modelVersion": "gemini-3-5-flash-lite",
"responseId": "01M3F7W9CNBGWDEBPB0NBDS0AD",
"usageMetadata": {
"candidatesTokenCount": 5,
"promptTokenCount": 31,
"totalTokenCount": 36
}
}Token counts are in usageMetadata, and responseId is a Gateway id, not a Google one.
Streaming#
streamGenerateContent always answers with Server-Sent Events (Content-Type: text/event-stream), whether or not you add ?alt=sse. This differs from Google's own API, which returns a single JSON array when alt=sse is missing — a client that expects that array needs ?alt=sse handling instead. The Google GenAI SDK already requests SSE and works unchanged.
Request
curl -N "https://api.atptoken.ai/v1beta/models/gemini-3-5-flash-lite:streamGenerateContent?alt=sse" \
-H "x-goog-api-key: atp-..." \
-H "Content-Type: application/json" \
-d '{ "contents": [{ "role": "user", "parts": [{ "text": "Say hi in 3 words" }] }] }'Response
data: {"candidates":[{"content":{"parts":[{"text":"Hello there"}],"role":"model"},"finishReason":null,"index":0}]}
data: {"candidates":[{"content":{"parts":[{"text":", friend!"}],"role":"model"},"finishReason":null,"index":0}]}
data: {"candidates":[{"content":{"parts":[{"text":""}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"candidatesTokenCount":5,"promptTokenCount":31,"totalTokenCount":36}}Each chunk carries only the new text. finishReason is null until the last chunk, which also carries usageMetadata. There is no [DONE] line — the stream ends when the connection closes.
Differences from Google's API#
Errors#
See Error codes for every status and what to check first.