Enhancing API Requests with Requests Module: Singleton Sessions, Back-Off Timers, and Rate Limiting

In today's interconnected world, interacting with RESTful APIs has become a common task for developers. The Python requests module is a versatile tool for making HTTP requests, but to truly optimize API consumption, we need to tackle issues like managing sessions, handling HTTP errors gracefully, and respecting rate limits. In this blog post, we'll explore how to achieve all of this by combining the requests module with the Singleton design pattern, HTTPAdapter for back-off timers, and implementing rate limiting.


Singleton Sessions for API Requests


When making API requests, it's important to reuse the same session for requests to the same base URL. This not only saves resources but also ensures consistent behavior, such as maintaining cookies or headers across requests. Here's how you can implement Singleton sessions:


```python

import requests

from requests.adapters import HTTPAdapter


class SessionManager:

    _instances = {}


    def __new__(cls, base_url):

        if base_url not in cls._instances:

            cls._instances[base_url] = super(SessionManager, cls).__new__(cls)

            cls._instances[base_url].session = requests.Session()

            cls._instances[base_url].session.mount('https://', HTTPAdapter(max_retries=3))

        return cls._instances[base_url]


# Example usage:

base_url = "https://api.example.com"

session_manager = SessionManager(base_url)

session = session_manager.session

```


With this setup, the same session is reused for requests to the same base_url, providing efficiency and consistent behavior.


Implementing a Back-Off Timer


Handling HTTP errors gracefully is crucial for robust API interactions. We can use the HTTPAdapter to implement a back-off timer for specific HTTP return codes, such as HTTP 429 (Too Many Requests) and HTTP 503 (Service Unavailable). Here's how to set it up:


```python

from requests.adapters import HTTPAdapter

from requests.packages.urllib3.util.retry import Retry


base_url = "https://api.example.com"

session = SessionManager(base_url).session


# Create a Retry instance with back-off settings

retry = Retry(

    total=3,  # Total number of retries

    backoff_factor=1,  # Back-off factor (exponential back-off)

    status_forcelist=[429, 503]  # Retry for HTTP 429 and 503 status codes

)


# Mount the HTTPAdapter with the Retry settings

adapter = HTTPAdapter(max_retries=retry)

session.mount('https://', adapter)

```


This configuration ensures that the session will automatically retry requests in the event of HTTP 429 or 503 responses, with a progressively increasing delay between retries.


Rate Limiting API Calls


Respecting rate limits set by an API is crucial to avoid being rate-limited or banned. To implement rate limiting, we can use a simple timer to control the number of requests made within a specific time interval. Here's a basic example:


```python

import time


class RateLimiter:

    def __init__(self, requests_per_minute):

        self.requests_per_minute = requests_per_minute

        self.interval = 60 / requests_per_minute

        self.last_request_time = 0


    def wait(self):

        current_time = time.time()

        time_since_last_request = current_time - self.last_request_time

        if time_since_last_request < self.interval:

            time_to_wait = self.interval - time_since_last_request

            time.sleep(time_to_wait)

        self.last_request_time = time.time()


# Example usage:

rate_limiter = RateLimiter(requests_per_minute=60)

for _ in range(100):

    rate_limiter.wait()

    response = session.get("https://api.example.com/resource")

    # Process the response as needed

```


In this example, the RateLimiter class ensures that no more than the specified number of requests per minute are made to the API, helping you stay within the rate limits.


Conclusion


By combining Singleton sessions, implementing back-off timers for HTTP errors, and respecting rate limits, you can create robust and efficient API interactions. These practices not only improve the reliability of your code but also ensure that you are a responsible consumer of the services you're interacting with.


Raell Dottin

Comments