# `CCXT.RateLimiter`
[🔗](https://github.com/ZenHive/ccxt_client/blob/main/lib/ccxt/rate_limiter.ex#L1)

Per-credential weighted rate limiter for exchange API requests.

Tracks request costs per `{exchange_id, credential_key}` using a sliding window.
Costs are summed (not counted) to handle weighted endpoints correctly.

## Usage

    # Authenticated request (per-API-key tracking)
    key = {"binance", api_key}
    case CCXT.RateLimiter.check_rate(key, %{requests: 1200, period: 60_000}, 4) do
      :ok -> make_request()
      {:delay, ms} -> Process.sleep(ms); make_request()
    end

    # Or use wait_for_capacity which blocks until ready:
    :ok = CCXT.RateLimiter.wait_for_capacity(key, rate_limit, cost)
    make_request()

## Credential Keys

The key is a tuple `{exchange_id, credential_key}` where:
- `exchange_id` is the exchange string ID (`"binance"`, `"bybit"`, etc.)
- `credential_key` is either:
  - The API key string (for authenticated requests) -- isolates per-user limits
  - `:public` atom (for public requests) -- shared pool for unauthenticated requests

# `key`

```elixir
@type key() :: {String.t(), String.t() | :public}
```

Rate limiter key: `{exchange_id, api_key | :public}`.

# `rate_limit`

```elixir
@type rate_limit() :: %{requests: pos_integer(), period: pos_integer()}
```

Rate limit configuration with max weight and period in milliseconds

# `check_rate`

```elixir
@spec check_rate(key(), rate_limit() | nil, number(), GenServer.name()) ::
  :ok | {:delay, pos_integer()}
```

Checks if a request can be made within rate limits.

Returns `:ok` if within limits (and records the request), or `{:delay, milliseconds}`
if the caller should wait before making the request.

## Parameters

- `key` -- `{exchange_id, api_key}` or `{exchange_id, :public}` tuple
- `rate_limit` -- `%{requests: max_weight, period: period_ms}` or nil (no limiting)
- `cost` -- Request weight/cost (default: 1)
- `name` -- GenServer name (default: `CCXT.RateLimiter`)

# `child_spec`

```elixir
@spec child_spec(keyword()) :: Supervisor.child_spec()
```

Returns a child specification for starting the rate limiter under a supervisor.

# `get_cost`

```elixir
@spec get_cost(key(), pos_integer(), GenServer.name()) :: number()
```

Gets current total cost for a key within a time window.

Useful for debugging and monitoring.

# `record_request`

```elixir
@spec record_request(key(), number(), GenServer.name()) :: :ok
```

Records a request for a key with specified cost.

Called automatically by `check_rate/4` when it returns `:ok`.
Exposed for manual tracking if needed.

# `reset`

```elixir
@spec reset(key(), GenServer.name()) :: :ok
```

Resets rate limit tracking for a key.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts the rate limiter.

# `wait_for_capacity`

```elixir
@spec wait_for_capacity(key(), rate_limit() | nil, number(), GenServer.name()) :: :ok
```

Blocks until rate limit capacity is available, then records the request.

## Parameters

- `key` -- `{exchange_id, api_key}` or `{exchange_id, :public}` tuple
- `rate_limit` -- `%{requests: max_weight, period: period_ms}` or nil
- `cost` -- Request weight/cost (default: 1)
- `name` -- GenServer name (default: `CCXT.RateLimiter`)

---

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