# `Bourse.RateLimiter.Shaping`
[🔗](https://github.com/ZenHive/bourse/blob/main/lib/bourse/rate_limiter/shaping.ex#L1)

Shapes endpoint rate-limit descriptors into `Bourse.RateLimiter` checks and
updates rate-limit state from response headers.

Extracted from `Bourse.HTTP` so transport stays focused on request execution
while bucket-axis normalization, cost shaping, and header→ETS updates live
with the RateLimiter family. Behavior-preserving delegation only.

# `axis_key`

```elixir
@type axis_key() :: {String.t(), credential_key(), String.t()}
```

Full rate key including bucket axis

# `credential_key`

```elixir
@type credential_key() :: String.t() | :public
```

Credential slice of a rate key: API key string or `:public`

# `rate_check`

```elixir
@type rate_check() ::
  {axis_key(), %{requests: non_neg_integer(), period: pos_integer()}, number()}
```

One RateLimiter.check_rates/1 triple

# `rate_key`

```elixir
@type rate_key() :: {String.t(), credential_key()}
```

Base rate key without bucket axis

# `build_rate_limit_checks`

```elixir
@spec build_rate_limit_checks(rate_key(), Bourse.Exchange.t(), term()) :: [
  rate_check()
]
```

Builds RateLimiter check triples from an endpoint rate-limit descriptor.

Accepts a numeric weight, a map with `:cost`/`:axes`/`:rate_limit_ms`, a list
of those maps, or falls back to weight `1` on the default `"request"` axis.

# `maybe_rate_limit`

```elixir
@spec maybe_rate_limit(rate_key(), Bourse.Exchange.t(), term()) :: :ok
```

Checks rate limit if enabled — blocks until capacity is available, returns `:ok`.

`rate_limit_ms` is "milliseconds between requests", so
max requests per period = period / rate_limit_ms.

# `maybe_update_state`

```elixir
@spec maybe_update_state(Bourse.Exchange.t(), map()) :: :ok
```

Parses rate limit headers from a response and updates the ETS state store.

Returns `:ok` when the exchange doesn't send rate limit headers (OKX, Kraken,
etc.) — the normal case for most exchanges, not an error.

# `rate_key`

```elixir
@spec rate_key(Bourse.Exchange.t()) :: rate_key()
```

Builds the base rate-limiter key from an exchange: `{exchange_id, api_key | :public}`.

---

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