Rate Limiting
Overview
HFortix-Core provides two complementary rate-limiting facilities:
Tracking (
RateLimitStats): visibility into API call patterns across multiple time windows. Informational only — it never blocks a request.Enforcement (
RateLimiter/AsyncRateLimiter): opt-in client-side throttling. Enabled by passingrate_limit=True(plus therate_limit_*tuning kwargs) to any HTTP client orCloudSessionconstructor. Disabled by default.
Key Features (Tracking)
Multiple time windows: Tracks last minute, last 5 minutes, and last hour
Separate tracking: Tracks calls and errors independently
Configurable limits: Set custom limits or leave as
NoneEfficient implementation: Uses
dequewithmaxlenfor memory efficiencyPer-client tracking: Each service instance has its own
RateLimitStatsSession-wide tracking: CloudSession tracks all calls across all services
No enforcement:
RateLimitStatsitself does not block requests — use the enforcement kwargs below for that
Time Windows
The system tracks statistics across three time windows:
Last minute (60 seconds): Short-term rate monitoring
Last 5 minutes (300 seconds): Medium-term trends
Last hour (3600 seconds): Long-term usage patterns
Both calls and errors are tracked separately for each window.
Basic Usage
FortiCare Example
from hfortix_forticare import FortiCare
# Configure custom limits (all optional, defaults to None)
fc = FortiCare(
api_id="...",
password="...",
rate_limit_calls_per_min=100, # 100 calls per minute
rate_limit_calls_per_5min=500, # 500 calls per 5 minutes
rate_limit_calls_per_hour=1000, # 1000 calls per hour
rate_limit_errors_per_hour=10 # 10 errors per hour
)
# Make API calls
products = fc.api.products.list.post()
# Check rate limit status
status = fc.get_rate_limit_status()
print(f"Calls last min: {status['calls_last_min']}/{status['limits']['calls_per_min']}")
print(f"Calls last 5min: {status['calls_last_5min']}/{status['limits']['calls_per_5min']}")
print(f"Calls last hour: {status['calls_last_hour']}/{status['limits']['calls_per_hour']}")
print(f"Within limits: {status['within_limits']}")
FortiZTP Example
from hfortix_fortiztp import FortiZTP
# FortiZTP: 2000 calls per hour limit
client = FortiZTP(
api_id="...",
password="...",
rate_limit_calls_per_hour=2000
)
# Check status
status = client.get_rate_limit_status()
Session-Wide Tracking
from hfortix_core.session import CloudSession
from hfortix_forticare import FortiCare
from hfortix_fortiztp import FortiZTP
with CloudSession(api_id="...", password="...") as session:
fc = FortiCare(session=session)
fz = FortiZTP(session=session)
# Check per-client stats
fc_status = fc.get_rate_limit_status()
fz_status = fz.get_rate_limit_status()
# Check session-wide stats (all services combined)
session_status = session.get_rate_limit_status()
print(f"Total calls (all services): {session_status['calls_last_hour']}")
Status Response Format
The get_rate_limit_status() method returns:
{
"calls_last_min": 45, # Calls in last 60 seconds
"calls_last_5min": 180, # Calls in last 300 seconds
"calls_last_hour": 523, # Calls in last 3600 seconds
"errors_last_min": 0, # Errors in last 60 seconds
"errors_last_5min": 1, # Errors in last 300 seconds
"errors_last_hour": 2, # Errors in last 3600 seconds
"total_calls": 1247, # Total since client creation
"total_errors": 5, # Total errors since creation
"limits": { # Configured limits
"calls_per_min": 100,
"calls_per_5min": 500,
"calls_per_hour": 1000,
"errors_per_min": None,
"errors_per_5min": None,
"errors_per_hour": 10
},
"within_limits": True # Whether within all set limits
}
Known API Rate Limits
FortiCare Asset Management
100 calls per minute
1000 calls per hour
10 errors per hour
Batch operations: Max 10 units, max 5 errors per batch
FortiZTP
2000 calls per hour
FortiGateCloud
100 calls per second
15 errors per minute
Enforcement
Enforcement is opt-in and implemented by RateLimiter (sync) and
AsyncRateLimiter (async). All HTTP clients and CloudSession accept
the same constructor kwargs:
from hfortix_core.http import HTTPClient
client = HTTPClient(
url="https://192.168.1.99",
token="...",
rate_limit=True, # Enable enforcement (default: False)
rate_limit_max_requests=100, # Allowed requests per window
rate_limit_window_seconds=60.0, # Window length
rate_limit_strategy="queue", # "queue" | "drop" | "raise"
rate_limit_queue_size=100, # Max queued requests
rate_limit_queue_timeout=30.0, # Max seconds to wait in queue
rate_limit_queue_overflow="block", # "block" | "drop" | "raise"
)
Strategy behavior when the window limit is hit:
"queue"— requests wait for the next window (may raiseRateLimitQueueFullError/RateLimitQueueTimeoutErrordepending onrate_limit_queue_overflowandrate_limit_queue_timeout)"drop"— requests are silently dropped"raise"—RateLimitExceededErroris raised immediately
Note: the limiter is a per-window token counter, not a concurrency
semaphore — RateLimiter.release() is intentionally a no-op; all
accounting happens in acquire().
API Reference
RateLimitStats Class (tracking)
- class hfortix_core.ratelimit.RateLimitStats(calls_per_min=None, calls_per_5min=None, calls_per_hour=None, errors_per_min=None, errors_per_5min=None, errors_per_hour=None)[source]
Bases:
objectSimple rate limit tracking (no enforcement).
Tracks API calls and errors over multiple time windows: - Last minute (60 seconds) - Last 5 minutes (300 seconds) - Last hour (3600 seconds)
Does NOT enforce limits - just provides visibility.
- Usage:
>>> stats = RateLimitStats( ... calls_per_min=100, ... calls_per_5min=500, ... calls_per_hour=1000, ... errors_per_hour=10 ... ) >>> >>> # Record calls >>> stats.record_call() >>> stats.record_error() >>> >>> # Check status >>> status = stats.get_status() >>> print(f"Calls last min: {status['calls_last_min']}") >>> print(f"Calls last 5min: {status['calls_last_5min']}") >>> print(f"Calls last hour: {status['calls_last_hour']}")
- Parameters:
- __init__(calls_per_min=None, calls_per_5min=None, calls_per_hour=None, errors_per_min=None, errors_per_5min=None, errors_per_hour=None)[source]
Initialize rate limit stats.
- Parameters:
calls_per_min (
int|None) – Maximum calls allowed per minute (None = no limit)calls_per_5min (
int|None) – Maximum calls allowed per 5 minutes (None = no limit)calls_per_hour (
int|None) – Maximum calls allowed per hour (None = no limit)errors_per_min (
int|None) – Maximum errors allowed per minute (None = no limit)errors_per_5min (
int|None) – Maximum errors allowed per 5 minutes (None = no limit)errors_per_hour (
int|None) – Maximum errors allowed per hour (None = no limit)
- get_status()[source]
Get current rate limit status.
- Returns:
calls_last_min: Call count in last 60 seconds
calls_last_5min: Call count in last 300 seconds
calls_last_hour: Call count in last 3600 seconds
errors_last_min: Error count in last 60 seconds
errors_last_5min: Error count in last 300 seconds
errors_last_hour: Error count in last 3600 seconds
total_calls: Total calls since creation
total_errors: Total errors since creation
limits: Configured limits
within_limits: True if all limits are respected (if set)
- Return type:
Dict with
RateLimiter Class (enforcement, sync)
AsyncRateLimiter Class (enforcement, async)
Implementation Details
Sliding Window Algorithm
The system uses collections.deque with maxlen to implement efficient sliding windows:
Each call/error timestamp is appended to the deque
When checking status, timestamps are filtered by time window
Old timestamps beyond the window are automatically dropped
Memory usage is bounded by
maxlen
Thread Safety
RateLimitStatsis not thread-safe by itselfCloudSession uses internal locking for thread-safe access
Per-client stats don’t require locking (single-threaded client usage assumed)
Future Enhancements
Planned features for future releases:
Adaptive throttling: Automatically slow down when approaching limits
Metrics export: Export statistics to Prometheus, CloudWatch, etc.
Historical tracking: Store and analyze rate limit trends over time
See Also
CloudSession - Multi-Service OAuth Management - CloudSession documentation
FortiCare API documentation
FortiZTP API documentation