Read the rate-limit headers on every response

GitHub returns x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-used, and x-ratelimit-reset on every authenticated call. The remaining count drops with each request; when it reaches zero the client must wait until the reset time shown in UTC epoch seconds. Tracking these values after each response gives a running picture of available capacity and prevents unnecessary attempts once the quota is exhausted.

The IETF RateLimit header draft defines RateLimit and RateLimit-Policy fields that generalize the same information across providers. When both RateLimit and Retry-After appear, the Retry-After value takes precedence. Clients benefit from treating the headers as the authoritative source instead of maintaining separate internal counters that can drift from server state.

  • Store the reset timestamp and remaining count after each response.
  • Before any new request, compare current time against the stored reset value.
  • Treat a 429 response with x-ratelimit-remaining set to zero as a hard stop until the reset time.

Apply backoff instead of fixed delays

Fixed sleep intervals either waste time or still trigger secondary limits. Exponential backoff with jitter spreads retries and reduces the chance that many clients retry at the same moment. This approach lets the client adapt to varying server load without assuming a constant recovery interval.

GitHub guidance states that after a secondary rate limit the client should wait at least one minute, then increase the interval on continued failures. The IETF draft notes that servers may return Retry-After to indicate the exact wait required. Using the server-provided value when available avoids both under-waiting and over-waiting that can occur with purely local calculations.

  • Start with a one-second base delay and double on each retry up to a defined cap.
  • Add random jitter between zero and the current delay value.
  • Reset the backoff counter only after a successful response.

Limit concurrency to avoid secondary limits

Secondary rate limits on GitHub block bursts of concurrent requests even when the primary hourly quota remains. The documentation recommends serial execution or a queue when many calls are needed. Running requests one after another or through a small pool keeps the request pattern predictable and lowers the risk of hitting additional throttling layers.

Making authenticated conditional requests with ETag or Last-Modified headers returns 304 when data is unchanged, preserving quota for actual changes. This technique reduces the total number of full responses required and stretches the available quota across a longer period of operation.

  • Use a single worker or a small bounded pool for polling loops.
  • Insert at least one second between successive mutating requests.
  • Cache responses that include ETag values and re-use them on subsequent calls.

Retry only on clear transient signals

A 429 or 403 with a rate-limit message means the client exceeded a quota. GitHub instructs clients to stop sending requests until the time in x-ratelimit-reset or the Retry-After header. Continuing to retry while remaining is zero can lead to account-level blocks. Distinguishing rate-related responses from other errors prevents wasted effort and protects long-term access.

The troubleshooting section repeats the same rule: wait the indicated interval or at least one minute, then increase the delay on repeated failures. Logging the exact headers received during each throttled response creates a record that helps diagnose whether the client is consistently approaching limits or only encountering them during specific usage patterns.

  • Check the response body for an explicit retry-after value when the status is 429.
  • Do not retry on 4xx errors that are not rate-related without manual review.
  • Log the rate-limit headers on every throttled response for later analysis.

Monitor and surface quota usage

Clients that expose current remaining quota and next reset time let operators adjust schedules before limits are hit. Regular review of logged headers reveals whether the client is approaching limits consistently or only during peak periods. Surface-level visibility turns rate-limit data into actionable information rather than an after-the-fact surprise.

Test the full retry path with a proxy that returns 429 responses after a fixed number of calls. Such controlled tests verify that backoff, header reading, and concurrency controls function together without requiring live service interaction for every validation run.

  • Emit metrics for remaining quota and observed reset intervals.
  • Alert when remaining drops below a chosen threshold well before zero.
  • Test the full retry path with a proxy that returns 429 responses after a fixed number of calls.