Join our Discord/Telegram for free 100MB and other exclusive perks!

How to Use Python Requests Retry with Proxying Proxies

IN THIS ARTICLE:

When web scraping or proxying in Python, it is often necessary to add the type of reliability implied by automatically sending failed requests. This paper is going to investigate the details of implementing Python requests retry logic via built-in features, custom, and proxy integration, but in the context of Proxying usage patterns.

Why Retry Failed Requests?

HTTP requests do not always succeed on the first attempt. Temporary issues such as server overload, rate limiting, unstable connections, or network interruptions can cause requests to fail unexpectedly.

Some of the most common retry-worthy errors include:

  • Server-side errors like 500, 502, 503, and 504
  • Rate limiting responses, such as 429 Too Many Requests
  • Connection timeouts and temporary network failures

Adding retry logic helps make your Python scripts more reliable and resilient. Instead of failing immediately, your application can automatically resend requests after short delays. This is especially useful in web scraping and automation workflows that rely on Proxying.io proxies for stable and uninterrupted data collection.

Preparing Your Python Environment

Before implementing retry logic, make sure your Python environment is ready and that the required libraries are installed.

Install the necessary packages using pip:

pip install requests urllib3 retrying

These libraries provide:

  • requests: Sends HTTP requests in Python
  • urllib3:  Powers advanced retry functionality
  • retrying:  Adds decorator-based retry support

After installation, verify everything is working properly:

import requests
from urllib3.util.retry import Retry
from retrying import retry
print("Environment ready!")

If the script runs without errors and prints Environment ready!, your setup is complete, and you can continue with the retry implementation examples.

Using HTTPAdapter + Retry (urllib3)

The simplest is to employ requests.adapters.HTTPAdapter together with urllib3.util.retry.Retry:

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import requests
retry_strategy = Retry(
    total=5,
   backoff_factor=2,
    status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
response = session.get("https://www.proxying.io")
print(response.status_code)
print(response.text[:200])

This config:

  • Retries of up to 5 times,
  • Implements exponential backoff (delay enhancements as a copy continues retries).
  • Target common transient error codes.

The backoff_factor gradually increases the waiting time between retries, preventing excessive request bursts against the target server.

Proxy Support via Proxying

When working with Proxying.io proxies, you can combine proxy routing with retry handling for improved scraping stability and IP rotation.

proxies = {
    "http": "http://USERNAME:PASSWORD@proxy.proxying.io:PORT",
    "https": "http://USERNAME:PASSWORD@proxy.proxying.io:PORT",
}
response = session.get(
    "https://example.com",
    proxies=proxies
)

Using retries together with Proxying.io proxies helps handle temporary proxy connection failures, reduce interruptions caused by blocked IPs, improve scraping success rates, and maintain consistent access to target websites.

This setup is particularly effective for large-scale scraping tasks where reliability and request continuity are important.

Exponential Backoff Formula Explained

Backoff strategies prevent rapid-fire retries, which could aggravate server load:

delay = base_delay * (backoff_factor ** retry_count)

Or, as used in urllib3:

delay = backoff_factor * (2 ** retry_count)

For example, with a backoff_factor of 2:

Delay sequence: 2, 4, 8, 16… seconds, increasing per retry.

Using Decorators for Retry Wrapping

If you need a lightweight way to add retry behavior to specific functions, decorators provide a clean and readable solution. The retrying library allows you to wrap retry logic around a function without manually managing loops or sessions.

from retrying import retry
import requests
@retry(
    stop_max_attempt_number=5,
    wait_fixed=2000,
    retry_on_result=lambda r: r is None
)
def fetch():
    response = requests.get("https://example.com")
    if response.status_code == 200:
        return response.text
    return None

How This Works

  • Retries the function up to 5 times
  • Waits 2 seconds between each attempt
  • Retries if the function returns None

Stops automatically once a valid response is returned

Fully Working Code

from retrying import retry
import requests
@retry(
    stop_max_attempt_number=5,          # Maximum 5 retry attempts
    wait_fixed=2000,                    # Wait 2 seconds between retries
    retry_on_result=lambda r: r is None # Retry if function returns None
)
def fetch(url):
    try:
        response = requests.get(url, timeout=10)
        # Only return data if request is successful
        if response.status_code == 200:
            return response.text
        # Return None for retryable failures
        return None
    except requests.exceptions.RequestException:
        # Any network error triggers a retry
        return None
if __name__ == "__main__":
    url = "https://example.com"
    result = fetch(url)
    if result:
        print("Request successful!")
        print(result[:500])  # print first 500 characters
    else:
        print("Request failed after retries.")

What this full code does

  • Retries up to 5 times
  • Waits 2 seconds between attempts
  • Retries on:
    • None result
    • network errors
  • Stops when a valid 200 OK response is received
  • Includes a simple test runner (__main__)

Basic Process

  • Basic configuration: use HTTPAdapter with Retry to have automatic, reliable retries.
  • Proxy: Integrate Proxying.io proxies into your session in order to rotate your IPs and avoid rate cut-offs.
  • Advanced behavior: Introduce custom classes (e.g. SmartRetrySession) to be more respectful towards rate-limiting headers and backoff attempts.
  • Simple utility: Decorators can be used to provide convenience when re-trying individual functions.
  • Best practices: Bring together timeout management, logs, and randomization to handle retry resilience.

Conclusion

A robustly designed Python request retry scheme is a cost-effective, highly effective way to improve how robust and reliable the web scraping or general API-driven process can be, particularly when augmented with Proxying.io. The combination of retries with proxy logic, timeouts, and backoff strategies is incredibly useful when it comes to the score of the final result: Robust and scalable automation.

Frequently Asked Questions (FAQs)

Yes. You can attach Proxying proxy credentials to the session and combine them with retry logic.

Retries handle failed proxy connections or blocked IPs while rotating to a fresh Proxying IP

No. A 404 (Not Found) is permanent, so retries won’t help.

About the author

IN THIS ARTICLE:

Earn Up to $2500 from referrals!

Subscribe to our newsletter

Want to scale your web data gathering with Proxies?

Related articles