https://github.com/webexsamples/webexretryafterdemo
Example reaching the API request rate limit, and handling future requests using the Retry-After header.
https://github.com/webexsamples/webexretryafterdemo
Last synced: 2 months ago
JSON representation
Example reaching the API request rate limit, and handling future requests using the Retry-After header.
- Host: GitHub
- URL: https://github.com/webexsamples/webexretryafterdemo
- Owner: WebexSamples
- Created: 2016-08-03T18:53:30.000Z (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2025-04-16T15:39:24.000Z (about 1 year ago)
- Last Synced: 2025-05-17T05:13:25.152Z (about 1 year ago)
- Language: Python
- Size: 117 KB
- Stars: 3
- Watchers: 7
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# โฑ๏ธ Webex Retry Python Demo
See the following blog post on the Webex Developer Portal for complete details on this application: [Rate Limiting with the Webex API](https://developer.webex.com/blog/rate-limiting-and-the-webex-api).
This simple Python demonstration shows how to properly handle rate limiting when making requests to the Webex APIs, implementing the Retry-After header pattern for robust API integration.
## โจ Features
- **๐ Rate Limit Handling** - Proper implementation of HTTP 429 response handling
- **โฐ Retry-After Implementation** - Respects server-provided retry delays
- **๐ Progress Monitoring** - Real-time feedback during sleep periods
- **๐ฏ Continuous Testing** - Infinite loop for demonstrating rate limit behavior
- **๐ Response Tracking** - HTTP status codes and tracking IDs for debugging
- **โก Chunked Sleep** - User-friendly progress updates during long waits
## ๐ Quick Start
### Prerequisites
- Python 3.x
- Valid Webex personal access token
- Internet connection for API calls
### Setup and Run
1. **Clone or download the file:**
```bash
wget https://raw.githubusercontent.com/WebexSamples/WebexRetryAfterDemo/main/retryafterdemo.py
# or manually download retryafterdemo.py
```
2. **Configure your access token:**
```python
# Edit retryafterdemo.py
bearer = "YOUR_PERSONAL_ACCESS_TOKEN_HERE"
```
3. **Run the demonstration:**
```bash
python retryafterdemo.py
```
4. **Observe the output:**
- Normal API responses with status codes and tracking IDs
- Rate limit detection and Retry-After header handling
- Progress updates during sleep periods
## ๐ Usage Guide
### Getting Your Access Token
1. Visit [Webex Developer Portal](https://developer.webex.com/docs/getting-started)
2. Log in with your Webex account
3. Copy your personal access token
4. Replace `PERSONAL_ACCESS_TOKEN` in the code
### Understanding the Output
```bash
# Normal successful response
200 1640995200.123456 Y2lzY29zcGFyazovL3VzL1RSQUNLSU5HX0lE
# Rate limit detected
code 429
headers Server: nginx
Retry-After: 60
Date: Thu, 01 Jan 2024 12:00:00 GMT
...
Sleeping for 60 seconds
Asleep for 50 more seconds
Asleep for 40 more seconds
...
```
### Expected Behavior
1. **Normal Operation:**
- Continuous API calls to `/v1/rooms` endpoint
- Display of HTTP 200 responses with timestamps
- Tracking ID logging for debugging purposes
2. **Rate Limit Triggered:**
- HTTP 429 status code detection
- Retry-After header value extraction
- Intelligent sleep with progress updates
- Automatic resumption after wait period
## ๐๏ธ Code Implementation
### Core Function Structure
```python
def sendWebexGET(url):
"""Send authenticated GET request to Webex API"""
request = urllib.request.Request(url,
headers={"Accept": "application/json",
"Content-Type": "application/json"})
request.add_header("Authorization", "Bearer " + bearer)
response = urllib.request.urlopen(request)
return response
```
### Rate Limiting Logic
```python
# Main execution loop
while True:
try:
result = sendWebexGET('https://webexapis.com/v1/rooms')
print(result.getcode(), time.time(), result.headers['Trackingid'])
except urllib.error.HTTPError as e:
if e.code == 429:
# Handle rate limiting
print('code', e.code)
print('headers', e.headers)
print('Sleeping for', e.headers['Retry-After'], 'seconds')
# Chunked sleep for better user experience
sleep_time = int(e.headers['Retry-After'])
while sleep_time > 10:
time.sleep(10)
sleep_time -= 10
print('Asleep for', sleep_time, 'more seconds')
time.sleep(sleep_time)
else:
# Handle other HTTP errors
print(e, e.code)
break
```
### Key Implementation Details
| Component | Description | Purpose |
|-----------|-------------|---------|
| **Infinite Loop** | `while True:` continuous execution | Demonstrate repeated API calls |
| **Exception Handling** | `try/except` for HTTP errors | Catch and handle rate limits |
| **Retry-After Header** | `e.headers['Retry-After']` | Server-specified wait time |
| **Chunked Sleep** | 10-second intervals with updates | User-friendly progress tracking |
| **Bearer Authentication** | `Authorization: Bearer` header | Webex API authentication |
## ๐ง Rate Limiting Concepts
### HTTP 429 Response
When rate limits are exceeded, Webex APIs return:
```http
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Date: Thu, 01 Jan 2024 12:00:00 GMT
Server: nginx
Content-Type: application/json
{
"message": "Rate limit exceeded",
"errors": [
{
"description": "Too Many Requests"
}
]
}
```
### Retry-After Header Values
| Value Type | Example | Description |
|------------|---------|-------------|
| **Seconds** | `60` | Wait 60 seconds before retry |
| **HTTP Date** | `Thu, 01 Jan 2024 12:01:00 GMT` | Wait until specified time |
### Best Practices Demonstrated
1. **Always Check for 429:** Specifically handle rate limit responses
2. **Respect Retry-After:** Use server-provided wait times
3. **Graceful Degradation:** Continue operation after rate limits
4. **User Feedback:** Provide progress updates during waits
5. **Error Handling:** Manage other HTTP errors appropriately
## ๐ Rate Limit Patterns
### Webex API Rate Limits
| API Category | Typical Limits | Retry Behavior |
|--------------|----------------|----------------|
| **REST APIs** | 300 requests/minute | Exponential backoff |
| **Admin APIs** | 100 requests/minute | Fixed Retry-After |
| **Compliance** | 50 requests/minute | Longer wait periods |
| **Recordings** | 20 requests/minute | Progressive delays |
### Triggering Rate Limits
The demo will trigger rate limits by:
- Making continuous requests without delays
- Exceeding the `/v1/rooms` endpoint limits
- Demonstrating real-world rate limiting scenarios
### Recovery Patterns
```python
# Progressive sleep with user feedback
sleep_time = int(e.headers['Retry-After'])
while sleep_time > 10:
time.sleep(10) # Sleep in 10-second chunks
sleep_time -= 10 # Decrement remaining time
print('Asleep for', sleep_time, 'more seconds') # Progress update
time.sleep(sleep_time) # Final sleep for remainder
```
## ๐งช Testing and Experimentation
### Modifying Request Frequency
```python
# Add delays to reduce rate limiting
import time
while True:
try:
result = sendWebexGET('https://webexapis.com/v1/rooms')
print(result.getcode(), time.time(), result.headers['Trackingid'])
time.sleep(2) # Add 2-second delay between requests
except urllib.error.HTTPError as e:
# Rate limiting logic...
```
### Testing Different Endpoints
```python
# Test various API endpoints
endpoints = [
'https://webexapis.com/v1/rooms',
'https://webexapis.com/v1/people/me',
'https://webexapis.com/v1/messages'
]
for endpoint in endpoints:
result = sendWebexGET(endpoint)
print(f"{endpoint}: {result.getcode()}")
```
### Monitoring Rate Limit Headers
```python
# Check rate limit headers in responses
def print_rate_limit_info(response):
headers = response.headers
if 'X-RateLimit-Limit' in headers:
print(f"Rate Limit: {headers['X-RateLimit-Limit']}")
if 'X-RateLimit-Remaining' in headers:
print(f"Remaining: {headers['X-RateLimit-Remaining']}")
if 'X-RateLimit-Reset' in headers:
print(f"Reset Time: {headers['X-RateLimit-Reset']}")
```
## ๐จ Troubleshooting
### Common Issues
| Issue | Solution |
|-------|----------|
| **Invalid Token** | Replace `PERSONAL_ACCESS_TOKEN` with valid token |
| **No Rate Limits** | Add more aggressive request patterns or remove delays |
| **Connection Errors** | Check internet connectivity and API status |
| **Permission Errors** | Ensure token has appropriate scopes |
### Debug Output
```python
# Add debug information
try:
result = sendWebexGET('https://webexapis.com/v1/rooms')
print(f"Success: {result.getcode()} at {time.time()}")
print(f"Tracking ID: {result.headers.get('Trackingid', 'None')}")
print(f"Response Headers: {dict(result.headers)}")
except urllib.error.HTTPError as e:
print(f"HTTP Error: {e.code}")
print(f"Error Headers: {dict(e.headers)}")
print(f"Error Message: {e.read().decode()}")
```
### Network Considerations
- **Proxy Settings:** Configure urllib for corporate proxies
- **SSL Verification:** Handle certificate validation if needed
- **Timeout Settings:** Add request timeouts for reliability
## ๐ Educational Value
### Learning Objectives
This demo teaches:
1. **HTTP Status Code Handling:** Understanding 429 responses
2. **Header Processing:** Reading and using Retry-After values
3. **Exception Management:** Graceful error handling in Python
4. **API Best Practices:** Respectful rate limit behavior
5. **User Experience:** Providing feedback during waits
### Real-World Applications
Apply these patterns to:
- **Data Migration:** Bulk operations with rate limiting
- **Monitoring Tools:** Regular API polling with backoff
- **Integration Services:** Reliable third-party API usage
- **Batch Processing:** Large-scale operations with throttling
### Extended Implementations
```python
# Production-ready rate limiting
import time
import random
class RateLimitHandler:
def __init__(self, max_retries=3):
self.max_retries = max_retries
def make_request(self, url):
for attempt in range(self.max_retries):
try:
return sendWebexGET(url)
except urllib.error.HTTPError as e:
if e.code == 429 and attempt < self.max_retries - 1:
wait_time = int(e.headers.get('Retry-After', 60))
# Add jitter to prevent thundering herd
jitter = random.uniform(0.1, 0.3) * wait_time
time.sleep(wait_time + jitter)
else:
raise
```
## ๐ค Contributing
Suggestions for enhancing this demo:
1. **Additional Error Types:** Handle more HTTP status codes
2. **Multiple Endpoints:** Test different API rate limits
3. **Metrics Collection:** Track rate limit patterns
4. **Configuration Options:** External token management
5. **Advanced Backoff:** Exponential or jittered strategies
## ๐ License
This demo is part of the Webex Samples collection and follows the same licensing terms.
## ๐ Support
For more information:
- **Blog Post**: [Rate Limiting with the Webex API](https://developer.webex.com/blog/rate-limiting-and-the-webex-api)
- **API Documentation**: [Webex Rate Limiting](https://developer.webex.com/docs/rate-limiting)
- **Developer Community**: [Webex Developer Portal](https://developer.webex.com/community)
## Thanks!
Made with โค๏ธ by the Webex Developer Relations Team at Cisco
---
**Note**: This demo intentionally triggers rate limits for educational purposes. In production applications, implement proper request spacing and rate limit prevention strategies.