Back to all posts
Automated Retrieval of Web Archives with Rotating Proxies

Automated Retrieval of Web Archives with Rotating Proxies

November 4, 2026

Overview

Archiving platforms such as the Wayback Machine preserve historical versions of web pages, but direct access can be throttled or blocked by target sites. By combining rotating proxy sessions with a disciplined fetching strategy, you can retrieve large batches of archived snapshots reliably, respect rate limits, and verify that each snapshot is still accessible.

Why Archive Scraping Matters

  • Historical research: Scholars need consistent captures of pages that have changed or disappeared.
  • Content audits: Marketing teams compare past and present messaging across domains.
  • Compliance checks: Organizations verify that public disclosures match archived records.
  • Data enrichment: AI training sets benefit from a temporal dimension.

A robust pipeline must rotate IPs to avoid detection, throttle requests to stay within polite limits, and confirm that each snapshot is reachable from a variety of geographic locations.

Setting Up a Rotating Proxy Session with RoProxy

RoProxy provides rotating sessions that can be controlled via a simple REST API. The workflow below assumes you already have a Pro or Business plan session with a known SESSION_ID and an API_KEY.

Step 1 – Obtain a Fresh IP

import os
import requests
import time

API_KEY = os.getenv("API_KEY", "API_KEY")
SESSION_ID = os.getenv("SESSION_ID", "SESSION_ID")

base = "https://backend-api.ro-proxy.com"
headers = {"Authorization": f"Bearer {API_KEY}"}

# List existing sessions (optional)
sessions_resp = requests.get(f"{base}/api/session", headers=headers)
sessions = sessions_resp.json()  # expects a list of session objects

# Rotate the chosen session
rotate_url = f"{base}/api/session/{SESSION_ID}/rotate"
rotate_resp = requests.post(rotate_url, headers=headers)
print(f"Rotation response: {rotate_resp.status_code}")

The call to /api/session/SESSION_ID/rotate forces an immediate IP change, which is useful before a high‑volume crawl.

Step 2 – Configure Proxy Environment Variables

Replace the placeholders with your actual gateway details. RoProxy sessions expose both an HTTP port and, on Pro/Business plans, a SOCKS5 port.

# HTTP proxy (used for both http and https requests)
export HTTP_PROXY="http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT"
export HTTPS_PROXY="http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT"

# SOCKS5 proxy (optional, Pro/Business)
export SOCKS5_PROXY="socks5://USERNAME:PASSWORD@GATEWAY_HOST:SOCKS5_PORT"

These variables are read automatically by libraries such as requests when a proxy dict is supplied.

Building the Archive Fetcher in Python

Below is a self‑contained script that demonstrates the full flow: session rotation, proxy configuration, batch retrieval of archived URLs, rate limiting, and integrity verification.

Step 3 – Core Archive Retrieval Function

import os
import requests
import hashlib
import json
import time

# Load proxy settings from environment variables
http_proxy = os.getenv("HTTP_PROXY")
socks5_proxy = os.getenv("SOCKS5_PROXY")

proxies = {
    "http": http_proxy,
    "https": http_proxy,
}
if socks5_proxy:
    # Some libraries support "socks5" scheme; requests does not natively support it.
    # For demonstration we keep the HTTP proxy; switch to urllib or aiohttp if needed.
    pass

def fetch_archive_snapshot(original_url: str, timestamp: str) -> bytes | None:
    """Return raw HTML of an archived snapshot or None on failure."""
    archive_url = f"https://web.archive.org/web/{timestamp}/{original_url}"
    try:
        resp = requests.get(archive_url, proxies=proxies, timeout=15)
        if resp.status_code == 200:
            return resp.content
        else:
            print(f"  ↳ {archive_url} → status {resp.status_code}")
            return None
    except Exception as e:
        print(f"  ↳ {archive_url} → error {e}")
        return None

Step 4 – Rate Limiting and Batch Processing

def crawl_archives(urls, timestamps, out_path="archives.json"):
    results = []
    for idx, (url, ts) in enumerate(zip(urls, timestamps), start=1):
        print(f"Processing {idx}/{len(urls)}: {url} @ {ts}")
        content = fetch_archive_snapshot(url, ts)
        entry = {
            "original_url": url,
            "timestamp": ts,
            "archive_url": f"https://web.archive.org/web/{ts}/{url}",
            "content_hash": hashlib.sha256(content).hexdigest() if content else None,
            "size_bytes": len(content) if content else 0,
            "retrieved_at": time.isoformat(time.localtime()),
        }
        results.append(entry)
        # Respect a conservative throttle – 1 request per second per IP rotation
        time.sleep(1)

    with open(out_path, "w", encoding="utf-8") as f:
        json.dump(results, f, indent=2)
    print(f"Saved {len(results)} entries to {out_path}")

The script rotates the RoProxy session before the crawl (Step 1) and then respects a one‑request‑per‑second ceiling to stay under typical archival limits.

Step 5 – Verifying Exit IPs and Geographic Diversity

To ensure traffic originates from distinct IPs, you can query a public service and compare results across runs.

def check_exit_ip() -> str:
    """Return the public IP seen by the proxy."""
    resp = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10)
    data = resp.json()
    return data["ip"]

# Example usage after a rotation
print(f"Current exit IP: {check_exit_ip()}")

If you need to confirm geographic variety, combine this check with a GeoIP lookup (e.g., MaxMind DB) or simply rotate sessions for each country you target on a Pro/Business plan.

Scheduling and Scaling the Job

A typical archive crawl can involve thousands of URLs. Use a scheduler like cron or systemd timers to invoke the Python script periodically. For higher throughput, consider:

  • Concurrency: Replace time.sleep with an asynchronous framework (e.g., asyncio + aiohttp) and limit concurrent tasks to avoid overwhelming the proxy.
  • Session pooling: Reuse the same session ID across multiple runs and only rotate when a failure threshold is reached.
  • Error back‑off: Implement exponential back‑off for HTTP 429 or 503 responses.

Example snippet for a simple cron job (run daily at 02:00):

0 2 * * * /usr/bin/python3 /path/to/archive_crawler.py >> /var/log/archive_crawl.log 2>&1

Best Practices and Troubleshooting

  • Proxy health: Periodically call the RoProxy session list endpoint to confirm the session is still active. A 401 or 404 indicates the credentials need refresh.
  • CAPTCHA handling: If the target site presents a CAPTCHA, rotate the IP immediately using the same /rotate endpoint and retry the request.
  • Logging: Store rotation timestamps and exit IPs in a log file; this helps correlate blocks with specific IP changes.
  • Fallback proxies: Keep a secondary proxy configuration (e.g., a backup HTTP endpoint) ready for when the primary gateway becomes unreachable.
  • SSL verification: For HTTPS archives, disable certificate verification only in controlled environments; otherwise, keep it enabled to avoid man‑in‑the‑middle risks.

When debugging connection timeouts, use tools like tcpdump or Wireshark to inspect the TCP handshake. A missing TCP SYN‑ACK often points to a blocked or overloaded gateway.

Conclusion

Automated archive retrieval becomes dependable when you combine disciplined IP rotation with careful rate limiting and thorough verification. By leveraging RoProxy’s session API and configuring its gateways through standard proxy environment variables, you obtain a clean, reproducible pipeline that can scale from a handful of historic pages to millions of snapshots. Remember to rotate sessions before heavy bursts, monitor exit IPs, and keep fallback mechanisms in place to ensure your archival data stays current and complete.

With RoProxy you can focus on the content you need rather than the mechanics of staying under anti‑scraping defenses.