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 en tu 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"                         }                     }                 ]             }         }     } }' 

Archivo de entrada

Para conjuntos de solicitudes más grandes, 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 a través de 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  

Configuración de solicitud

Puedes incluir cualquier configuración de solicitud que usarías en una solicitud estándar que no sea por lotes. Por ejemplo, puedes especificar la temperatura, las instrucciones del sistema o incluso pasar otras modalidades. En el siguiente ejemplo, se muestra una solicitud intercalada de ejemplo que contiene 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.'}]}} ] 

De manera similar, 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 ': {}}]} ] 

También puedes especificar salida estructurada. En el siguiente ejemplo, se muestra cómo especificar los parámetros para tus solicitudes por lotes.

from google import genai from pydantic import BaseModel, TypeAdapter  class Recipe(BaseModel):     recipe_name: str     ingredients: list[str]  client = genai.Client()  # A list of dictionaries, where each is a GenerateContentRequest inline_requests = [     {         'contents': [{             'parts': [{'text': 'List a few popular cookie recipes, and include the amounts of ingredients.'}],             'role': 'user'         }],         'config': {             'response_mime_type': 'application/json',             'response_schema': list[Recipe]         }     },     {         'contents': [{             'parts': [{'text': 'List a few popular gluten free cookie recipes, and include the amounts of ingredients.'}],             'role': 'user'         }],         'config': {             'response_mime_type': 'application/json',             'response_schema': list[Recipe]         }     } ]  inline_batch_job = client.batches.create(     model="models/gemini-2.5-flash",     src=inline_requests,     config={         'display_name': "structured-output-job-1"     }, )  # wait for the job to finish job_name = inline_batch_job.name print(f"Polling status for job: {job_name}")  while True:     batch_job_inline = client.batches.get(name=job_name)     if batch_job_inline.state.name in ('JOB_STATE_SUCCEEDED', 'JOB_STATE_FAILED', 'JOB_STATE_CANCELLED', 'JOB_STATE_EXPIRED'):         break     print(f"Job not finished. Current state: {batch_job_inline.state.name}. Waiting 30 seconds...")     time.sleep(30)  print(f"Job finished with state: {batch_job_inline.state.name}")  # print the response for i, inline_response in enumerate(batch_job_inline.dest.inlined_responses):     print(f"\n--- Response {i+1} ---")      # Check for a successful response     if inline_response.response:         # The .text property is a shortcut to the generated text.         print(inline_response.response.text)  

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_RUNNING: El trabajo está en ejecución.
  • 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.
  • JOB_STATE_EXPIRED: El trabajo venció porque se ejecutó o estuvo pendiente durante más de 48 horas. El trabajo no tendrá ningún resultado para recuperar. Puedes volver a enviar el trabajo o dividir las solicitudes en lotes más pequeños.

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',     'JOB_STATE_EXPIRED', ])  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" elif [[ $batch_state == "JOB_STATE_EXPIRED" ]]; then     echo "Batch expired after 48 hours" fi 

Cómo cancelar 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  # Delete the batch job 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 conocer la compatibilidad de cada modelo con el modo por lotes. Las modalidades admitidas para el modo por lotes son las mismas que las admitidas en la API interactiva (o en el 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. Consulta la página de precios para obtener más detalles. Consulta la página de límites de frecuencia para obtener detalles sobre los límites de frecuencia de esta función.
  • 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 2 GB por archivo de entrada.
  • Control de errores: Verifica si hay failedRequestCount en batchStats 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 un objeto de estado que indica un error para esa solicitud específica. Consulta la guía de solución de problemas para obtener un conjunto completo de códigos de error.
  • 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. Para 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.