Rate Limiting¶
NetworkPype's throttler enforces API rate limits asynchronously using a shared task log and an async context manager interface. It supports multiple concurrent limits, weighted requests, and linked limits where one request affects several pools.
Core Concepts¶
| Concept | Class | Description |
|---|---|---|
| Rate limit definition | RateLimit |
Declares a limit: N requests per T seconds |
| Linked limits | LinkedLimitWeightPair |
One request consumes capacity from another limit |
| Task log | TaskLog |
Records when capacity was consumed |
| Throttler | AsyncThrottler |
Manages the shared log and enforces limits |
| Context | AsyncRequestContext |
Awaitable gate that blocks until capacity is free |
Defining Rate Limits¶
from networkpype.throttler.rate_limit import RateLimit
# Simple: 10 requests per second
simple = RateLimit(
limit_id="ticker",
limit=10,
time_interval=1.0,
)
# Weighted: each request counts as 2 units
weighted = RateLimit(
limit_id="order",
limit=100,
time_interval=60.0,
weight=2,
)
limit_id is the key you pass to execute_task (and execute_request's throttler_limit_id parameter).
Linked Limits¶
Linked limits let a request against one endpoint also consume capacity from a shared global pool:
from networkpype.throttler.rate_limit import LinkedLimitWeightPair, RateLimit
RATE_LIMITS = [
# Global pool: 1200 requests per minute
RateLimit(limit_id="global", limit=1200, time_interval=60.0),
# Endpoint pool: 10 per second, also drains the global pool by 1
RateLimit(
limit_id="/api/v1/ticker",
limit=10,
time_interval=1.0,
linked_limits=[LinkedLimitWeightPair(limit_id="global", weight=1)],
),
# Heavy endpoint: 5 per second, drains global pool by 5 each time
RateLimit(
limit_id="/api/v1/order",
limit=5,
time_interval=1.0,
linked_limits=[LinkedLimitWeightPair(limit_id="global", weight=5)],
),
]
Creating the Throttler¶
from decimal import Decimal
from networkpype.throttler.throttler import AsyncThrottler
throttler = AsyncThrottler(
rate_limits=RATE_LIMITS,
retry_interval=0.1, # seconds between capacity checks
safety_margin_pct=0.05, # use only 95% of declared limits
limits_share_percentage=Decimal("50"), # this instance uses 50% of limits
)
limits_share_percentage is useful when multiple application processes share a single API key. Setting it to Decimal("50") halves every limit, so two processes together stay within the full quota.
Using the Throttler Directly¶
async with throttler.execute_task("/api/v1/ticker"):
response = await make_raw_request()
execute_task blocks until capacity is available, then logs the task and allows the block to run.
Integration with RESTManager¶
RESTManager.execute_request handles throttling automatically via throttler_limit_id:
data = await manager.execute_request(
url="https://api.example.com/api/v1/ticker",
throttler_limit_id="/api/v1/ticker",
method=RESTMethod.GET,
)
The manager wraps the network call inside throttler.execute_task(throttler_limit_id), so no manual context manager is needed.
Safety Margin¶
The safety_margin_pct parameter reserves a fraction of each limit as a buffer. For example, with limit=100 and safety_margin_pct=0.05, the effective limit is 95. This prevents exceeding the declared limit due to clock skew or network latency in the capacity check window.
Updating Limits at Runtime¶
You can swap the rate limit list on an existing throttler:
throttler.rate_limits = [
RateLimit(limit_id="global", limit=600, time_interval=60.0),
]
The setter deep-copies the list and rebuilds the internal ID map.
Next Steps¶
- Connection Factory --- Pass the throttler into
ConnectionManagersFactory - API Reference: AsyncThrottler --- Full API
- API Reference: RateLimit --- Dataclass fields