Modo por lotes

El modo por lotes de la API de Gemini está diseñado para procesar grandes volúmenes de solicitudes de forma asíncrona con el 50% del costo estándar. El tiempo de respuesta objetivo es de 24 horas, pero, en la mayoría de los casos, es mucho más rápido.

Usa el modo por lotes para tareas a gran escala y no urgentes, como el preprocesamiento de datos o la ejecución de evaluaciones en las que no se requiere una respuesta inmediata.

Crea un trabajo por lotes

Tienes dos formas de enviar tus solicitudes en el modo por lotes:

  • Solicitudes intercaladas: Es una lista de objetos GenerateContentRequest incluidos directamente en tu solicitud de creación por lotes. Esto es adecuado para lotes más pequeños que mantienen el tamaño total de la solicitud por debajo de los 20 MB. El resultado que devuelve el modelo es una lista de objetos inlineResponse.
  • Archivo de entrada: Un archivo JSON Lines (JSONL) en el que cada línea contiene un objeto GenerateContentRequest completo. Este método se recomienda para solicitudes más grandes. El resultado que devuelve el modelo es un archivo JSONL en el que cada línea es un objeto GenerateContentResponse o un objeto de estado.

Solicitudes intercaladas

Para una pequeña cantidad de solicitudes, puedes incorporar directamente los objetos GenerateContentRequest dentro de tu objeto BatchGenerateContentRequest. En el siguiente ejemplo, se llama al método BatchGenerateContent con solicitudes intercaladas:

Python


from google import genai
from google.genai import types

client = genai.Client()

# A list of dictionaries, where each is a GenerateContentRequest
inline_requests = [
    {
        'contents': [{
            'parts': [{'text': 'Tell me a one-sentence joke.'}],
            'role': 'user'
        }]
    },
    {
        'contents': [{
            'parts': [{'text': 'Why is the sky blue?'}],
            'role': 'user'
        }]
    }
]

inline_batch_job = client.batches.create(
    model="models/gemini-2.5-flash",
    src=inline_requests,
    config={
        'display_name': "inlined-requests-job-1",
    },
)

print(f"Created batch job: {inline_batch_job.name}")

REST

curl https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:batchGenerateContent \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-X POST \
-H "Content-Type:application/json" \
-d '{
    "batch": {
        "display_name": "my-batch-requests",
        "input_config": {
            "requests": {
                "requests": [
                    {
                        "request": {"contents": [{"parts": [{"text": "Describe the process of photosynthesis."}]}]},
                        "metadata": {
                            "key": "request-1"
                        }
                    },
                    {
                        "request": {"contents": [{"parts": [{"text": "Describe the process of photosynthesis."}]}]},
                        "metadata": {
                            "key": "request-2"
                        }
                    }
                ]
            }
        }
    }
}'

Puedes usar cualquier solicitud que usarías en el modo no interactivo (o interactivo). Por ejemplo, podrías especificar la temperatura, las instrucciones del sistema o incluso pasar otras modalidades. En el siguiente ejemplo, se muestran algunas solicitudes intercaladas que contienen una instrucción del sistema para una de las solicitudes:

inline_requests_list = [
    {'contents': [{'parts': [{'text': 'Write a short poem about a cloud.'}]}]},
    {'contents': [{'parts': [{'text': 'Write a short poem about a cat.'}]}], 'system_instructions': {'parts': [{'text': 'You are a cat. Your name is Neko.'}]}}
]

Del mismo modo, también puedes especificar las herramientas que se usarán para una solicitud. En el siguiente ejemplo, se muestra una solicitud que habilita la herramienta de Búsqueda de Google:

inline_requests_list = [
    {'contents': [{'parts': [{'text': 'Who won the euro 1998?'}]}]},
    {'contents': [{'parts': [{'text': 'Who won the euro 2025?'}]}], 'tools': [{'google_search ': {}}]}
]

Archivo de entrada

Para conjuntos más grandes de solicitudes, prepara un archivo de líneas JSON (JSONL). Cada línea de este archivo debe ser un objeto JSON que contenga una clave definida por el usuario y un objeto de solicitud, en el que la solicitud sea un objeto GenerateContentRequest válido. La clave definida por el usuario se usa en la respuesta para indicar qué resultado corresponde a qué solicitud. Por ejemplo, la solicitud con la clave definida como request-1 tendrá su respuesta anotada con el mismo nombre de clave.

