docs
API

Gemini generateContent

Markdown

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

Full guide
POSThttps://api.ailoot.ai/v1beta/models/{model}:generateContent
Gemini generateContent
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."}]}
    ]
  }' 
200 OK
{
  "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

  • x-goog-api-keyheaderrequired

    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

  • modelstringrequired

    A model ID that supports the Gemini format, such as gemini-3.8-flash or gemini-3.1-pro. 27 in total.

POSThttps://api.ailoot.ai/v1beta/models/{model}:streamGenerateContent?alt=sse
GEThttps://api.ailoot.ai/v1beta/models

Request body

  • contentsarrayrequired

    Messages with role (user/model) and parts: text, inlineData/fileData, functionCall/functionResponse. Attachments must be supported inputs for the model.

  • systemInstructionobjectoptional

    A text system instruction: {"parts": [{"text": "…"}]}.

  • generationConfigobjectoptional

    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"].

  • safetySettingsarrayoptional

    Objects with required category and threshold, optional method. Values must be supported by the exact model.

  • toolsarrayoptional

    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.

  • toolConfigobjectoptional

    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.

InputSupported MIME types
Inline imagesimage/png, image/jpeg, image/webp
Documentsapplication/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
Audioaudio/wav, audio/mp3, audio/mpeg, audio/aiff, audio/aac, audio/ogg, audio/flac
Videovideo/mp4, video/mpeg, video/mov, video/quicktime, video/avi, video/x-flv, video/mpg, video/webm, video/wmv, video/3gpp
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.

{"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.

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.

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

  • candidates[].content.partsarray

    The model's response.

  • usageMetadataobject

    promptTokenCount, candidatesTokenCount, totalTokenCount.

Still have questions? Send your Request ID and a description — we'll help you troubleshoot.Contact support
More details

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.