Source: https://ailoot.ai/docs/en/api/gemini/
Canonical: https://ailoot.ai/docs/en/api/gemini/
Language: en

[API](https://ailoot.ai/docs/en/) 

# Gemini generateContent

[Markdown](https://ailoot.ai/docs/en/api/gemini/index.md)

The native Google Gemini format for Gemini CLI, Google GenAI SDK, and REST clients.

POST`https://api.ailoot.ai/v1beta/models/{model}:generateContent`

Gemini generateContent

```bash
curl "https://api.ailoot.ai/v1beta/models/gemini-3.8-flash:generateContent" \
  -H "x-goog-api-key: $AILOOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [
      {"role": "user", "parts": [{"text": "Come up with three names for a coffee shop."}]}
    ]
  }' 
```

```python
from google import genai
from google.genai import types
import os

client = genai.Client(
    api_key=os.environ["AILOOT_API_KEY"],
    http_options=types.HttpOptions(base_url="https://api.ailoot.ai"),  # without /v1beta
)

resp = client.models.generate_content(
    model="gemini-3.8-flash",
    contents="Come up with three names for a coffee shop.",
)
print(resp.text)
```

```js
import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI({
  apiKey: process.env.AILOOT_API_KEY,
  httpOptions: { baseUrl: "https://api.ailoot.ai" }, // without /v1beta
});

const resp = await ai.models.generateContent({
  model: "gemini-3.8-flash",
  contents: "Come up with three names for a coffee shop.",
});
console.log(resp.text);
```

200 OK

```json
{
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": [{ "text": "1. «Bean and Steam» 2. «Morning Grind» 3. «The Corner Café»" }]
      },
      "finishReason": "STOP"
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 9,
    "candidatesTokenCount": 27,
    "totalTokenCount": 36
  },
  "modelVersion": "gemini-3.8-flash"
}
```

## Authentication

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `x-goog-api-key` | header | required | Key `sk-…`. You can also use `Authorization: Bearer` or `x-api-key`. For compatibility with Google, the API also accepts `?key=` in the URL, with the lowest priority; `?api_key=` is rejected with 400. We recommend using a header to keep the key out of logs. |

## Path

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | required | A model ID that supports the Gemini format, such as `gemini-3.8-flash` or `gemini-3.1-pro`. 27 in total. |

POST`https://api.ailoot.ai/v1beta/models/{model}:streamGenerateContent?alt=sse`

GET`https://api.ailoot.ai/v1beta/models`

## Request body

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `contents` | array | required | Messages with `role` (`user`/`model`) and `parts`: text, inlineData/fileData, functionCall/functionResponse. Attachments must be supported inputs for the model. |
| `systemInstruction` | object | optional | A text system instruction: `{"parts": [{"text": "…"}]}`. |
| `generationConfig` | object | optional | `generationConfig` accepts `temperature`, `topP`, `topK`, `maxOutputTokens`, `thinkingConfig`, `responseMimeType` (`text/plain` or `application/json`), `responseSchema` or `responseJsonSchema` (a JSON object), `stopSequences` (an array of strings), `candidateCount: 1`, an integer `seed`, numeric `presencePenalty` and `frequencyPenalty`. Supported ranges and capabilities depend on the exact model; for a JSON response, choose one supported schema format and `application/json`. For text models, `responseModalities` — only `["TEXT"]`. |
| `safetySettings` | array | optional | Objects with required `category` and `threshold`, optional `method`. Values must be supported by the exact model. |
| `tools` | array | optional | Client-side `functionDeclarations` are executed by your application. Text models also accept `{"googleSearch": {}}` and `{"urlContext": {}}`: individually or together, in the array named `tools`. Neither Google Search nor URL context can be combined with `functionDeclarations` in the same request. Only client-side functions are available for image output. Other built-in tools and explicit cache are not supported. Search/URL context may require a reserve based on the full input limit; the actual tokens reported in `toolUsePromptTokenCount` are counted as input tokens. |
| `toolConfig` | object | optional | `functionCallingConfig`: mode (AUTO/ANY/NONE/VALIDATED/MODE\_UNSPECIFIED), allowedFunctionNames. |

If the model does not support a field, attachment, or tool combination, the request is rejected before funds are reserved, and nothing is charged. You will receive HTTP `400` with `status` `INVALID_ARGUMENT` and class `invalid_request` (in `ErrorInfo.reason` — `INVALID_REQUEST`). Invalid JSON, path parameter errors, and other validation errors also return `400`; read the error message and Request ID. Capabilities vary by model: check its formats and inputs in the catalog.

## PDF, audio, video, and links

For Gemini models that support the relevant input, `parts[].inlineData` contains `mimeType` and non-empty base64 data; a PDF must be an actual PDF file. `fileData` contains a public `https` URL in `fileUri` and the file's MIME type. The MIME type can be omitted for YouTube. `http`, private URLs, `gs://` and Google Files API objects are not supported, nor are uploads through the Google Files API. For video, you can add `videoMetadata`: `startOffset`/`endOffset` as strings in seconds (for example, `1.5s`) and `fps` greater than 0 and no greater than 24. Request body size, context, supported inputs, and response limits are checked for the model and format.

| Input | Supported MIME types |
| --- | --- |
| Inline images | image/png, image/jpeg, image/webp |
| Documents | application/pdf; text/plain, text/html, text/css, text/csv, text/xml, text/rtf, text/markdown, text/javascript, application/x-javascript, text/x-python, application/x-python |
| Audio | audio/wav, audio/mp3, audio/mpeg, audio/aiff, audio/aac, audio/ogg, audio/flac |
| Video | video/mp4, video/mpeg, video/mov, video/quicktime, video/avi, video/x-flv, video/mpg, video/webm, video/wmv, video/3gpp |

```python
import json, os, urllib.request

def generate(model, payload):
    req = urllib.request.Request(
        "https://api.ailoot.ai/v1beta/models/" + model + ":generateContent",
        data=json.dumps(payload).encode(),
        headers={"x-goog-api-key": os.environ["AILOOT_API_KEY"],
                 "Content-Type": "application/json"},
    )
    # One send. After a timeout, check the Request ID in your usage history before retrying.
    with urllib.request.urlopen(req, timeout=300) as response:
        print("Request ID:", response.headers.get("X-Request-ID"))
        return json.load(response)

import base64, pathlib, sys

# Supply a real local PDF, audio or video file and its supported MIME type.
path, mime = pathlib.Path(sys.argv[1]), sys.argv[2]
payload = {"contents": [{"role": "user", "parts": [
    {"text": "Briefly describe the contents of the file."},
    {"inlineData": {"mimeType": mime,
                    "data": base64.b64encode(path.read_bytes()).decode()}},
]}]}
print(generate("gemini-3.8-flash", payload))
# Alternative attachment: {"fileData": {"mimeType": mime, "fileUri": public_https_url}}
# For a real YouTube URL: {"fileData": {"fileUri": youtube_url}}
```

## Search and URL context

Client-side `functionDeclarations` are executed by your application. Text models also accept `{"googleSearch": {}}` and `{"urlContext": {}}`: individually or together, in the array named `tools`. Neither Google Search nor URL context can be combined with `functionDeclarations` in the same request. Only client-side functions are available for image output. Other built-in tools and explicit cache are not supported. Search/URL context may require a reserve based on the full input limit; the actual tokens reported in `toolUsePromptTokenCount` are counted as input tokens.

```json
{"tools": [{"googleSearch": {}}, {"urlContext": {}}]}
```

## Image output

Images in Gemini-format responses are supported only by `gemini-3.1-flash-image`. This is a separate capability from `/v1/images/generations`: other image models do not work here. This model accepts up to 131072 input tokens and returns up to 32768 output tokens; the full cost of the longest possible response is reserved in advance.

`responseModalities` — a non-empty set of TEXT/IMAGE values with no duplicates. `imageConfig.imageSize`: 1K/2K/4K; `aspectRatio`: 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9, 1:4, 4:1, 1:8, 8:1. This model accepts text and inline PNG/JPEG/WebP images; PDF/audio/video/fileData and built-in Search/URL context are not accepted here. Availability and pricing for your key are listed in the catalog and in `GET /v1/models`.

```python
import json, os, urllib.request

def generate(model, payload):
    req = urllib.request.Request(
        "https://api.ailoot.ai/v1beta/models/" + model + ":generateContent",
        data=json.dumps(payload).encode(),
        headers={"x-goog-api-key": os.environ["AILOOT_API_KEY"],
                 "Content-Type": "application/json"},
    )
    # One send. After a timeout, check the Request ID in your usage history before retrying.
    with urllib.request.urlopen(req, timeout=300) as response:
        print("Request ID:", response.headers.get("X-Request-ID"))
        return json.load(response)

import base64, pathlib

reply = generate("gemini-3.1-flash-image", {
    "contents": [{"role": "user", "parts": [{"text": "Watercolor: a lighthouse at dawn"}]}],
    "generationConfig": {
        "responseModalities": ["TEXT", "IMAGE"],
        "imageConfig": {"aspectRatio": "1:1", "imageSize": "1K"},
    },
})
index = 0
for candidate in reply.get("candidates", []):
    for part in candidate.get("content", {}).get("parts", []):
        if "text" in part:
            print(part["text"])
        blob = part.get("inlineData", {})
        if blob.get("mimeType", "").startswith("image/") and blob.get("data"):
            index += 1
            extension = {"image/png": "png", "image/jpeg": "jpg",
                         "image/webp": "webp"}.get(blob["mimeType"], "bin")
            pathlib.Path(f"gemini-image-v{index}.{extension}").write_bytes(
                base64.b64decode(blob["data"]))
print("All returned images:", index)
```

## Full function calling cycle

The client executes functions. Send back the model's entire original content, including thoughtSignature, unchanged, then functionResponse with the same name and id if id was included in functionCall.

```python
import json, os, urllib.request

def generate(model, payload):
    req = urllib.request.Request(
        "https://api.ailoot.ai/v1beta/models/" + model + ":generateContent",
        data=json.dumps(payload).encode(),
        headers={"x-goog-api-key": os.environ["AILOOT_API_KEY"],
                 "Content-Type": "application/json"},
    )
    # One send. After a timeout, check the Request ID in your usage history before retrying.
    with urllib.request.urlopen(req, timeout=300) as response:
        print("Request ID:", response.headers.get("X-Request-ID"))
        return json.load(response)

tools = [{"functionDeclarations": [{
    "name": "multiply", "description": "Multiply two numbers",
    "parameters": {"type": "object", "properties": {
        "a": {"type": "number"}, "b": {"type": "number"}},
        "required": ["a", "b"]},
}]}]
contents = [{"role": "user", "parts": [{"text": "Multiply 7 by 8 using the function"}]}]
for turn in range(4):  # Bound the tool loop; do not resend a request whose result is unclear.
    reply = generate("gemini-3.8-flash", {"contents": contents, "tools": tools})
    content = reply["candidates"][0]["content"]
    contents.append(content)  # Keep ALL original parts and thoughtSignature unchanged.
    results = []
    for part in content.get("parts", []):
        call = part.get("functionCall")
        if not call:
            continue
        if call["name"] != "multiply":
            raise ValueError("Unexpected function")
        args = call["args"]
        result = {"name": call["name"], "response": {"value": args["a"] * args["b"]}}
        if "id" in call:
            result["id"] = call["id"]
        results.append({"functionResponse": result})
    if not results:
        print(content)
        break
    contents.append({"role": "user", "parts": results})
else:
    raise RuntimeError("Tool loop limit reached")
```

## Response

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `candidates[].content.parts` | array |  | The model's response. |
| `usageMetadata` | object |  | `promptTokenCount`, `candidatesTokenCount`, `totalTokenCount`. |

Paid Gemini responses are delivered in full once the cost has been calculated, rather than word by word. The Gemini stream has no auxiliary lines, so allow extra time in your client timeout.

Still have questions? Send your Request ID and a description — we'll help you troubleshoot.[Contact support](mailto:support@ailoot.ai)

## API connection

### How do I use the Gemini API format with Ailoot?

The native Google Gemini format for Gemini CLI, Google GenAI SDK, and REST clients.

## Ailoot · The company and its project portfolio





Ailoot combines established operating experience, a large client base, stable service and ongoing KPI control.





The company and its project portfolio: millions of customers; monthly sales in the billions.





Ailoot brings that operating experience to API access for AI features in customer products, agents, applications and automations.