Este archivo se sube con la API de File. El tamaño máximo de archivo permitido para un archivo de entrada es de 2 GB.

El siguiente es un ejemplo de un archivo JSONL. Puedes guardarlo en un archivo llamado my-batch-requests.json:

{"key": "request-1", "request": {"contents": [{"parts": [{"text": "Describe the process of photosynthesis."}]}], "generation_config": {"temperature": 0.7}}}
{"key": "request-2", "request": {"contents": [{"parts": [{"text": "What are the main ingredients in a Margherita pizza?"}]}]}}

Al igual que con las solicitudes intercaladas, puedes especificar otros parámetros, como instrucciones del sistema, herramientas o cualquier otra configuración en cada solicitud JSON.

Puedes subir este archivo con la API de File, como se muestra en el siguiente ejemplo. Si trabajas con entrada multimodal, puedes hacer referencia a otros archivos subidos dentro de tu archivo JSONL.

Python


from google import genai
from google.genai import types

client = genai.Client()

# Create a sample JSONL file
with open("my-batch-requests.jsonl", "w") as f:
    requests = [
        {"key": "request-1", "request": {"contents": [{"parts": [{"text": "Describe the process of photosynthesis."}]}]}},
        {"key": "request-2", "request": {"contents": [{"parts": [{"text": "What are the main ingredients in a Margherita pizza?"}]}]}}
    ]
    for req in requests:
        f.write(json.dumps(req) + "\n")

# Upload the file to the File API
uploaded_file = client.files.upload(
    file='my-batch-requests.jsonl',
    config=types.UploadFileConfig(display_name='my-batch-requests', mime_type='jsonl')
)

print(f"Uploaded file: {uploaded_file.name}")

REST

tmp_batch_input_file=batch_input.tmp
echo -e '{"contents": [{"parts": [{"text": "Describe the process of photosynthesis."}]}], "generationConfig": {"temperature": 0.7}}\n{"contents": [{"parts": [{"text": "What are the main ingredients in a Margherita pizza?"}]}]}' > batch_input.tmp
MIME_TYPE=$(file -b --mime-type "${tmp_batch_input_file}")
NUM_BYTES=$(wc -c < "${tmp_batch_input_file}")
DISPLAY_NAME=BatchInput

tmp_header_file=upload-header.tmp

# Initial resumable request defining metadata.
# The upload url is in the response headers dump them to a file.
curl "https://generativelanguage.googleapis.com/upload/v1beta/files \
-D "${tmp_header_file}" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "X-Goog-Upload-Protocol: resumable" \
-H "X-Goog-Upload-Command: start" \
-H "X-Goog-Upload-Header-Content-Length: ${NUM_BYTES}" \
-H "X-Goog-Upload-Header-Content-Type: ${MIME_TYPE}" \
-H "Content-Type: application/jsonl" \
-d "{'file': {'display_name': '${DISPLAY_NAME}'}}" 2> /dev/null

upload_url=$(grep -i "x-goog-upload-url: " "${tmp_header_file}" | cut -d" " -f2 | tr -d "\r")
rm "${tmp_header_file}"

# Upload the actual bytes.
curl "${upload_url}" \
-H "Content-Length: ${NUM_BYTES}" \
-H "X-Goog-Upload-Offset: 0" \
-H "X-Goog-Upload-Command: upload, finalize" \
--data-binary "@${tmp_batch_input_file}" 2> /dev/null > file_info.json

file_uri=$(jq ".file.uri" file_info.json)

En el siguiente ejemplo, se llama al método BatchGenerateContent con el archivo de entrada subido con la API de File:

Python


# Assumes `uploaded_file` is the file object from the previous step
file_batch_job = client.batches.create(
    model="gemini-2.5-flash",
    src=uploaded_file.name,
    config={
        'display_name': "file-upload-job-1",
    },
)

print(f"Created batch job: {file_batch_job.name}")

REST

