Skip to content

Rate limits

Limit GITHUB_TOKEN PAT or App
Primary REST 1,000 requests per hour per repository (15,000 for repositories owned by an Enterprise Cloud account) 5,000 per hour per user (App installations scale with repositories and users)
Primary GraphQL 1,000 points per hour per repository (15,000 for Enterprise Cloud) 5,000 points per hour for a user; App installations scale with repositories and users
Secondary 80 content-creating requests per minute, 500 per hour, and a concurrency cap; not exposed in headers same
Statuses per commit and context 1,000; further writes return a validation error, not a rate limit same

Git fetches do not count against any of these, which is why the git ancestry adapter is preferred whenever a clone is available: a refresh then spends two GraphQL points per 100 PRs (a listing before and after the fetch) plus the writes, and no compares at all.

What each command costs with the API adapter

Section titled “What each command costs with the API adapter”
Command Requests
refresh-pr-status Fixed: 1 for the default branch unless --base is set, 1 to resolve --pr or a bare ref, 1 per configured baseline, 1 for the base head, 1 compare per present baseline for the base-membership guard, 1 compare per applicable baseline that is not absent, and 1 compare per PR when any baseline has a scope. With reporting: the baseline refs and the base head are read again (once more per round while they keep moving, at most three rounds), GET /user once per run for a user token, 1 GraphQL point for the current status, 1 write. Around 9 for a single baseline with reporting.
refresh-pr-statuses Fixed: 1 for the default branch unless --base is set, GET /user once for a user token, 1 per baseline, 1 for the base head, 1 compare per present baseline for the base-membership check, 1 GraphQL point per 100 open PRs (2 with the git adapter, which lists again after fetching the heads). Per PR: 1 compare per present baseline plus 1 when any baseline is scoped, memoized per head within the run. Per write: 1 request per physical attempt, up to three on transient failures.
move-baseline Fixed: 1 for the default branch unless --base is set, 1 per baseline, 1 for the base head, 1 to resolve --to when given. Per baseline that can move on a label: 1 GraphQL point per 50 merged labeled PRs per distinct label, up to 2 compares per candidate. Per baseline with markers: 1 compare. Per move: 1 compare for the fast-forward guard and 1 ref write. Then 1 per baseline to re-read the refs before the result (not in a dry run).
report Fixed: 1 for the default branch unless --base is set, 1 per baseline, 1 for the base head, 1 GraphQL point per 100 open PRs, 1 compare per present baseline. Per PR: 1 compare when any baseline is scoped. No writes.

A default link names the base branch rather than a baseline commit, so a move leaves every failing PR’s status alone and costs no writes.

Worked cold start with GITHUB_TOKEN, one baseline, 600 open PRs, nothing stamped yet, run as --scope unstamped or --scope all (the default scope selects none of them, since none carries a status): 6 GraphQL points to list, 600 compares, 600 writes. The refresh stops at the 450-write cap with 150 PRs left; it wrote something, so it is paused and exits 0, and the next run of the same command finishes them. With the git adapter the 600 compares disappear.

  • Per-run cap (--max-writes-per-run, default 450): a hard stop, below the 500 content-creating requests per hour secondary limit with room for reads. Every physical write attempt counts, failed and retried ones included, since GitHub charged for each request; the transport never retries a status write on its own.
  • Per-minute cap (--max-writes-per-minute, default 60): a sliding window under the 80 per minute secondary limit; the refresh sleeps when it is reached.
  • Primary reserve: the refresh reads x-ratelimit-remaining from every response and stops writing when 50 requests remain, so reads at the end of the run never fail on an exhausted window.
  • Classification: a primary limit (x-ratelimit-remaining: 0), a secondary limit (403 or 429 with retry-after or a body mentioning the rate limit) and a GraphQL RATE_LIMITED error on a 200 all stop the run with the summary and a retry hint. For reads and ref updates, server errors (any 5xx) and network failures are retried three times with quadratic backoff through the official Octokit retry plugin; status writes are retried by refresh-pr-status and refresh-pr-statuses themselves so every attempt is budgeted; no 4xx is ever retried.

An incomplete or paused refresh is not lost work: every status it wrote is current, and the next run skips them.