client = RateLimitlyClient(
auth_key="rl-aes1...", # Required Bech32 credential.
dns_srv=None, # Derive the tenant domain from the credential.
policy=None, # Use the C-compatible default policy.
)
The credential contains format version 1, the API-key ID, a 32-byte cookie or AES-256-GCM key, and six quotas packed into one 32-bit word. The client verifies the Bech32 checksum, exact 45-byte payload, version, and quota codes before accepting it. Legacy unversioned keys and unknown versions are rejected. See the normative wire-protocol credential format.
When dns_srv is omitted, discovery queries:
_ratelimitly._udp.c-<key-id>.p0.ratelimitly.com
Only SRV targets of the form s-<server-id>... become r-server endpoints. A failed refresh does not invent a default port or server; cached endpoints remain usable, otherwise the operation returns RCLIENT_ERR_DNS.
Python uses the same single policy model as rl-c-client:
RequestPolicy(
unit_ms: int,
replay_count: int,
replay_gap: Schedule,
final_receive_units: int = 1,
completion_delivery: bool = True,
)
There are no separate “standard,” “single round,” or compatibility policy modes. Those behaviors are parameter choices.
For round k, replay_gap.get_gap(k) gives the round duration B(k) in units. The initial transmission is round zero and each replay adds one round. The final interval is receive-only.
dedup_ttl_ms = unit_ms × (
B(0) + B(1) + ... + B(replay_count) + final_receive_units
)
The computed value is both the request’s deduplication TTL and its maximum decision horizon. It must fit uint32 and must not exceed the credential’s dedup_ttl_ms_max.
from ratelimitly import default_request_policy
policy = default_request_policy()
This produces:
| Parameter | Value |
|---|---|
unit_ms |
20 |
replay_count |
1 |
| schedule | fixed, one unit |
final_receive_units |
1 |
completion_delivery |
true |
| deduplication TTL / horizon | 60 ms |
The default sequence is:
RCLIENT_ERR_TIMEOUT.When a result is selected before the deduplication deadline and completion_delivery is enabled, the client best-effort resends the same logical request to missing servers. This helps replicas converge for grants and denials; deduplication prevents a server that already handled the request from consuming it twice.
from ratelimitly import (
ExponentialSchedule,
FixedSchedule,
LinearSchedule,
RequestPolicy,
)
fixed = RequestPolicy(
unit_ms=20,
replay_count=0,
replay_gap=FixedSchedule(1),
final_receive_units=0,
completion_delivery=False,
)
linear = RequestPolicy(
unit_ms=10,
replay_count=3,
replay_gap=LinearSchedule(
initial_units=1,
step_units=1,
maximum_units=4,
),
final_receive_units=1,
)
exponential = RequestPolicy(
unit_ms=5,
replay_count=3,
replay_gap=ExponentialSchedule(
initial_units=1,
factor=2,
maximum_units=8,
),
final_receive_units=1,
)
Validate a policy against a particular credential before using it:
ttl_ms = policy.calculate_horizon_ms(auth_info.dedup_ttl_ms_max)
The client performs this validation during construction and again before encoding a request.
RCLIENT_ERR_TIMEOUT, RCLIENT_ERR_DNS, and RCLIENT_ERR_IO mean that no RateLimitly decision is available. The library does not turn such failures into a grant or denial. Framework integrations and applications must explicitly choose their own fail-open, fail-closed, or fallback behavior.