I’m encountering a 429 Too Many Requests error when using the @google/genai package to generate interview questions in a Next.js client-side application. The error persists despite implementing a retry mechanism, and it seems to be related to exceeding the free-tier quota limits for the Gemini 2.5 Pro model. Below is the relevant code and the error log.
The application is designed to generate interview questions using the Gemini 2.5 Pro model based on user input (job role, description, and experience). The withRetry function attempts to handle the 429 error with exponential backoff (5s, 10s, 20s), but it fails after three attempts. The error details indicate I’ve exceeded the free-tier quotas for daily tokens, daily requests, per-minute requests, and per-minute tokens.
my code:
GeminiAIModal.js:
import { GoogleGenAI } from '@google/genai';
const apiKey = process.env.NEXT_PUBLIC_GEMINI_API_KEY;
if (!apiKey) {
throw new Error('NEXT_PUBLIC_GEMINI_API_KEY is not set in environment variables');
}
const ai = new GoogleGenAI({ apiKey });
const config = {
thinkingConfig: {
thinkingBudget: -1, // Unlimited thinking budget as per your UI
},
responseMimeType: 'application/json', // Structured output
};
const model = 'gemini-2.5-pro';
async function withRetry(fn, maxRetries = 3, baseDelay = 5000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = baseDelay * Math.pow(2, attempt - 1);
console.log(`429 error, retrying after ${delay}ms (attempt ${attempt}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
export async function generateInterviewQuestions(prompt) {
try {
const result = await withRetry(async () => {
const response = await ai.models.generateContentStream({
model,
config,
contents: [
{
role: 'user',
parts: [{ text: prompt }],
},
],
});
let fullText = '';
for await (const chunk of response) {
fullText += chunk.text;
}
return fullText;
});
return JSON.parse(result);
} catch (error) {
console.error('Error in generateInterviewQuestions:', error);
throw new Error('Failed to generate interview questions from Gemini API');
}
}
AddCardWithDialog.jsx:
'use client';
import React, { useState } from 'react';
import { Dialog } from '@headlessui/react';
import { XMarkIcon } from '@heroicons/react/24/solid';
import { generateInterviewQuestions } from '../../utils/GeminiAIModal';
function AddCardWithDialog({ title, description }) {
const [isOpen, setIsOpen] = useState(false);
const [form, setForm] = useState({
role: '',
description: '',
experience: '',
});
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const handleStartInterview = async (e) => {
e.preventDefault();
setIsLoading(true);
setError(null);
const contents = `job position: ${form.role},
job description: ${form.description},
no of year experience: ${form.experience},
depends of those info give me 5 interview qs in json format
give qs and answers in json format`;
try {
const result = await generateInterviewQuestions(contents);
console.log('Interview Questions:', result);
setIsOpen(false);
} catch (err) {
console.error('Error generating questions:', err);
setError('Failed to generate interview questions. Please try again.');
} finally {
setIsLoading(false);
}
};
const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
return (
<>
<div
onClick={() => setIsOpen(true)}
className='p-10 border rounded-2xl bg-secondary hover:scale-105 hover:shadow-lg cursor-pointer transition-all'
>
<h2 className='text-lg text-center font-semibold text-gray-800'>{title}</h2>
</div>
<Dialog open={isOpen} onClose={() => setIsOpen(false)} className='relative z-50'>
<div className='fixed inset-0 bg-black/30 backdrop-blur-sm' />
<div className='fixed inset-0 flex items-center justify-center p-4'>
<Dialog.Panel className='relative w-full max-w-lg rounded-2xl bg-white p-6 shadow-xl'>
<button
onClick={() => setIsOpen(false)}
className='absolute top-4 right-4 text-gray-500 hover:text-red-600 transition-colors'
>
<XMarkIcon className='w-6 h-6' />
</button>
<Dialog.Title className='text-2xl font-bold text-gray-900 mb-1'>
Tell us more about Job you are interviewing
</Dialog.Title>
<Dialog.Description className='text-gray-500 mb-6'>
Add Details about job position, your skills and year of experience
</Dialog.Description>
<div className='space-y-4'>
<div>
<label className='block text-sm font-medium mb-1 text-gray-700'>
Job Position / Role name
</label>
<input
type='text'
name='role'
value={form.role}
onChange={handleChange}
className='w-full border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500'
/>
</div>
<div>
<label className='block text-sm font-medium mb-1 text-gray-700'>
Job Description / Tech Stack in Short
</label>
<textarea
name='description'
rows={3}
value={form.description}
onChange={handleChange}
className='w-full border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none'
/>
</div>
<div>
<label className='block text-sm font-medium mb-1 text-gray-700'>
No of Year Experience
</label>
<input
type='number'
name='experience'
value={form.experience}
onChange={handleChange}
className='w-full border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500'
/>
</div>
</div>
{error && (
<p className='mt-4 text-red-600 text-sm'>{error}</p>
)}
<div className='mt-6 flex justify-end space-x-3'>
<button
onClick={() => setIsOpen(false)}
className='px-4 py-2 rounded-md bg-gray-200 text-gray-600 hover:bg-gray-300'
disabled={isLoading}
>
Cancel
</button>
<button
onClick={handleStartInterview}
className='px-4 py-2 rounded-md bg-blue-600 text-white hover:bg-blue-700 disabled:bg-blue-400'
disabled={isLoading}
>
{isLoading ? 'Generating...' : 'Start Interview'}
</button>
</div>
</Dialog.Panel>
</div>
</Dialog>
</>
);
}
export default AddCardWithDialog;
the error log:
<project-path>utilsGeminiAIModal.js:24 429 error, retrying after 5000ms (attempt 1/3)
<project-path>utilsGeminiAIModal.js:36
POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse 429 (Too Many Requests)
[...stack trace...]
<project-path>utilsGeminiAIModal.js:24 429 error, retrying after 10000ms (attempt 2/3)
[...similar 429 error...]
<project-path>utilsGeminiAIModal.js:54 Error in generateInterviewQuestions: ClientError: got status: 429 . {"error":{"message":"{n "error": {n "code": 429,n "message": "You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits.",n "status": "RESOURCE_EXHAUSTED",n "details": [n {n "@type": "type.googleapis.com/google.rpc.QuotaFailure",n "violations": [n {n "quotaMetric": "generativelanguage.googleapis.com/generate_content_free_tier_input_token_count",n "quotaId": "GenerateContentInputTokensPerModelPerDay-FreeTier",n "quotaDimensions": {n "location": "global",n "model": "gemini-2.5-pro"n }n },n {n "quotaMetric": "generativelanguage.googleapis.com/generate_content_free_tier_requests",n "quotaId": "GenerateRequestsPerDayPerProjectPerModel-FreeTier",n "quotaDimensions": {n "model": "gemini-2.5-pro",n "location": "global"n }n },n {n "quotaMetric": "generativelanguage.googleapis.com/generate_content_free_tier_requests",n "quotaId": "GenerateRequestsPerMinutePerProjectPerModel-FreeTier",n "quotaDimensions": {n "model": "gemini-2.5-pro",n "location": "global"n }n },n {n "quotaMetric": "generativelanguage.googleapis.com/generate_content_free_tier_input_token_count",n "quotaId": "GenerateContentInputTokensPerModelPerMinute-FreeTier",n "quotaDimensions": {n "location": "global",n "model": "gemini-2.5-pro"n }n }n ]n },n {n "@type": "type.googleapis.com/google.rpc.Help",n "links": [n {n "description": "Learn more about Gemini API quotas",n "url": "https://ai.google.dev/gemini-api/docs/rate-limits"n }n ]n },n {n "@type": "type.googleapis.com/google.rpc.RetryInfo",n "retryDelay": "49s"n }n ]n }n}n","code":429,"status":""}}
at throwErrorIfNotOK (<project-path>[email protected]:11201:1)
[...stack trace...]
<project-path>componentsuiAddCardWithDialog.jsx:34 Error generating questions: Error: Failed to generate interview questions from Gemini API
at generateInterviewQuestions (<project-path>utilsGeminiAIModal.js:55:15)
[...stack trace...]