BATCH_INPUT_FILE='files/123456' # File ID
curl https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:batchGenerateContent \
-X POST \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type:application/json" \
-d "{
    'batch': {
        'display_name': 'my-batch-requests',
        'input_config': {
            'requests': {
                'file_name': ${BATCH_INPUT_FILE}
            }
        }
    }
}"

Cuando crees un trabajo por lotes, se devolverá un nombre de trabajo. Usa este nombre para supervisar el estado del trabajo y recuperar los resultados una vez que se complete el trabajo.

A continuación, se muestra un ejemplo de un resultado que contiene un nombre de trabajo:


Created batch job from file: batches/123456789

Supervisa el estado del trabajo

Usa el nombre de la operación que obtuviste cuando creaste el trabajo por lotes para sondear su estado. El campo de estado del trabajo por lotes indicará su estado actual. Un trabajo por lotes puede tener uno de los siguientes estados:

  • JOB_STATE_PENDING: Se creó el trabajo y se espera a que el servicio lo procese.
  • JOB_STATE_SUCCEEDED: El trabajo se completó correctamente. Ahora puedes recuperar los resultados.
  • JOB_STATE_FAILED: El trabajo falló. Consulta los detalles del error para obtener más información.
  • JOB_STATE_CANCELLED: El usuario canceló el trabajo.

Puedes sondear el estado del trabajo periódicamente para verificar si se completó.

Python


# Use the name of the job you want to check
# e.g., inline_batch_job.name from the previous step
job_name = "YOUR_BATCH_JOB_NAME"  # (e.g. 'batches/your-batch-id')
batch_job = client.batches.get(name=job_name)

completed_states = set([
    'JOB_STATE_SUCCEEDED',
    'JOB_STATE_FAILED',
    'JOB_STATE_CANCELLED',
])

print(f"Polling status for job: {job_name}")
batch_job = client.batches.get(name=job_name) # Initial get
while batch_job.state.name not in completed_states:
  print(f"Current state: {batch_job.state.name}")
  time.sleep(30) # Wait for 30 seconds before polling again
  batch_job = client.batches.get(name=job_name)

print(f"Job finished with state: {batch_job.state.name}")
if batch_job.state.name == 'JOB_STATE_FAILED':
    print(f"Error: {batch_job.error}")

Recuperando resultados

Una vez que el estado del trabajo indique que tu trabajo por lotes se completó correctamente, los resultados estarán disponibles en el campo response.

Python

import json

# Use the name of the job you want to check
# e.g., inline_batch_job.name from the previous step
job_name = "YOUR_BATCH_JOB_NAME"
batch_job = client.batches.get(name=job_name)

if batch_job.state.name == 'JOB_STATE_SUCCEEDED':

    # If batch job was created with a file
    if batch_job.dest and batch_job.dest.file_name:
        # Results are in a file
        result_file_name = batch_job.dest.file_name
        print(f"Results are in file: {result_file_name}")

        print("Downloading result file content...")
        file_content = client.files.download(file=result_file_name)
        # Process file_content (bytes) as needed
        print(file_content.decode('utf-8'))

    # If batch job was created with inline request
    elif batch_job.dest and batch_job.dest.inlined_responses:
        # Results are inline
        print("Results are inline:")
        for i, inline_response in enumerate(batch_job.dest.inlined_responses):
            print(f"Response {i+1}:")
            if inline_response.response:
                # Accessing response, structure may vary.
                try:
                    print(inline_response.response.text)
                except AttributeError:
                    print(inline_response.response) # Fallback
            elif inline_response.error:
                print(f"Error: {inline_response.error}")
    else:
        print("No results found (neither file nor inline).")
else:
    print(f"Job did not succeed. Final state: {batch_job.state.name}")
    if batch_job.error:
        print(f"Error: {batch_job.error}")

REST

BATCH_NAME="batches/123456" # Your batch job name

curl https://generativelanguage.googleapis.com/v1beta/$BATCH_NAME \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type:application/json" 2> /dev/null > batch_status.json

if jq -r '.done' batch_status.json | grep -q "false"; then
    echo "Batch has not finished processing"
fi

