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.
Summary
Types
Full rate key including bucket axis
Credential slice of a rate key: API key string or :public
One RateLimiter.check_rates/1 triple
Base rate key without bucket axis
Functions
Builds RateLimiter check triples from an endpoint rate-limit descriptor.
Checks rate limit if enabled — blocks until capacity is available, returns :ok.
Parses rate limit headers from a response and updates the ETS state store.
Builds the base rate-limiter key from an exchange: {exchange_id, api_key | :public}.
Types
@type axis_key() :: {String.t(), credential_key(), String.t()}
Full rate key including bucket axis
@type credential_key() :: String.t() | :public
Credential slice of a rate key: API key string or :public
@type rate_check() :: {axis_key(), %{requests: non_neg_integer(), period: pos_integer()}, number()}
One RateLimiter.check_rates/1 triple
@type rate_key() :: {String.t(), credential_key()}
Base rate key without bucket axis
Functions
@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.
@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.
@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.
@spec rate_key(Bourse.Exchange.t()) :: rate_key()
Builds the base rate-limiter key from an exchange: {exchange_id, api_key | :public}.