## TL;DR
The review job is calling the API faster than the installation's hourly budget allows. Find the hot loop, cache repeated reads, back off on 403s, and fetch less per call. The quota is per installation per hour, so every redundant call counts.

## The error
```text
API rate limit exceeded for installation ID failed the review run: how to fix
403: You have exceeded a secondary rate limit. Please wait a few minutes before you try again.
```

## Steps
### 1. Find the hot loop in the run logs
Look at which endpoints the review job called and how many times.
Expected output: one or two endpoints dominate, usually repeated GETs for the same PR data on every retry.

### 2. Cache repeated reads
Store PR metadata, file lists, and diffs after the first fetch and reuse them for the rest of the run.
Expected output: the same logical review makes a fraction of the API calls.

### 3. Add exponential backoff with jitter on 403 and 429
When the API says slow down, wait and retry with growing delays plus random jitter.
Expected output: transient limit hits recover instead of failing the run.

### 4. Fetch less per call
Use pagination sensibly, request only the fields needed, and avoid polling loops that re-list unchanged resources.
Expected output: lower call volume for identical review coverage.

### 5. Watch the rate-limit headers on the next run
Check `x-ratelimit-remaining` in the logs after the fix.
Expected output: the remaining budget stays comfortably above zero through the whole run.

## When to use
- A review run fails with rate limit exceeded for the installation ID
- Retries of the same job fail faster each time
- The logs show hundreds of API calls for one PR

## When not to use
- Authentication failures (bad credentials, expired installation token)
- Pure secondary abuse limits from concurrent jobs (stagger the jobs instead)
- GraphQL complexity errors (restructure the query)

## Tool compatibility
- GitHub REST API v3 rate limits for GitHub App installations
- Applies to reviewdog, custom review bots, and CI jobs using an installation token
- Same discipline helps on GitHub Enterprise with its own limits

## Variant phrasings
### rate limit exceeded for user ID instead of installation ID
The job is authenticating as a user, not an installation. User quotas are smaller; switch to installation auth or cut calls the same way.

### secondary rate limit on a review job
Too many concurrent requests, not too many total. Add concurrency limits and jitter rather than just caching.

### review job passes on retry but fails on first run
The first run does the expensive full fetch. Cache the results as artifacts so retries and re-runs start warm.

## Why it happens
Installation tokens share one hourly quota across everything that installation does. A review job that refetches the PR diff, file list, and comments on every step, and then retries the whole job on failure, multiplies its call count until the quota is gone. The API is not broken; the job is just chatty.

## Edge cases
- One installation shared across many repos: the quota is shared too. Split heavy repos onto separate installations.
- Webhook bursts (many PRs at once): queue the review jobs instead of running them all concurrently.
- Long-running review agents: refresh cached data on a timer rather than refetching per decision.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_0xgILs4EUAhYKxtXrTt9Fg
