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_idis the exchange string ID ("binance","bybit", etc.)credential_keyis either:- The API key string (for authenticated requests) -- isolates per-user limits
:publicatom (for public requests) -- shared pool for unauthenticated requests
Summary
Types
Rate limiter key: {exchange_id, api_key | :public}.
Rate limit configuration with max weight and period in milliseconds
Functions
Checks if a request can be made within rate limits.
Returns a child specification for starting the rate limiter under a supervisor.
Gets current total cost for a key within a time window.
Records a request for a key with specified cost.
Resets rate limit tracking for a key.
Starts the rate limiter.
Blocks until rate limit capacity is available, then records the request.
Types
Rate limiter key: {exchange_id, api_key | :public}.
@type rate_limit() :: %{requests: pos_integer(), period: pos_integer()}
Rate limit configuration with max weight and period in milliseconds
Functions
@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}tuplerate_limit--%{requests: max_weight, period: period_ms}or nil (no limiting)cost-- Request weight/cost (default: 1)name-- GenServer name (default:CCXT.RateLimiter)
@spec child_spec(keyword()) :: Supervisor.child_spec()
Returns a child specification for starting the rate limiter under a supervisor.
@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.
@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.
@spec reset(key(), GenServer.name()) :: :ok
Resets rate limit tracking for a key.
@spec start_link(keyword()) :: GenServer.on_start()
Starts the rate limiter.
@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}tuplerate_limit--%{requests: max_weight, period: period_ms}or nilcost-- Request weight/cost (default: 1)name-- GenServer name (default:CCXT.RateLimiter)