| 1. What Causes the Error? | 2. Step-by-Step Solution |
| 3. Best Practices | 4. Frequently Asked Questions |
⚠️ GitHub 429 Too Many Requests means your Next.js 14/Node.js backend is sending too many GitHub API requests within a rate-limit window. Check the GitHub API rate-limit headers, authenticate requests, reduce duplicate calls, and add caching/backoff instead of repeatedly retrying immediately.
GitHub returns a 429 Too Many Requests response when an API client exceeds an applicable request limit or is being temporarily throttled. Common causes include:
For a Next.js 14 + Node.js backend, this is primarily a server-side/API integration issue, although browser-side code can also trigger it when it communicates directly with GitHub.
Use a GitHub token through an environment variable rather than making repeated unauthenticated requests. For example, in .env.local:
GITHUB_TOKEN=your_github_token
Do not expose this token through client-side JavaScript or commit it to Git.
A basic server-side request can look like this:
❌ The Problematic Code
const response = await fetch( "https://api.github.com/repos/vercel/next.js" ); const data = await response.json();
This request provides no authentication and can become problematic when your application sends it repeatedly.
✅ The Fixed Code
const response = await fetch(
"https://api.github.com/repos/vercel/next.js",
{
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
},
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.status}`);
}
const data = await response.json();
The important changes are:
Authorization authenticates the API request.Accept explicitly requests GitHub's current JSON API representation.response.ok prevents the application from silently treating an error response as valid data.Before adding retries, inspect the response headers.
const response = await fetch("https://api.github.com/repos/vercel/next.js", {
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
},
});
console.log("Remaining:", response.headers.get("x-ratelimit-remaining"));
console.log("Reset:", response.headers.get("x-ratelimit-reset"));
The x-ratelimit-remaining value helps determine how many requests remain for the applicable rate-limit bucket. The x-ratelimit-reset value provides the reset time as a Unix timestamp.
A common mistake is creating an infinite or aggressive retry loop.
❌ Incorrect Retry Pattern
async function getGithubData() {
const response = await fetch("https://api.github.com/repos/vercel/next.js");
if (response.status === 429) {
return getGithubData();
}
return response.json();
}
This can repeatedly hit the same endpoint while GitHub is throttling the client.
✅ Safer Retry Pattern
async function getGithubData() {
const response = await fetch(
"https://api.github.com/repos/vercel/next.js",
{
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
},
}
);
if (response.status === 429) {
const retryAfter = response.headers.get("retry-after");
if (retryAfter) {
throw new Error(
`GitHub rate limit reached. Retry after ${retryAfter} seconds.`
);
}
throw new Error("GitHub rate limit reached.");
}
if (!response.ok) {
throw new Error(`GitHub API error: ${response.status}`);
}
return response.json();
}
The key difference is that the application stops instead of immediately generating another request.
If multiple users request the same GitHub data, caching can dramatically reduce unnecessary API calls. For example:
const response = await fetch(
"https://api.github.com/repos/vercel/next.js",
{
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
},
next: {
revalidate: 300,
},
}
);
const data = await response.json();
Here, revalidate: 300 tells Next.js to reuse the fetched data for a period instead of requesting the same GitHub resource for every request. For frequently accessed GitHub data, consider an application-level cache such as Redis or another shared cache when your deployment architecture requires it.
Check your application for patterns such as:
fetchGithub(); fetchGithub(); fetchGithub();
Also check:
A single backend request can often serve multiple frontend consumers.
x-ratelimit-remaining so your application can detect approaching limits before requests start failing.A: No. A 429 response comes from the HTTP/API service being accessed. In a Next.js application, the usual problem is excessive or repeated requests from the application to GitHub.
A: Authentication can provide a different API rate-limit allowance, but it does not make the application unlimited. Excessive authenticated requests can still encounter rate limits.
A: No. Avoid immediate retries. Respect available rate-limit or retry information and wait before making another request.
A: Yes. Appropriate caching can reduce repeated requests for identical GitHub data and therefore lower API traffic.
A: Store it in a server-side environment variable such as GITHUB_TOKEN. Do not expose it through a NEXT_PUBLIC_ variable or client-side code.
A: Inspect the response's x-ratelimit-remaining header. The x-ratelimit-reset header can also indicate when the applicable limit resets.
A: A small user base does not necessarily mean low API traffic. Server-side loops, repeated rendering, polling, retries, multiple workers, and uncached requests can generate many GitHub requests quickly.
A: Yes. A retry loop that immediately repeats failed requests can increase request volume and prolong the rate-limiting problem. Use bounded retries with appropriate delays instead.