docs
API

Gemini generateContent

Markdown

Formato nativo de Google Gemini: para Gemini CLI, Google GenAI SDK y clientes REST.

Guía completa
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": "Inventa tres nombres para una cafetería."}]}
    ]
  }' 
200 OK
{
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": [{ "text": "1. «Grano y vapor» 2. «Molienda matutina» 3. «Cafetería de la esquina»" }]
      },
      "finishReason": "STOP"
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 9,
    "candidatesTokenCount": 27,
    "totalTokenCount": 36
  },
  "modelVersion": "gemini-3.8-flash"
}

Autenticación

  • x-goog-api-keyheaderobligatorio

    Clave sk-…. También se admiten Authorization: Bearer o x-api-key. Por compatibilidad con Google, también se admite ?key= en la URL, con la prioridad más baja; ?api_key= se rechaza con 400. Recomendamos usar un encabezado para que la clave no aparezca en los registros.

Ruta

  • modelstringobligatorio

    ID de un modelo con formato Gemini, por ejemplo, gemini-3.8-flash o gemini-3.1-pro. Hay 27 en total.

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

Cuerpo de la solicitud

  • contentsarrayobligatorio

    Mensajes con role (user/model) y parts: text, inlineData/fileData, functionCall/functionResponse. Los adjuntos deben ser de los tipos que admite el modelo como entrada.

  • systemInstructionobjectopcional

    Instrucción de sistema en formato de texto: {"parts": [{"text": "…"}]}.

  • generationConfigobjectopcional

    generationConfig admite temperature, topP, topK, maxOutputTokens, thinkingConfig, responseMimeType (text/plain o application/json), responseSchema o responseJsonSchema (objeto JSON), stopSequences (arreglo de cadenas), candidateCount: 1, un seed de tipo entero, y los valores numéricos presencePenalty y frequencyPenalty. Los rangos válidos y las funciones disponibles dependen del modelo específico; para obtener una respuesta JSON, elige un único formato de esquema compatible y application/json. En los modelos de texto, responseModalities solo admite ["TEXT"].

  • safetySettingsarrayopcional

    Objetos con los campos obligatorios category y threshold y el campo opcional method. Los valores deben ser compatibles con el modelo específico.

  • toolsarrayopcional

    Las funciones del cliente functionDeclarations las ejecuta tu aplicación. Los modelos de texto también admiten {"googleSearch": {}} y {"urlContext": {}}, por separado o juntos en el arreglo tools. Ni Google Search ni URL context se pueden combinar con functionDeclarations en una misma solicitud. Para generar imágenes solo se admiten funciones del cliente. No se admiten otras herramientas integradas ni caché explícita. Search/URL context pueden requerir una reserva por el límite completo de entrada; los valores reales de toolUsePromptTokenCount se contabilizan como tokens de entrada.

  • toolConfigobjectopcional

    functionCallingConfig: mode (AUTO/ANY/NONE/VALIDATED/MODE_UNSPECIFIED), allowedFunctionNames.

Si el modelo no admite un campo, un adjunto o una combinación de herramientas, la solicitud se rechaza antes de reservar saldo y no se cobra nada. Se devuelve HTTP 400 con status INVALID_ARGUMENT y la clase invalid_request (en ErrorInfo.reason — INVALID_REQUEST). Un JSON no válido, errores en los parámetros de ruta y otros fallos de validación también devuelven 400; lee el mensaje de error y el Request ID. Las capacidades dependen del modelo: consulta sus formatos y entradas en el catálogo.

PDF, audio, video y enlaces

En los modelos Gemini que admiten el tipo de entrada correspondiente, parts[].inlineData contiene mimeType y datos base64 no vacíos; el PDF debe ser un archivo PDF real. fileData contiene una dirección pública https en fileUri y el tipo MIME del archivo. En YouTube se puede omitir el tipo MIME. http, las direcciones privadas, gs:// y los objetos de Google Files API no se admiten, tampoco la carga mediante Google Files API. En los videos se puede agregar videoMetadata: startOffset/endOffset como cadenas en segundos (por ejemplo, 1.5s) y fps debe ser mayor que 0 y no superar 24. El tamaño del cuerpo, el contexto, la entrada admitida y el límite de respuesta se validan según el modelo y el formato.

