> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chappiesdk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate-Limit Backoff Configuration in the Chappie SDK

> Control how Chappie retries HTTP 429 responses using exponential backoff, Retry-After header support, and configurable per-client retry limits.

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

<ParamField path="maxRetries" type="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`.
</ParamField>

<ParamField path="initialDelay" type="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`.
</ParamField>

<ParamField path="maximumDelay" type="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`.
</ParamField>

<ParamField path="respectsRetryAfterHeader" type="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.
</ParamField>

## Customising backoff

Pass a `ChappieRateLimitBackoffPolicy` to `Chappie.client(rateLimitBackoff:)` or embed it in a `ChappieConfiguration`:

```swift theme={null}
let client = Chappie.client(
    rateLimitBackoff: ChappieRateLimitBackoffPolicy(
        maxRetries: 3,
        initialDelay: 1,
        maximumDelay: 15,
        respectsRetryAfterHeader: true
    )
)
```

Via a shared configuration:

```swift theme={null}
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:

```swift theme={null}
let client = Chappie.client(rateLimitBackoff: .disabled)
```

<Accordion title="What does .disabled expand to?">
  `.disabled` is a convenience static that sets `maxRetries` to `0`:

  ```swift theme={null}
  public static let disabled = ChappieRateLimitBackoffPolicy(maxRetries: 0)
  ```

  You can achieve the same result by passing `ChappieRateLimitBackoffPolicy(maxRetries: 0)` directly.
</Accordion>

## 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.

```swift theme={null}
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)")
}
```

<Tip>
  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.
</Tip>
