API Reference

This gateway exposes Gemini-style REST routes and an optional OpenAI-compatible Chat Completions route. Requests use JSON bodies, JSON responses, standard HTTP methods, and API-key authentication.

The upstream is the cookie-authenticated Gemini Web application. The Gemini REST surface is a compatibility layer, not Google AI Studio or the official Gemini Developer API.


Authentication

The API uses API keys to authenticate requests. You can view and manage your API keys in the Admin Dashboard. Your API keys carry many privileges, so be sure to keep them secure!

Bearer Token

All API requests should include your API key in an Authorization HTTP header as follows:

Authorization: Bearer YOUR_API_KEY

Gemini-compatible authentication

Gemini REST clients may use any one of these forms:

x-goog-api-key: YOUR_API_KEY
Authorization: Bearer YOUR_API_KEY
GET /v1beta/models?key=YOUR_API_KEY

Supported Models

Select one of the current Gemini models below and pass its exact ID in the model parameter.

gemini-3.5-flash-lite

The fastest model for short answers and lightweight tasks.

gemini-3.6-flash

The balanced Gemini model for comprehensive everyday assistance.

gemini-3.1-pro

Advanced mathematics, programming, and complex reasoning.


Gemini REST API

These paths match Gemini REST naming. Both v1beta and v1 variants are available.

GET  /v1beta/models
GET  /v1beta/models/{model}
POST /v1beta/models/{model}:generateContent
POST /v1beta/models/{model}:streamGenerateContent
POST /v1beta/models/{model}:countTokens

GET /models accepts pageSize and pageToken, and returns nextPageToken when another page is available.


Generate Content

POST /v1beta/models/{model}:generateContent

Send Gemini contents, parts, and optional systemInstruction.

curl "https://aggeee.io.vn/v1beta/models/gemini-3.6-flash:generateContent" \
  -H "x-goog-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "systemInstruction": {"parts": [{"text": "Answer concisely."}]},
    "contents": [{"role": "user", "parts": [{"text": "Hello!"}]}]
  }'

Response

{
  "candidates": [{
    "content": {"role": "model", "parts": [{"text": "Hello!"}]},
    "finishReason": "STOP",
    "index": 0
  }],
  "usageMetadata": {
    "promptTokenCount": 4,
    "candidatesTokenCount": 2,
    "totalTokenCount": 6
  },
  "modelVersion": "gemini-3.6-flash",
  "responseId": "gateway-generated-response-id"
}

Function Calling

Declare functions using functionDeclarations. The gateway validates the selected name and arguments. Your application executes the function; the gateway never executes arbitrary terminal commands.

{
  "contents": [{"role": "user", "parts": [{"text": "Weather in Hue?"}]}],
  "tools": [{"functionDeclarations": [{
    "name": "get_weather",
    "description": "Get current weather for a city",
      "parametersJsonSchema": {
        "type": "OBJECT",
      "properties": {"city": {"type": "STRING"}},
      "required": ["city"]
    }
  }]}],
  "toolConfig": {"functionCallingConfig": {"mode": "AUTO"}}
}

A requested function is returned as a Gemini part:

{
  "candidates": [{"content": {"role": "model", "parts": [{
    "functionCall": {"name": "get_weather", "args": {"city": "Hue"}}
  }]}}]
}

Execute the approved function and send its result in the next request:

{
  "contents": [
    {"role": "user", "parts": [{"text": "Weather in Hue?"}]},
    {"role": "model", "parts": [{"functionCall": {
      "name": "get_weather", "args": {"city": "Hue"}
    }}]},
    {"role": "function", "parts": [{"functionResponse": {
      "name": "get_weather",
      "response": {"temperature": 31, "condition": "Sunny"}
    }}]}
  ]
}

AUTO: the model may answer normally or request a function.

VALIDATED: same choice behavior as AUTO, with gateway validation of any returned call.

ANY: at least one valid function call is required.

NONE: function calls are disabled.

allowedFunctionNames restricts selection to a declared subset.

parameters and parametersJsonSchema are both accepted but are mutually exclusive. Function selection is prompt-based emulation because Gemini Web does not expose Google's native Function Calling protocol.


Images and Files

Use Gemini-style inlineData. Each inline part is base64 encoded and limited to 20 MiB by this gateway.

import base64
import requests

with open("image.jpg", "rb") as image_file:
    encoded = base64.b64encode(image_file.read()).decode("ascii")

response = requests.post(
    "https://aggeee.io.vn/v1beta/models/gemini-3.6-flash:generateContent",
    headers={"x-goog-api-key": "YOUR_API_KEY"},
    json={"contents": [{"parts": [
        {"text": "Describe this image."},
        {"inlineData": {"mimeType": "image/jpeg", "data": encoded}}
    ]}]},
    timeout=500,
)
response.raise_for_status()