EntradaTipos MIME admitidos
Imágenes inlineimage/png, image/jpeg, image/webp
Documentosapplication/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"},
    )
    # Un solo envío. Si se agota el tiempo de espera, consulta el Request ID en tu historial de uso antes de reintentar.
    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

# Proporciona un archivo local real, ya sea PDF, audio o video, y su tipo MIME compatible.
path, mime = pathlib.Path(sys.argv[1]), sys.argv[2]
payload = {"contents": [{"role": "user", "parts": [
    {"text": "Describe brevemente el contenido del archivo."},
    {"inlineData": {"mimeType": mime,
                    "data": base64.b64encode(path.read_bytes()).decode()}},
]}]}
print(generate("gemini-3.8-flash", payload))
# Adjunto alternativo: {"fileData": {"mimeType": mime, "fileUri": public_https_url}}
# Para una URL real de YouTube: {"fileData": {"fileUri": youtube_url}}

Search y URL context

Las funciones del cliente functionDeclarations las ejecuta tu aplicación. Los modelos de texto también admiten {"googleSearch": {}} y {"urlContext": {}}, por separado o juntos en el arreglo tools. Ni Google Search ni URL context se pueden combinar con functionDeclarations en una misma solicitud. Para generar imágenes solo se admiten funciones del cliente. No se admiten otras herramientas integradas ni caché explícita. Search/URL context pueden requerir una reserva por el límite completo de entrada; los valores reales de toolUsePromptTokenCount se contabilizan como tokens de entrada.

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

Imágenes de salida

Solo gemini-3.1-flash-image puede devolver imágenes en el formato Gemini. Esta función es distinta de /v1/images/generations: los demás modelos de imágenes no funcionan aquí. Este modelo admite hasta 131072 tokens de entrada y 32768 de respuesta; el costo completo de la respuesta más larga se reserva por adelantado.

responseModalities : un conjunto no vacío de TEXT/IMAGE, sin repeticiones. 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. Este modelo admite texto y PNG/JPEG/WebP inline como entrada; no admite PDF/audio/video/fileData ni Search/URL context integrados. La disponibilidad y los precios para tu clave se encuentran en el catálogo y en 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"},
    )
    # Un solo envío. Si se agota el tiempo de espera, consulta el Request ID en tu historial de uso antes de reintentar.
    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": "Acuarela: un faro al amanecer"}]}],
    "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)

Ciclo completo de llamadas a funciones

El cliente ejecuta las funciones. Envía de vuelta todo el content original del modelo con thoughtSignature sin cambios, y luego functionResponse con el mismo name y el mismo id, si estaba presente en 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"},
    )
    # Un solo envío. Si se agota el tiempo de espera, consulta el Request ID en tu historial de uso antes de reintentar.
    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": "Multiplicar dos números",
    "parameters": {"type": "object", "properties": {
        "a": {"type": "number"}, "b": {"type": "number"}},
        "required": ["a", "b"]},
}]}]
contents = [{"role": "user", "parts": [{"text": "Multiplica 7 por 8 con la función"}]}]
for turn in range(4):  # Limita el ciclo de herramientas; no vuelvas a enviar una solicitud cuyo resultado no esté claro.
    reply = generate("gemini-3.8-flash", {"contents": contents, "tools": tools})
    content = reply["candidates"][0]["content"]
    contents.append(content)  # Conserva TODAS (ALL) las partes originales (parts) y thoughtSignature sin cambios.
    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")

Respuesta

  • candidates[].content.partsarray

    Respuesta del modelo.

  • usageMetadataobject

    promptTokenCount, candidatesTokenCount, totalTokenCount.

¿Tienes alguna pregunta? Envíanos el Request ID y una descripción; te ayudaremos a resolverla.Contactar a soporte