An open API service indexing awesome lists of open source software.

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.

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.