Persistent fileData.fileUri and resumable Files API uploads are unavailable. Use inlineData.


Streaming and Token Counting

POST /v1beta/models/gemini-3.6-flash:streamGenerateContent?alt=sse
POST /v1beta/models/gemini-3.6-flash:countTokens

The streaming route uses Server-Sent Events. The Gemini Web response is collected first, so this compatibility route currently emits one final event instead of token-level deltas.

With ?alt=sse, the response is text/event-stream. Without it, the endpoint returns the final streamed JSON object with application/json.

countTokens and usageMetadata are estimates, not output from Google's tokenizer.

Supported compatibility generation options are candidateCount: 1, stopSequences, responseMimeType: "application/json", responseSchema, and responseJsonSchema. Parameters that cannot be honored by Gemini Web—such as temperature, topP, topK, seed, and maxOutputTokens—return INVALID_ARGUMENT instead of being silently ignored.


Chat Completions

POST /v1/chat/completions

Creates a model response for the given chat conversation.

Request Body

model required string

ID of the model to use (e.g., gemini-3.1-pro).

messages required array

A list of messages comprising the conversation so far. Each object must have a role (system, user, assistant) and content.

stream optional boolean

stream: true is rejected on this route. Use Gemini's :streamGenerateContent endpoint for SSE.

Example Request

import requests

response = requests.post(
    "https://aggeee.io.vn/v1/chat/completions",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "model": "gemini-3.1-pro",
        "messages": [{"role": "user", "content": "Hello!"}],
    },
    timeout=500,
)
response.raise_for_status()
print(response.json()["choices"][0]["message"]["content"])

Response Format

{
  "id": "chatcmpl_a1b2c3d4",
  "object": "chat.completion",
  "created": 1718291039,
  "model": "gemini-3.1-pro",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hi there! How can I help you today?"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 0,
    "completion_tokens": 0,
    "total_tokens": 0
  }
}

Conversation API

Use conversations when you want Gemini to continue the same underlying chat instead of creating a fresh Gemini chat for every request.

POST /v1/chats

Create an empty conversation and receive a conversation_id.

GET /v1/chats

List conversations owned by the current API key.

GET /v1/chats/{conversation_id}

Inspect a conversation and its stored Gemini metadata.

DELETE /v1/chats/{conversation_id}

Delete gateway metadata for a conversation.

Recommended Flow

# First request: no conversation_id, gateway creates a new Gemini chat.
curl https://aggeee.io.vn/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "model": "gemini-3.1-pro",
    "messages": [
      {"role": "user", "content": "My name is Hung."}
    ]
  }'

# Use conversation_id from the first response to continue the same Gemini chat.
curl https://aggeee.io.vn/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "model": "gemini-3.1-pro",
    "conversation_id": "conv_xxx",
    "messages": [
      {"role": "user", "content": "What is my name?"}
    ]
  }'

Conversation Response Fields

{
  "id": "chatcmpl_a1b2c3d4",
  "object": "chat.completion",
  "model": "gemini-3.1-pro",
  "conversation_id": "conv_xxx",
  "gemini_metadata": ["gemini_chat_id", "reply_id", "candidate_id"],
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "Your name is Hung."
      }
    }
  ]
}

Compatibility Matrix

CapabilityStatusNotes
Text generationSupportedGemini Web output in a Gemini REST envelope.
Inline images, PDF, audio, videoSupportedTemporarily uploaded from inlineData.
Function callingEmulatedAllowlist and JSON Schema validated.
StreamingFinal event onlySSE without upstream token deltas.
Token countsEstimatedApproximately one token per four characters.
Safety settings, cached content, service tiersUnavailableRejected explicitly instead of being silently ignored.
Built-in tools (code execution, Google Search, URL context)Unavailable through this contractOnly custom function declarations use the validated compatibility layer.
Persistent Files API, embeddings, caches, tuningUnavailableNot exposed by Gemini Web.

Error Codes

Our API uses conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided (e.g., a required parameter was omitted, or you ran out of credits).

HTTP Code Description Meaning / Resolution
400 Bad Request The request was unacceptable, often due to missing a required parameter (like empty messages).
401 / 403 Unauthorized No valid API key provided, or your API key is deactivated. Check your Authorization header.
500 Server Error Something went wrong on our end (e.g. all backend Gemini accounts are overloaded). The system will Auto-Retry before throwing this error.
503 Service Unavailable No active Gemini accounts are currently available in the system pool.

Example Error Response

{
  "detail": "Unauthorized"
}

Gemini REST error response

{
  "error": {
    "code": 400,
    "message": "contents must be a non-empty array",
    "status": "INVALID_ARGUMENT"
  }
}