Skip to content

Example reaching the API request rate limit, and handling future requests using the Retry-After header.

Notifications You must be signed in to change notification settings

WebexSamples/WebexRetryAfterDemo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 

Repository files navigation

⏱️ 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.

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:

    wget https://raw.githubusercontent.com/WebexSamples/WebexRetryAfterDemo/main/retryafterdemo.py
    # or manually download retryafterdemo.py
  2. Configure your access token:

    # Edit retryafterdemo.py
    bearer = "YOUR_PERSONAL_ACCESS_TOKEN_HERE"
  3. Run the demonstration:

    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
  2. Log in with your Webex account
  3. Copy your personal access token
  4. Replace PERSONAL_ACCESS_TOKEN in the code

Understanding the Output

# 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

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

# 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/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

# 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

# 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

# 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

# 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

# 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

# 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:

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.

About

Example reaching the API request rate limit, and handling future requests using the Retry-After header.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages