Gemini - API - automatización - vibecoding
6 min lectura

Gemini API: cuándo reintentar un 429 o 503 sin duplicar acciones

Distingue cuota de saturación, prueba reintentos limitados con Node y evita que un agente repita correos, publicaciones o cobros.

Gemini API: cuándo reintentar un 429 o 503 sin duplicar acciones

Tu app recibe un pedido para resumir diez documentos. Gemini responde 503. El agente vuelve a llamar, pero el usuario ya hizo clic otra vez y al final se generan dos resultados, dos correos o dos cobros. Otro día aparece un 429, el servidor insiste veinte veces y consume aún más capacidad. El error no está en usar reintentos: está en tratar todos los fallos como si fueran la misma espera.

Esta guía resuelve la decisión en el servidor al llamar a generateContent de Gemini. Ya publicamos una prueba de interfaz para conservar el mensaje del usuario cuando una app devuelve 429; aquí la pregunta es diferente: cuándo repetir la llamada al proveedor, cuándo detenerla y cómo evitar que una generación repetida ejecute dos veces una acción externa. No he probado una cuenta real de Gemini para este artículo; el ejemplo de Node se ejecutó contra respuestas simuladas.

429 y 503 piden decisiones distintas

La tabla oficial de errores de la API de generación describe 429 RESOURCE_EXHAUSTED como un límite de solicitudes, tokens, cuota diaria o gasto. Por eso «reintenta siempre» es una mala política: una cuota diaria agotada o un tope de gasto no se arreglan esperando dos segundos. Mira el mensaje de error y los límites de tu proyecto en AI Studio. Los límites se aplican por proyecto, no por clave API, y pueden variar por modelo y nivel.

503 UNAVAILABLE indica que el servicio puede estar sobrecargado o sin capacidad. Un miembro del equipo de Google aclaró en su foro que 503 no equivale a haber agotado la cuota y recomendó espera progresiva con pocos intentos, o Batch API para trabajo no inmediato. La distinción no garantiza que el siguiente intento vaya a funcionar. Pon un presupuesto de tiempo y una salida visible para el usuario.

Diagrama de decisiones: 429 de RPM o TPM permite espera corta, cuota diaria o gasto exige detenerse, y 503 admite reintentos limitados

Una función que falla con control

Este ejemplo usa Node 18 o superior y la API generateContent. Recibe la clave y el modelo desde tu servidor; nunca expongas la clave en el navegador. Devuelve el texto solo después de una respuesta válida. Reintenta hasta tres intentos en 503. Para 429 solo reintenta si el mensaje identifica un límite corto de RPM/TPM; si la causa es ambigua, diaria o de gasto, se detiene para revisión. El texto del proveedor puede cambiar: el filtro es deliberadamente conservador y no reemplaza observar tus logs.

// Node 18+. A demo of bounded retries for the legacy Gemini generateContent API.
// Set GEMINI_API_KEY and GEMINI_MODEL to make a real request.
const pause = ms => new Promise(resolve => setTimeout(resolve, ms));

export async function generateWithRetry({ prompt, key, model, fetcher = fetch, wait = pause }) {
  if (!prompt || !key || !model) throw new Error('prompt, key, and model are required');
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:generateContent`;
  for (let attempt = 1; attempt <= 3; attempt++) {
    const response = await fetcher(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'x-goog-api-key': key },
      body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }] }),
      signal: AbortSignal.timeout(20000),
    });
    const data = await response.json().catch(() => ({}));
    if (response.ok) return data.candidates?.[0]?.content?.parts?.map(p => p.text ?? '').join('') ?? '';

    const message = String(data.error?.message ?? '');
    const shortLimit = response.status === 429 &&
      /per.minute|per.second|RPM|TPM|rate limit/i.test(message) &&
      !/daily|per.day|RPD|spend|billing|balance|prepay/i.test(message);
    const temporary = response.status === 503 || shortLimit;
    if (!temporary || attempt === 3) {
      throw new Error(`Gemini HTTP ${response.status}: ${message || 'check logs and project limits'}`);
    }
    const seconds = Number(response.headers?.get('retry-after'));
    const delay = Number.isFinite(seconds) && seconds > 0
      ? Math.min(seconds * 1000, 10000)
      : Math.min(500 * 2 ** (attempt - 1) + Math.random() * 250, 10000);
    await wait(delay);
  }
}

Para usarla, guarda la función como gemini-retry.mjs y llama generateWithRetry({prompt, key: process.env.GEMINI_API_KEY, model: process.env.GEMINI_MODEL}) desde tu ruta de servidor. Define el modelo que tengas habilitado; no copies una versión antigua sin revisar la lista vigente de modelos. La función respeta un Retry-After numérico hasta diez segundos y, si no existe, espera progresivamente con un poco de variación para evitar que todos tus trabajadores reintenten a la vez. Un fallo de red o timeout se devuelve como error en este ejemplo; decide aparte si es seguro repetirlo.

Comprueba la política sin consumir cuota

Inyecté un fetcher falso y una espera falsa y ejecuté cinco escenarios en Node: dos respuestas 503 antes de éxito, un 429 identificado como RPM antes de éxito, y tres errores que deben detenerse en la primera llamada (cuota diaria, gasto y 403). La prueba local pasó. Eso valida las ramas del código, no demuestra la capacidad, latencia ni formato exacto de los errores de tu cuenta. En producción registra estado HTTP, código/mensaje, modelo, proyecto, número de intento y duración sin guardar prompts sensibles innecesariamente. Observa los límites reales en AI Studio.

En la interfaz conserva el texto original, informa que no hubo respuesta final y ofrece reintentar cuando corresponda. Si el resultado se usa para enviar correo, publicar un artículo o cobrar, separa esa acción de la generación y protégela con un identificador único persistente: volver a llamar al modelo no debe volver a ejecutar el efecto externo. El ejemplo no implementa ese registro de idempotencia porque depende de tu base de datos y de la acción concreta; prometer que el fetch por sí solo impide duplicados sería falso.

Cuándo usar otra ruta

Si procesas cientos de documentos que no necesitan una respuesta inmediata, evalúa Batch API. Si los 429 son recurrentes, reduce concurrencia, tamaño del contexto y frecuencia antes de pedir más límite. Si el 503 persiste después de tres intentos, no dejes al usuario mirando un spinner infinito: conserva la tarea como pendiente o permite retomarla. Mide tasa de éxito después del primer reintento, latencia p95 y costo por tarea terminada para decidir si esta política mejora tu producto.

Fuentes y alcance

Google: errores de generateContent; límites por proyecto; orientación del equipo Gemini sobre 429/503. Consulta la documentación del endpoint que uses: la nueva Interactions API tiene sus propios códigos y formato. Fecha de revisión: 10 de noviembre de 2026.

Recibe lo nuevo en tu correo

Workshops gratis, herramientas y novedades de IA aplicadas a negocio, sin humo.

🎓 Workshops en vivo🛠️ Herramientas nuevas⚡ Novedades de IA

Sin spam. Te das de baja cuando quieras.

Comentarios

Aún no hay comentarios. Sé el primero.

Deja un comentario