Rate Limits & Retries
The SDK provides built-in handling for rate limits and automatic retries for transient failures. Understanding these mechanisms is crucial for building robust applications.
Rate Limiting Signals
When working with APIs, rate limiting is a common mechanism to prevent abuse and ensure fair usage. The Nexla SDK provides clear signals when rate limits are encountered and includes built-in mechanisms to handle these situations gracefully.
RateLimitError Exception
The SDK raises RateLimitError for HTTP 429 responses. The retry_after attribute is populated from the Retry-After header or JSON body when available.
Here's how to handle rate limit errors:
from nexla_sdk.exceptions import RateLimitError
def list_sources_with_backoff(client: NexlaClient):
try:
return client.sources.list(per_page=100)
except RateLimitError as exc:
wait = exc.retry_after or 30
print(f"Hit rate limit, retrying in {wait}s")
raise
Built-in Retry Logic
The SDK includes automatic retry logic for transient failures. RequestsHttpClient enables urllib3.Retry with exponential backoff (backoff_factor=0.5) for transient status codes 429, 502, 503, and 504, covering GET/POST/PUT/DELETE/PATCH calls.
Rate Limit Information
Understanding your current rate limit status and usage is essential for building applications that can adapt to API constraints. The SDK provides methods to query and monitor rate limit information.
Get Rate Limits
You can check your current rate limit status using the metrics API:
# Get current rate limit information
rate_limits = client.metrics.get_rate_limits()
print(f"Rate limits: {rate_limits}")
Monitor Rate Limit Usage
For production applications, it's important to monitor rate limit usage to avoid hitting limits unexpectedly:
def monitor_rate_limits():
"""Monitor rate limit usage across resources"""
try:
rate_limits = client.metrics.get_rate_limits()
print("📊 Rate Limit Status:")
for resource_type, limits in rate_limits.items():
print(f" {resource_type}:")
print(f" Used: {limits.get('used', 'N/A')}")
print(f" Limit: {limits.get('limit', 'N/A')}")
print(f" Reset: {limits.get('reset_time', 'N/A')}")
print()
except Exception as e:
print(f"❌ Error getting rate limits: {e}")
monitor_rate_limits()
Retry Strategies
When API calls fail due to transient issues, implementing effective retry strategies can significantly improve application reliability. Here are several approaches you can use with the Nexla SDK.
Exponential Backoff
Implementing exponential backoff helps prevent overwhelming the API when retrying failed requests:
import time
from nexla_sdk.exceptions import RateLimitError, ServerError
def exponential_backoff(func, max_retries=5):
"""Implement exponential backoff for retries"""
for attempt in range(max_retries):
try:
return func()
except RateLimitError as e:
wait_time = e.retry_after or (2 ** attempt)
if attempt < max_retries - 1:
print(f"Rate limited. Waiting {wait_time}s...")
time.sleep(wait_time)
continue
raise
except ServerError as e:
if attempt < max_retries - 1:
wait_time = 2 ** attempt
print(f"Server error. Retrying in {wait_time}s...")
time.sleep(wait_time)
continue
raise
Circuit Breaker Pattern
For high-availability applications, implementing a circuit breaker pattern can help prevent cascading failures:
class CircuitBreaker:
def __init__(self, failure_threshold=5, timeout=60):
self.failure_threshold = failure_threshold
self.timeout = timeout
self.failure_count = 0
self.last_failure_time = None
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
def call(self, func):
if self.state == "OPEN":
if time.time() - self.last_failure_time > self.timeout:
self.state = "HALF_OPEN"
else:
raise Exception("Circuit breaker is OPEN")
try:
result = func()
if self.state == "HALF_OPEN":
self.state = "CLOSED"
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
raise
# Usage
breaker = CircuitBreaker()
result = breaker.call(lambda: client.flows.list())
Idempotency Expectations
Understanding which operations are safe to retry is crucial for building robust applications. Idempotent operations can be safely repeated without side effects, while non-idempotent operations require careful handling.
Safe Operations
Understanding which operations are safe to retry is important for implementing robust error handling:
GET,PUT, andDELETEendpoints are idempotent- Repeating the same call produces the same state (standard REST semantics)
POSTendpoints create new resources
Handling Duplicates
When creating resources, you may encounter conflicts if the resource already exists. Here's how to handle this gracefully:
from nexla_sdk.exceptions import ResourceConflictError
def create_or_get_destination(payload):
"""Create destination or get existing one"""
try:
destination = client.destinations.create(payload)
return destination
except ResourceConflictError:
# Resource already exists, find and return it
existing = client.destinations.list(name=payload["name"], per_page=1)
if existing:
return existing[0]
else:
raise Exception("Conflict but resource not found")
Safe Create/Update Pattern
A common pattern is to attempt creation first, then fall back to updating if the resource already exists:
def safe_create_or_update(payload, resource_type="destination"):
"""Safely create or update a resource"""
try:
if resource_type == "destination":
return client.destinations.create(payload)
except ResourceConflictError:
# Fallback to updating the existing resource
if resource_type == "destination":
existing = client.destinations.list(name=payload["name"], per_page=1)
if existing:
return client.destinations.update(existing[0].id, payload)
else:
raise
Best Practices
Following established best practices for rate limiting and retries will help you build more reliable and efficient applications. These guidelines are based on industry standards and proven patterns.
1. Respect Server Wait Times
def respect_server_wait(exc):
"""Respect server-provided wait times"""
if hasattr(exc, 'retry_after') and exc.retry_after:
wait_time = exc.retry_after
print(f"Server requests wait time: {wait_time}s")
time.sleep(wait_time)
2. Use Pagination for Large Datasets
def get_all_flows():
"""Get all flows using pagination"""
all_flows = []
page = 1
per_page = 100
while True:
try:
flows = client.flows.list(per_page=per_page, page=page)
if not flows:
break
all_flows.extend(flows)
page += 1
except RateLimitError as e:
wait_time = e.retry_after or 30
print(f"Rate limited. Waiting {wait_time}s...")
time.sleep(wait_time)
return all_flows
3. Implement Jitter for Retries
import random
def jittered_backoff(base_wait, max_jitter=0.1):
"""Add jitter to prevent thundering herd"""
jitter = random.uniform(0, max_jitter)
return base_wait * (1 + jitter)
def retry_with_jitter(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except RateLimitError as e:
if attempt < max_retries - 1:
base_wait = e.retry_after or (2 ** attempt)
wait_time = jittered_backoff(base_wait)
print(f"Retrying in {wait_time:.2f}s...")
time.sleep(wait_time)
continue
raise
Monitoring and Alerting
Proactive monitoring of rate limit usage helps prevent unexpected failures and allows you to optimize your application's API usage patterns. Implementing proper alerting ensures you can respond quickly to issues.
Rate Limit Monitoring
def monitor_rate_limits():
"""Monitor and alert on rate limit usage"""
try:
rate_limits = client.metrics.get_rate_limits()
for resource_type, limits in rate_limits.items():
used = limits.get('used', 0)
limit = limits.get('limit', 0)
if limit > 0:
usage_percent = (used / limit) * 100
if usage_percent > 80:
print(f"⚠️ High rate limit usage for {resource_type}: {usage_percent:.1f}%")
elif usage_percent > 90:
print(f"🚨 Critical rate limit usage for {resource_type}: {usage_percent:.1f}%")
except Exception as e:
print(f"❌ Error monitoring rate limits: {e}")
Next Steps
- Error Handling - Comprehensive error management
- Troubleshooting - Common issues and solutions
- Operations - Monitoring and observability
For detailed rate limiting strategies and retry patterns, see the Rate Limits & Idempotency page in our Nexla SDK guides.