Resolving GitHub API Rate Limit (403/429) & Authentication (401) Errors
Fix GitHub API rate limits (403/429), timeouts, and 401 errors. Learn how to diagnose API limits, implement backoff strategies, and authenticate requests.
- Unauthenticated requests are strictly limited to 60 per hour; authenticate using a Personal Access Token (PAT) or GitHub App for up to 5,000 to 15,000 requests/hour.
- A 403 Forbidden or 429 Too Many Requests status typically indicates you have triggered GitHub's primary limits or secondary abuse detection mechanisms.
- Check the X-RateLimit-Remaining and Retry-After HTTP response headers to dynamically manage API pacing and retry logic.
- A 401 Unauthorized error means your PAT is missing, expired, incorrectly formatted in the header, or lacks SAML SSO authorization for the target organization.
- 502 Server Errors and connection timeouts usually occur when querying massive datasets; switch to GraphQL or implement strict pagination to resolve them.
| Method | When to Use | Time to Implement | Risk / Impact |
|---|---|---|---|
| Add Personal Access Token | Hitting the default unauthenticated 60/hr limit | 5 minutes | Low (ensure token is securely stored) |
| Conditional Requests (ETag) | Polling data frequently that rarely changes | 30 minutes | Low (saves massive API quota) |
| Exponential Backoff | Encountering secondary rate limits (429/403) | 1-2 hours | Low (improves overall pipeline stability) |
| Migrate to GitHub Apps | Enterprise automation requiring >5k requests/hour | 2-4 hours | Medium (requires App installation and lifecycle management) |
| GraphQL API Migration | Experiencing 502s or timeouts on large REST endpoints | 1-2 days | Medium (requires query rewriting, high reward) |
Understanding GitHub API Errors
When automating CI/CD pipelines, gathering repository metrics, or managing pull requests, encountering GitHub API errors is practically a rite of passage for DevOps and Site Reliability Engineers. The most disruptive errors revolve around rate limits (403 and 429), authentication failures (401), and upstream timeouts (502). Let's break down why these happen and how to build resilient systems to handle them.
1. The 403 and 429 Rate Limit Errors
GitHub aggressively protects its infrastructure from noisy neighbors. If you execute a script without authentication, you are severely capped at 60 requests per hour per IP address. Once exceeded, GitHub intercepts the request and responds with:
HTTP/2 403 Forbidden
{
"message": "API rate limit exceeded for xxx.xxx.xxx.xxx.",
"documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting"
}
However, even with a Personal Access Token (PAT) yielding 5,000 requests per hour, your scripts might still fail. Concurrent bursts or expensive requests trigger Secondary Rate Limits. In these scenarios, GitHub typically returns a 429 Too Many Requests or a 403 Forbidden with a warning about abuse detection mechanisms. This happens if you make too many concurrent requests, rapidly mutate repository state (e.g., creating 50 branches in 10 seconds), or consume too much CPU time on GitHub's backend.
2. The 401 Unauthorized Error
A 401 Bad credentials error implies your authentication mechanism was entirely rejected. Common root causes include:
- Token Expiration: The PAT has reached its predefined expiration date.
- Malformed Headers: Passing the token as
token ghp_xyzinstead of the requiredBearer ghp_xyzformat in theAuthorizationheader. - SAML SSO Restrictions: The target repository is owned by an Organization enforcing SAML Single Sign-On, but your token hasn't been explicitly authorized for that organization.
3. Timeouts and 502 Bad Gateway Errors
These errors (HTTP 502 Server Error or standard TCP timeouts) occur when GitHub's upstream servers take too long to compile the response. Common culprits include querying the Search API with overly broad parameters, requesting massive monolithic diffs, or hitting the GET /pulls endpoint on massive monorepos without proper pagination constraints.
Step-by-Step Troubleshooting and Resolution
Step 1: Diagnose Your Current Rate Limit Status
Before modifying automation code, determine exactly which limit you have hit. GitHub provides a dedicated rate limit endpoint that does not consume your quota.
You should always inspect the HTTP headers returned by GitHub API calls. Critical headers include:
x-ratelimit-limit: Your total quota for the current window.x-ratelimit-remaining: Requests left in the current window.x-ratelimit-reset: The exact epoch timestamp when your quota fully resets.retry-after: The number of seconds you must wait before retrying (absolutely crucial for recovering from secondary limits).
Step 2: Implement Robust Authentication (Fixing 401s)
If you are unauthenticated, generate a fine-grained PAT or classic token. Always inject it via the Authorization header rather than via URL parameters (which is deprecated and insecure).
If you still receive a 401 error:
- Navigate to GitHub Developer Settings and verify the token is active.
- If accessing an enterprise org repo, click "Configure SSO" next to the active token and click "Authorize" for your specific organization.
- Validate your HTTP request formatting. It must look exactly like:
Authorization: Bearer <YOUR_TOKEN>.
Step 3: Implement Exponential Backoff and Jitter (Fixing 429s/403s)
To survive secondary rate limits gracefully, your HTTP client must be capable of handling 429 and 403 responses by parsing the Retry-After header. If the header is missing, you must fall back to exponential backoff.
A resilient retry loop in Python or Go should intercept the 403/429 status code, calculate the sleep duration specified in the headers (plus a small random jitter to avoid thundering herd problems), sleep, and retry. Avoid firing concurrent requests from multiple threads; GitHub's abuse mechanisms explicitly penalize parallel mutations.
Step 4: Utilize Conditional Requests to Save Quota
To drastically reduce your API consumption for polling workloads, use Conditional Requests. When you successfully fetch a resource, GitHub returns an ETag header and a Last-Modified header. Store these values locally or in your database.
On your next polling cycle, send these headers in your request:
If-None-Match: <ETag_Value>If-Modified-Since: <Timestamp>
If the resource hasn't changed, GitHub returns a HTTP 304 Not Modified with an empty body. Crucially, 304 responses do not count against your primary API rate limit, allowing you to poll aggressively without exhausting your 5,000/hour limit.
Step 5: Mitigate 502s and Timeouts
When a 502 Server Error or a timeout occurs, GitHub failed to compile the response in time. To fix this:
- Reduce Page Sizes: Decrease your
per_pagequery parameter from the maximum 100 down to 30 or fewer. - Scope Queries: Add
sort,direction, andsinceparameters to limit the dataset. - Migrate to GraphQL: If you are hitting timeouts fetching deeply nested REST resources, switch to the GitHub GraphQL API (v4). GraphQL allows you to traverse relationships and fetch exactly the fields you need in a single HTTP request, massively reducing upstream processing time and preventing query timeouts.
Frequently Asked Questions
#!/bin/bash
# Diagnostic script to check GitHub API Rate Limit and Headers
TOKEN="your_personal_access_token_here"
echo "=== Checking Core Rate Limit Status ==="
curl -s -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/rate_limit | grep -A 5 '"core"'
echo -e "\n=== Making a test request and dumping headers ==="
# We use -i to include HTTP headers in the output
curl -s -i -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/octocat/hello-world | grep -iE "(HTTP/|x-ratelimit|retry-after|x-github-request-id)"
echo -e "\nNote: If 'x-ratelimit-remaining' is 0, wait until the epoch timestamp in 'x-ratelimit-reset'."
echo "If 'retry-after' is present, you MUST pause execution for that many seconds to avoid abuse bans."DevOps Troubleshooting Editorial
Our editorial team consists of Senior DevOps and Site Reliability Engineers dedicated to demystifying cloud infrastructure, CI/CD pipelines, and API integrations.
Sources
- https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
- https://docs.github.com/en/rest/guides/best-practices-for-using-the-rest-api#handling-rate-limits
- https://docs.github.com/en/graphql/overview/resource-limitations
- https://stackoverflow.com/questions/55072704/github-api-rate-limit-exceeded-403-forbidden