Skip to main content
When the ChatGPT/Codex backend returns an HTTP 429 Too Many Requests response, Chappie automatically retries the request with exponential backoff so your app stays responsive without hammering the API. You can tune the retry count, delay bounds, and header behaviour — or disable backoff entirely — through ChappieRateLimitBackoffPolicy.

Default behaviour

The default policy (ChappieRateLimitBackoffPolicy.default) retries up to 2 times, starting with a 1-second delay, capped at 30 seconds, and always honours the Retry-After response header when the server includes one. After all retries are exhausted, Chappie throws ChappieClientError.usageLimitReached (if the response body carries usage-limit metadata) or a ChappieClientError.httpError(429, ...) otherwise.

Policy parameters

maxRetries
Int
default:"2"
The maximum number of retry attempts after the first 429. Set to 0 to disable automatic retries entirely — equivalent to using .disabled. The SDK clamps negative values to 0.
initialDelay
TimeInterval
default:"1"
The initial wait in seconds before the first retry. Subsequent retries double this value up to maximumDelay. The SDK clamps negative values to 0.
maximumDelay
TimeInterval
default:"30"
The upper bound in seconds for the exponential delay. No retry waits longer than this value, even if the Retry-After header requests a longer wait. The SDK clamps negative values to 0.
respectsRetryAfterHeader
Bool
default:"true"
When true, Chappie reads the Retry-After response header and uses that value instead of the computed exponential delay, subject to maximumDelay. When false, Chappie always uses its own computed backoff and ignores the header.

Customising backoff

Pass a ChappieRateLimitBackoffPolicy to Chappie.client(rateLimitBackoff:) or embed it in a ChappieConfiguration:
let client = Chappie.client(
    rateLimitBackoff: ChappieRateLimitBackoffPolicy(
        maxRetries: 3,
        initialDelay: 1,
        maximumDelay: 15,
        respectsRetryAfterHeader: true
    )
)
Via a shared configuration:
let config = ChappieConfiguration(
    rateLimitBackoff: ChappieRateLimitBackoffPolicy(
        maxRetries: 3,
        initialDelay: 1,
        maximumDelay: 15,
        respectsRetryAfterHeader: true
    )
)

let authSession = Chappie.authSession(configuration: config)
let client = Chappie.client(configuration: config)

Disabling backoff entirely

Use the .disabled static value to turn off all automatic retries. Chappie surfaces the 429 immediately as an error:
let client = Chappie.client(rateLimitBackoff: .disabled)
.disabled is a convenience static that sets maxRetries to 0:
public static let disabled = ChappieRateLimitBackoffPolicy(maxRetries: 0)
You can achieve the same result by passing ChappieRateLimitBackoffPolicy(maxRetries: 0) directly.

Handling exhausted retries

When Chappie runs out of retry attempts, it throws one of two errors depending on the response body:
  • ChappieClientError.usageLimitReached(ChappieUsageLimit) — when the backend returns structured usage-limit metadata (plan type, reset time, usage percentages). Inspect the associated ChappieUsageLimit value to show a meaningful message.
  • ChappieClientError.httpError(Int, String) — for bare 429 responses without usage-limit metadata.
do {
    let reply = try await client.send("Summarise this document.")
} catch ChappieClientError.usageLimitReached(let limit) {
    print(limit.plan?.displayName ?? "Unknown plan")
    if let resetsAt = limit.resetsAt {
        print("Resets at \(resetsAt.formatted(date: .abbreviated, time: .shortened))")
    }
} catch ChappieClientError.httpError(let status, let body) {
    print("HTTP \(status): \(body)")
}
Keep respectsRetryAfterHeader: true in production. The Retry-After header lets the server communicate the exact backoff it needs, which is almost always shorter than the worst-case exponential path and avoids unnecessary delay for your users.