batch_state=$(jq -r '.metadata.state' batch_status.json)
if [[ $batch_state = "JOB_STATE_SUCCEEDED" ]]; then
    if [[ $(jq '.response | has("inlinedResponses")' batch_status.json) = "true" ]]; then
        jq -r '.response.inlinedResponses' batch_status.json
        exit
    fi
    responses_file_name=$(jq -r '.response.responsesFile' batch_status.json)
    curl https://generativelanguage.googleapis.com/download/v1beta/$responses_file_name:download?alt=media \
    -H "x-goog-api-key: $GEMINI_API_KEY" 2> /dev/null
elif [[ $batch_state = "JOB_STATE_FAILED" ]]; then
    jq '.error' batch_status.json
elif [[ $batch_state == "JOB_STATE_CANCELLED" ]]; then
    echo "Batch was cancelled by the user"
fi

Cancela un trabajo por lotes

Puedes cancelar un trabajo por lotes en curso con su nombre. Cuando se cancela un trabajo, se detiene el procesamiento de solicitudes nuevas.

Python

# Cancel a batch job
client.batches.cancel(name=batch_job_to_cancel.name)

REST

BATCH_NAME="batches/123456" # Your batch job name

# Cancel the batch
curl https://generativelanguage.googleapis.com/v1beta/$BATCH_NAME:cancel \
-H "x-goog-api-key: $GEMINI_API_KEY" \

# Confirm that the status of the batch after cancellation is JOB_STATE_CANCELLED
curl https://generativelanguage.googleapis.com/v1beta/$BATCH_NAME \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type:application/json" 2> /dev/null | jq -r '.metadata.state'

Borra un trabajo por lotes

Puedes borrar un trabajo por lotes existente con su nombre. Cuando se borra un trabajo, este deja de procesar solicitudes nuevas y se quita de la lista de trabajos por lotes.

Python

# Delete a batch job
client.batches.delete(name=batch_job_to_delete.name)

REST

BATCH_NAME="batches/123456" # Your batch job name

# Cancel the batch
curl https://generativelanguage.googleapis.com/v1beta/$BATCH_NAME:delete \
-H "x-goog-api-key: $GEMINI_API_KEY" \

Detalles técnicos

  • Modelos admitidos: El modo por lotes admite una variedad de modelos de Gemini. Consulta la página Modelos para obtener la lista más reciente de modelos compatibles. Las modalidades admitidas para el modo por lotes son las mismas que las admitidas en la API interactiva (o modo no por lotes).
  • Precios: El uso del modo por lotes se cobra al 50% del costo estándar de la API interactiva para el modelo equivalente.
  • Objetivo de nivel de servicio (SLO): Los trabajos por lotes están diseñados para completarse en un plazo de 24 horas. Muchos trabajos pueden completarse mucho más rápido, según su tamaño y la carga actual del sistema.
  • Almacenamiento en caché: El almacenamiento en caché del contexto está habilitado para las solicitudes por lotes. Si una solicitud de tu lote genera un acierto de caché, los tokens almacenados en caché se cobran al mismo precio que el tráfico en modo no por lotes.

Prácticas recomendadas

  • Usa archivos de entrada para solicitudes grandes: Para una gran cantidad de solicitudes, siempre usa el método de entrada de archivos para una mejor administración y para evitar alcanzar los límites de tamaño de la solicitud para la llamada BatchGenerateContent en sí. Ten en cuenta que hay un límite de tamaño de archivo de 2 GB por archivo de entrada.
  • Control de errores: Verifica el batchStats para failedRequestCount después de que se complete un trabajo. Si usas la salida de archivos, analiza cada línea para verificar si es un objeto GenerateContentResponse o de estado que indica un error para esa solicitud específica.
  • Envía trabajos una sola vez: La creación de un trabajo por lotes no es idempotente. Si envías la misma solicitud de creación dos veces, se crearán dos trabajos por lotes separados.
  • Divide los lotes muy grandes: Si bien el tiempo de respuesta objetivo es de 24 horas, el tiempo de procesamiento real puede variar según la carga del sistema y el tamaño del trabajo. En el caso de los trabajos grandes, considera dividirlos en lotes más pequeños si se necesitan resultados intermedios antes.

¿Qué sigue?

Consulta el notebook del modo por lotes para ver más ejemplos.