Imagen ilustrativa: Unsplash
Si tu aplicación empezó a devolver 429 RESOURCE_EXHAUSTED al llamar a la API de Gemini, lo primero que tienes que hacer no es reintentar: es abrir el cuerpo del error y leer el campo quotaId. Ahí está la respuesta. El caso típico que veo en proyectos reales es este: un backend que genera descripciones de producto funciona toda la mañana, y a media tarde empieza a fallar en cadena. El desarrollador asume que Google está saturado, mete un reintento cada segundo y en media hora tiene el proyecto bloqueado hasta el día siguiente. El problema no era saturación. Era una cuota diaria agotada, y cada reintento la empujaba más lejos.
Los dos errores 429 que no son el mismo error
La API devuelve el mismo código HTTP para dos situaciones que se resuelven de forma opuesta.
El primero es un límite temporal: enviaste demasiadas peticiones o demasiados tokens en un minuto. Se recupera solo. Esperas, reintentas y sigue funcionando.
El segundo es una cuota agotada: consumiste todas las peticiones que tu proyecto tiene asignadas para ese modelo en el día. No se recupera hasta que el contador se reinicia. Aquí reintentar no sirve de nada, y reintentar en bucle es peor que no hacer nada.
La trampa es que el cuerpo del error incluye un campo retryDelay en ambos casos, y en el segundo ese valor es engañoso. En los reportes del foro oficial de desarrolladores aparece un caso claro: un error de cuota diaria devolvía retryDelay: 1s. El desarrollador implementó su reintento con ese valor y siguió recibiendo 429 durante horas. La respuesta del equipo fue que el retryDelay no aplica a agotamiento de cuota, y que hay que mirar el objeto details para distinguir un caso del otro.
Cómo leer el cuerpo del error y saber qué cuota agotaste
Un 429 de cuota diaria trae, dentro de error.details, un bloque de tipo QuotaFailure con cuatro datos que lo dicen todo:
"quotaMetric": "generativelanguage.googleapis.com/generate_content_free_tier_requests"
"quotaId": "GenerateRequestsPerDayPerProjectPerModel-FreeTier"
"quotaValue": "250"
"retryDelay": "1s"
"status": "RESOURCE_EXHAUSTED"
Traducido: el proyecto agotó las 250 peticiones diarias por modelo del nivel gratuito. Fíjate en el detalle que casi todo el mundo pasa por alto: el quotaId termina en FreeTier. Si tu proyecto tiene facturación activa y sigues viendo FreeTier en ese campo, el problema no es que gastaste mucho. Es que tus peticiones no están saliendo por el proyecto que crees.
Este fragmento imprime el diagnóstico en lugar de reintentar a ciegas:
def diagnosticar_429(err_json):
detalles = err_json.get("error", {}).get("details", [])
for d in detalles:
if "QuotaFailure" in d.get("@type", ""):
for v in d.get("violations", []):
qid = v.get("quotaId", "")
print("Cuota:", qid, "| límite:", v.get("quotaValue"))
if "PerDay" in qid:
return "diaria" # no reintentar hoy
return "por_minuto" # backoff y continuar
return "desconocida"
Si la función devuelve diaria, tu código debe dejar de llamar a ese modelo, no dormir un segundo y volver a intentarlo.
Dónde están tus límites reales
Aquí hay un cambio que invalida la mitad de los artículos que vas a encontrar buscando este error. La documentación oficial de límites de la API de Gemini ya no publica una tabla con los valores de RPM, TPM y RPD por modelo. Dice, literalmente, que los límites dependen de varios factores y que se consultan en Google AI Studio. Cualquier tabla de números por modelo que encuentres en un blog es una foto del pasado, y los valores cambian sin aviso.
Los tres sitios donde miras tus números de verdad:
- El panel de límites de Google AI Studio, en
aistudio.google.com/rate-limit, que muestra los límites activos de tu proyecto. - El cuerpo del propio error 429, que te dice la cuota exacta y su valor en el momento del fallo.
- La sección de cuotas de Google Cloud Console para el proyecto, si trabajas con facturación y quieres pedir un aumento.
Lo que sí está documentado y es estable son los niveles de uso y como se desbloquean. Esa es la tabla que importa:
| Nivel | Cómo se desbloquea | Límite de gasto por ventana de 10 min | Para qué sirve en la práctica |
|---|---|---|---|
| Free | Proyecto activo, sin facturación | No aplica | Prototipos y pruebas. Cuota diaria por modelo muy baja |
| Tier 1 | Cuenta de facturación vinculada al proyecto | 10 USD | Primer entorno de producción pequeño |
| Tier 2 | 100 USD pagados y 3 días desde el primer pago | 50 USD | Cargas con picos, varios clientes |
| Tier 3 | 1.000 USD pagados y 30 días desde el primer pago | 200 USD | Procesos masivos y agentes en producción |
Dos matices sobre esa tabla. El límite de gasto por ventana de diez minutos es un tope adicional: aunque no superes RPM ni TPM, un pico de peticiones caras puede dispararlo y devolverte un 429. Y el tiempo de espera de los niveles 2 y 3 no se salta pagando de golpe, porque cuenta desde el primer pago, no desde el importe.
Cinco configuraciones que provocan 429 sin que hayas gastado nada
- Facturación activa pero el proyecto sigue en Free. Es el fallo más reportado. El
quotaIdsigue diciendoFreeTiery la cuota diaria por modelo se queda en el valor gratuito en lugar del de pago. En los reportes del foro la diferencia entre un nivel y otro era de 250 peticiones diarias frente a 10.000. Suele ser propagación reciente de la facturación, y a veces se resuelve solo en unas horas. - La clave de API pertenece a otro proyecto. Vinculaste la facturación al proyecto A y estas llamando con una clave creada en el proyecto B. La cuota que se consume es la de B. Compruébalo en AI Studio antes de abrir un ticket.
- Aplicar
retryDelaya una cuota diaria. Ya lo vimos: convierte un fallo de un modelo en un bucle que puede afectar al resto de tu proyecto. - Alias de modelo que consume otro contador. Hay reportes de peticiones enviadas a una versión de modelo que aparecen contabilizadas bajo otra en el panel de uso. Si el número del panel no cuadra con el tráfico que envías, contrasta siempre contra el
quotaIddel error, que es el dato que la API usa de verdad. - Reintentos en paralelo sin jitter. Diez workers que fallan a la vez y reintentan al mismo milisegundo generan una segunda oleada idéntica. Sin aleatoriedad en la espera, el reintento reproduce el pico que causó el error.
El backoff exponencial que sí funciona
Los SDK oficiales ya implementan reintentos con backoff, así que si estás llamando por HTTP crudo estás reimplementando algo resuelto. Si aun así necesitas control propio, estos son los pasos:
- Clasifica el error antes de reintentar. Cuota diaria: abortar. Límite por minuto o 503: reintentar.
- Empieza en un segundo y duplica en cada intento: 1, 2, 4, 8, 16.
- Suma un valor aleatorio a cada espera, entre cero y el valor de la espera actual.
- Pon un techo de reintentos, cinco es razonable, y un tiempo máximo total.
- Registra el
quotaIdde cada fallo. Si no lo guardas, en producción no vas a saber cuál de las cuotas te tumbó.
import random, time
def llamar_con_backoff(fn, max_intentos=5):
for intento in range(max_intentos):
try:
return fn()
except Error429 as e:
if e.tipo == "diaria":
raise # no tiene sentido esperar
espera = (2 ** intento) + random.uniform(0, 2 ** intento)
time.sleep(espera)
raise RuntimeError("Límite de reintentos alcanzado")
Cómo bajar el consumo antes de pagar más nivel
Subir de nivel es la solución obvia y no siempre la correcta. Cuatro ajustes que reducen el consumo sin tocar la factura:
- Recorta el prompt de entrada. El límite de tokens por minuto se mide sobre la entrada. Un prompt de sistema de dos mil tokens repetido en cada llamada es lo que te está agotando el TPM, no las respuestas.
- Usa el modo por lotes para lo que no es interactivo. Tiene sus propios límites, separados de los de tiempo real, con topes de concurrencia y de tokens encolados por nivel. Mover los trabajos nocturnos ahí libera la cuota del día.
- Cachea el contexto que se repite. Si mandas el mismo documento base en cada petición, estás pagando ese contexto una y otra vez contra tu TPM.
- Reparte por modelo. Las cuotas diarias se cuentan por proyecto y por modelo. Si tienes dos tareas distintas, usar un modelo más ligero para la sencilla te da dos contadores en lugar de uno.
Veredicto crítico
La API de Gemini es de las más generosas para prototipar, y ese es exactamente el motivo por el que su nivel gratuito no sirve para producción. Una cuota diaria por modelo y por proyecto significa que tu aplicación no degrada: se apaga, y se apaga cuando más tráfico tienes. Si lo que construyes tiene usuarios reales, el nivel gratuito no es un plan, es una demo.
Mi crítica de fondo no es a los límites, que son razonables, sino a la observabilidad. Que el mismo código 429 cubra un pico de un minuto y un muro de veinticuatro horas, y que además venga con un retryDelay que en el segundo caso te empuja a hacer justo lo contrario de lo que deberías, es un mal diseño de mensaje de error. Y que la documentación pública haya dejado de listar los límites por modelo mejora la precisión, porque los números cambian, pero dificulta dimensionar un proyecto antes de escribir la primera línea.
Cuándo sí conviene quedarse en el nivel gratuito: pruebas, trabajos por lotes que toleran ejecutarse en varios días, y proyectos personales. Cuándo no: cualquier cosa con tráfico de usuarios, un agente que encadena varias llamadas por tarea, o un proceso con fecha de entrega. Para ese caso, Tier 1 con una cuenta de facturación vinculada cuesta muy poco y cambia el orden de magnitud de la cuota diaria.
Sigue leyendo
- Servidor MCP configurado pero no aparece en el cliente, si el problema está en la capa de herramientas y no en la API.
- Firebase App Check bloquea tus peticiones, otro caso donde el error que llega al cliente no dice la verdad.
- Google Play rechaza tu app Flutter por seguridad de los datos, para cuando el bloqueo no es técnico sino de política.
- El resto está en el índice de guías prácticas.
Fuentes
Límites de uso y niveles de la API de Gemini: ai.google.dev/gemini-api/docs/rate-limits. Guía oficial de resolución de problemas y backoff exponencial: ai.google.dev/gemini-api/docs/troubleshooting. Hilo del foro oficial de desarrolladores sobre el campo retryDelay y el agotamiento de cuota diaria: discuss.ai.google.dev. Reporte de proyectos con facturación activa que siguen consumiendo cuota del nivel gratuito: discuss.ai.google.dev. Documentación de Google Cloud sobre el código de error 429 y sus mitigaciones: docs.cloud.google.com.
No te pierdas lo próximo que publiquemos
Cada día, las noticias y guías de inteligencia artificial que importan, directas a tu correo.
Es gratis. Sin spam. Puedes darte de baja cuando quieras.