# `Codex.RateLimit`
[🔗](https://github.com/nshkrdotcom/codex_sdk/blob/v0.18.1/lib/codex/rate_limit.ex#L1)

Rate limit detection and handling utilities.

## Detection

Rate limits are detected from:
- `Codex.Error` structs with `:rate_limit` kind
- HTTP 429 status codes
- Error messages containing "rate_limit"
- Retry-After headers in responses

## Handling

When rate limited, the SDK:
1. Extracts retry-after hint if available
2. Backs off for the specified duration (or default)
3. Emits telemetry event
4. Retries the request

## Configuration

    config :codex_sdk,
      rate_limit_default_delay_ms: 60_000,
      rate_limit_max_delay_ms: 300_000,
      rate_limit_multiplier: 2.0

## Example

    Codex.RateLimit.with_rate_limit_handling(fn ->
      make_api_call()
    end, max_attempts: 3)

# `rate_limit_info`

```elixir
@type rate_limit_info() :: %{
  optional(:retry_after_ms) =&gt; non_neg_integer() | nil,
  optional(:message) =&gt; String.t(),
  optional(:details) =&gt; map(),
  optional(:source) =&gt; atom(),
  optional(:body) =&gt; map()
}
```

# `calculate_delay`

```elixir
@spec calculate_delay(rate_limit_info(), pos_integer()) :: non_neg_integer()
```

Calculates delay for rate limit backoff.

If the rate limit info contains an explicit `retry_after_ms`, that value
is used. Otherwise, exponential backoff is applied based on the attempt number.

## Examples

    iex> Codex.RateLimit.calculate_delay(%{retry_after_ms: 45_000}, 1)
    45_000

    iex> delay1 = Codex.RateLimit.calculate_delay(%{}, 1)
    iex> delay2 = Codex.RateLimit.calculate_delay(%{}, 2)
    iex> delay2 > delay1
    true

# `detect`

```elixir
@spec detect(term()) :: {:rate_limited, rate_limit_info()} | :ok
```

Detects rate limit error from response or error.

Returns `{:rate_limited, info}` if a rate limit is detected,
or `:ok` if not rate limited.

## Examples

    iex> error = Codex.Error.rate_limit("Rate limited", retry_after_ms: 30_000)
    iex> {:rate_limited, info} = Codex.RateLimit.detect({:error, error})
    iex> info.retry_after_ms
    30_000

    iex> Codex.RateLimit.detect({:ok, :success})
    :ok

# `handle`

```elixir
@spec handle(
  rate_limit_info(),
  keyword()
) :: :ok
```

Handles rate limit by waiting and emitting telemetry.

Sleeps for the calculated delay and emits a `[:codex, :rate_limit, :rate_limited]`
telemetry event.

## Options

  * `:attempt` - Current attempt number (default: 1)

## Examples

    info = %{retry_after_ms: 1000}
    Codex.RateLimit.handle(info, attempt: 1)
    # Sleeps for 1000ms and emits telemetry

# `parse_retry_after`

```elixir
@spec parse_retry_after(map()) :: non_neg_integer() | nil
```

Parses Retry-After header from response.

Handles both numeric seconds and HTTP-date formats.

## Examples

    iex> Codex.RateLimit.parse_retry_after(%{headers: %{"retry-after" => "60"}})
    60_000

    iex> Codex.RateLimit.parse_retry_after(%{headers: %{"Retry-After" => "120"}})
    120_000

    iex> Codex.RateLimit.parse_retry_after(%{})
    nil

# `with_rate_limit_handling`

```elixir
@spec with_rate_limit_handling(
  (-&gt; term()),
  keyword()
) :: term()
```

Wraps function with rate limit handling.

Automatically detects rate limit responses and retries with appropriate
backoff. Uses exponential backoff by default, or respects explicit
retry-after hints from the API.

## Options

  * `:max_attempts` - Maximum number of attempts (default: 3)

## Examples

    result = Codex.RateLimit.with_rate_limit_handling(fn ->
      make_api_call()
    end, max_attempts: 3)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
