Introduction
GitHub Actions workflows can fail long before they hit the documented hourly API cap. The GitHub API also enforces secondary throttling when a workflow sends too many requests too quickly, even if the token technically still has quota left. This is why automation that loops over pull requests, issues, releases, or check runs can start returning 403 and rate-limit errors unpredictably.
Symptoms
- API requests fail with
403 Forbiddenor explicit rate-limit messages - Logs mention
secondary rate limiteven though the core limit is not exhausted - A workflow that performs many small GitHub API calls becomes flaky at scale
- Retries without backoff make the workflow fail faster rather than recover
Common Causes
- A script or action performs too many sequential API calls in a tight loop
- The workflow uses the default token for high-volume automation that would be better batched
- Retry logic replays requests immediately instead of backing off
- Multiple jobs in the same workflow burst against the same repository resources concurrently
Step-by-Step Fix
- 1.Check the current rate-limit state
- 2.Measure before changing code so you know whether the problem is the primary limit, secondary throttling, or both.
curl -s \
-H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/rate_limit- 1.Reduce bursty request patterns
- 2.Replace per-item loops with bulk queries or fewer API writes where possible.
- 3.Add exponential backoff around GitHub API calls
- 4.Immediate retries often guarantee secondary throttling. A short backoff with jitter is much safer.
sleep 5- 1.Use the right token for the workload
- 2.High-volume repository maintenance sometimes needs a different execution model or token strategy than a normal CI run.
Prevention
- Batch GitHub API operations instead of calling the API once per object whenever possible
- Add backoff and jitter to all custom retry logic
- Monitor workflow steps that make large numbers of GitHub API calls
- Avoid parallel jobs that hammer the same GitHub API surfaces unnecessarily