This error occurs when you've exceeded GitHub's API rate limits, typically during unauthenticated requests. The fix is to authenticate with GitHub using a personal access token or SSH key, which significantly increases your rate limit allowance.
GitHub imposes rate limits on API requests to prevent abuse and ensure service availability for all users. When you encounter a "403 rate limit exceeded" error, it means you've made too many requests within a specific time window. The rate limits differ dramatically based on authentication: - **Unauthenticated requests**: 60 requests per hour (shared across your IP address) - **Authenticated requests**: 5,000 requests per hour (tied to your user account) This error commonly occurs when: - Cloning repositories over HTTPS without authentication - Running scripts that make many API calls - Using CI/CD pipelines without proper credentials - Multiple developers sharing the same IP (office network, VPN) - Automated tools or bots hitting the API frequently
First, verify your current rate limit status to understand the scope of the issue:
curl -s https://api.github.com/rate_limit | grep -A 4 '"core"'For authenticated requests (replace TOKEN with your token):
curl -s -H "Authorization: token YOUR_TOKEN" https://api.github.com/rate_limitLook at the remaining value - if it's 0, you've hit the limit. The reset timestamp shows when your limit will refresh.
Generate a personal access token to authenticate your Git operations:
1. Go to GitHub Settings > Developer settings > Personal access tokens > Tokens (classic)
2. Or use this direct link: https://github.com/settings/tokens/new
3. Click "Generate new token (classic)"
4. Give it a descriptive name (e.g., "Git CLI access")
5. Select the required scopes:
- repo - Full control of private repositories
- read:org - If you need to access organization repos
6. Click "Generate token" and copy it immediately (you won't see it again)
For fine-grained tokens (newer, more secure):
1. Go to Developer settings > Personal access tokens > Fine-grained tokens
2. Set repository access and permissions as needed
Store your credentials so Git uses them automatically:
Option A: Using Git credential helper (recommended)
# Enable credential caching (stores in memory for 1 hour)
git config --global credential.helper cache
# Or store credentials permanently (less secure but convenient)
git config --global credential.helper storeThen run any Git command that requires auth - you'll be prompted for username and password. Use your GitHub username and the PAT as password.
Option B: Include token in remote URL (not recommended for shared machines)
git remote set-url origin https://[email protected]/username/repo.gitOption C: Using GitHub CLI (gh)
# Install gh CLI and authenticate
gh auth loginThis automatically configures Git credentials.
SSH keys provide authenticated access without tokens and aren't subject to the same rate limits:
Generate an SSH key (if you don't have one):
ssh-keygen -t ed25519 -C "[email protected]"Add the public key to GitHub:
# Copy your public key
cat ~/.ssh/id_ed25519.pubThen add it at: https://github.com/settings/ssh/new
Update your remote URL to use SSH:
git remote set-url origin [email protected]:username/repo.gitTest the connection:
ssh -T [email protected]You should see: "Hi username! You've successfully authenticated..."
If you need immediate access and can't authenticate right now, you can wait for the limit to reset:
Check when your limit resets:
curl -s -I https://api.github.com/users/octocat | grep -i x-ratelimitThe X-RateLimit-Reset header contains a Unix timestamp. Convert it to a readable time:
# Linux
date -d @1234567890
# macOS
date -r 1234567890Rate limits typically reset hourly. For unauthenticated requests, the 60-request limit resets every hour.
For CI/CD pipelines, use repository secrets to store your token:
GitHub Actions:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }} # Built-in token for same repo
# Or use a PAT for cross-repo access:
# token: ${{ secrets.PAT_TOKEN }}GitLab CI:
variables:
GIT_CREDENTIALS: "https://oauth2:${GITHUB_TOKEN}@github.com"Jenkins:
Store the token in Jenkins credentials and inject it:
withCredentials([string(credentialsId: 'github-token', variable: 'TOKEN')]) {
sh 'git clone https://${TOKEN}@github.com/user/repo.git'
}### Understanding GitHub Rate Limits
GitHub has several types of rate limits:
| Type | Unauthenticated | Authenticated |
|------|-----------------|---------------|
| Core API | 60/hour | 5,000/hour |
| Search API | 10/minute | 30/minute |
| GraphQL API | N/A | 5,000 points/hour |
| Git operations | Varies | Higher limits |
### GitHub Enterprise Considerations
If you're using GitHub Enterprise Server, rate limits are configured by your administrator and may differ from github.com. Check with your admin or consult your instance's documentation.
### Conditional Requests
Use conditional requests to avoid counting against your rate limit when data hasn't changed:
# Save the ETag from a previous request
curl -H "If-None-Match: \"abc123\"" https://api.github.com/repos/owner/repo
# Returns 304 Not Modified if unchanged (doesn't count against limit)### Token Scopes and Rate Limits
Different token scopes don't affect rate limits - all authenticated requests get 5,000/hour. However, using a token with minimal required scopes is a security best practice.
### Secondary Rate Limits
GitHub also has secondary rate limits based on:
- Concurrent requests (avoid parallel bulk operations)
- Total requests per minute (even if under hourly limit)
- CPU time for expensive operations
If you hit secondary limits, you'll see a different error message asking you to slow down.
### Monitoring Rate Limit Usage
For applications making many API calls, monitor your usage:
curl -s -H "Authorization: token YOUR_TOKEN" \
https://api.github.com/rate_limit | jq '.resources.core'Implement exponential backoff when limits are low:
import time
remaining = int(response.headers['X-RateLimit-Remaining'])
if remaining < 10:
reset_time = int(response.headers['X-RateLimit-Reset'])
sleep_time = reset_time - time.time() + 1
time.sleep(max(0, sleep_time))kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git
fatal: unable to access: no_proxy configuration not working
How to fix 'no_proxy configuration not working' in Git
fatal: unable to read tree object in treeless clone
How to fix 'unable to read tree object in treeless clone' in Git