Rate-limit headers and an opt-in retry for GitHub's rate limits.
from_response/1 parses the x-ratelimit-* headers into a snapshot.
retry/2 is a Req-compatible retry policy. Req already retries ordinary
transient errors (5xx, network errors) on its own; this adds awareness of
GitHub's secondary rate limits, which arrive as a 403 (not retried by
default) carrying a retry-after header or x-ratelimit-remaining: 0. Enable
it through :req_options:
GhEx.new(
auth: {:token, token},
req_options: [retry: &GhEx.RateLimit.retry/2]
)Req bounds the attempts with :max_retries (default 3).
Summary
Types
@type t() :: %GhEx.RateLimit{ limit: non_neg_integer() | nil, remaining: non_neg_integer() | nil, reset: DateTime.t() | nil, resource: String.t() | nil, used: non_neg_integer() | nil }
Functions
@spec from_response(Req.Response.t()) :: t() | nil
Builds a snapshot from a response, or nil when no rate-limit headers exist.
@spec retry(Req.Request.t(), Req.Response.t() | Exception.t()) :: {:delay, non_neg_integer()} | boolean()
A Req-compatible retry policy that understands GitHub's rate limits.
Returns {:delay, ms} for a 403 or 429 that signals a rate limit, waiting
the retry-after header or, when x-ratelimit-remaining is 0, until
x-ratelimit-reset. Other 429s and the usual transient statuses
(408, 500, 502, 503, 504) return true so Req applies its own
backoff. A plain 403 (an authorization failure, not a rate limit) is not
retried. Wire it in through :req_options; Req applies :max_retries.