How to fix fatal: unable to access: The requested URL returned error: 403 rate limit exceeded in Git

GitBEGINNERMEDIUMUpdated Dec 6, 2025ยท 178 views

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.

Quick fix

Fixes 403

curl -s https://api.github.com/rate_limit | grep -A 4 '"core"'
  1. Check your current rate limit status
  2. Create a GitHub Personal Access Token (PAT)
  3. Configure Git to use your token
  4. Switch to SSH authentication
git โ€” error
curl -s https://api.github.com/rate_limit | grep -A 4 '"core"'
fatal: unable to access: The requested URL returned error: 403 rate limit exceeded

What this error means

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

Typical symptoms

  • Git clone/fetch/pull fails with 'HTTP 403' or 'rate limit exceeded'
  • Error message mentions 'The requested URL returned error: 403'
  • Operations that worked earlier in the day suddenly fail
  • Multiple team members experience failures at the same time
  • CI/CD pipeline jobs fail when accessing GitHub repositories
  • GitHub API responses include 'X-RateLimit-Remaining: 0' header

Common causes

  1. Using HTTPS without authentication (unauthenticated requests have very low limits)
  2. Multiple users or processes sharing the same IP address exhaust the shared limit
  3. Automated scripts or CI/CD pipelines making excessive API calls
  4. Git credential helper not configured, causing repeated unauthenticated requests
  5. Personal access token expired or revoked
  6. Organization has additional rate limiting policies in place
  7. Using GitHub Actions or other automation without proper token configuration

How to fix "fatal: unable to access: The requested URL returned error: 403 rate limit exceeded"

1Check your current rate limit status

First, verify your current rate limit status to understand the scope of the issue:

bash
curl -s https://api.github.com/rate_limit | grep -A 4 '"core"'

For authenticated requests (replace TOKEN with your token):

bash
curl -s -H "Authorization: token YOUR_TOKEN" https://api.github.com/rate_limit

Look at the remaining value - if it's 0, you've hit the limit. The reset timestamp shows when your limit will refresh.

2Create a GitHub Personal Access Token (PAT)

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

3Configure Git to use your token

Store your credentials so Git uses them automatically:

Option A: Using Git credential helper (recommended)

bash
# 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 store

Then 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)

bash
git remote set-url origin https://YOUR_TOKEN@github.com/username/repo.git

Option C: Using GitHub CLI (gh)

bash
# Install gh CLI and authenticate
gh auth login

This automatically configures Git credentials.

4Switch to SSH authentication

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):

bash
ssh-keygen -t ed25519 -C "your_email@example.com"

Add the public key to GitHub:

bash
# Copy your public key
cat ~/.ssh/id_ed25519.pub

Then add it at: https://github.com/settings/ssh/new

Update your remote URL to use SSH:

bash
git remote set-url origin git@github.com:username/repo.git

Test the connection:

bash
ssh -T git@github.com

You should see: "Hi username! You've successfully authenticated..."

5Wait for rate limit reset

If you need immediate access and can't authenticate right now, you can wait for the limit to reset:

Check when your limit resets:

bash
curl -s -I https://api.github.com/users/octocat | grep -i x-ratelimit

The X-RateLimit-Reset header contains a Unix timestamp. Convert it to a readable time:

bash
# Linux
date -d @1234567890

# macOS
date -r 1234567890

Rate limits typically reset hourly. For unauthenticated requests, the 60-request limit resets every hour.

6Configure CI/CD with proper authentication

For CI/CD pipelines, use repository secrets to store your token:

GitHub Actions:

yaml
- 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:

yaml
variables:
  GIT_CREDENTIALS: "https://oauth2:${GITHUB_TOKEN}@github.com"

Jenkins:

Store the token in Jenkins credentials and inject it:

groovy
withCredentials([string(credentialsId: 'github-token', variable: 'TOKEN')]) {
    sh 'git clone https://${TOKEN}@github.com/user/repo.git'
}

Advanced notes

### 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:

bash
# 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:

bash
curl -s -H "Authorization: token YOUR_TOKEN" \
  https://api.github.com/rate_limit | jq '.resources.core'

Implement exponential backoff when limits are low:

python
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))

Frequently asked questions

What does "403" mean?
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.
How do I fix "403"?
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.
What causes "403"?
Using HTTPS without authentication (unauthenticated requests have very low limits) Multiple users or processes sharing the same IP address exhaust the shared limit Automated scripts or CI/CD pipelines making excessive API calls
Is the "403" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading