Hai configurato il parser, hai collegato i proxy, ma l'API continua a restituire errori 429 "Too Many Requests" o blocca l'accesso? Il problema non è nei proxy in sé, ma in una strategia errata nel loro utilizzo. Il rate limiting è un meccanismo di protezione dell'API che limita il numero di richieste da un singolo indirizzo IP in un determinato periodo. In questo articolo analizzeremo perché si verificano blocchi quando si lavora tramite proxy e come configurare correttamente il sistema per bypassare i limiti.
Che cos'è il rate limiting API e come funziona
Il rate limiting (limitazione della frequenza delle richieste) è un meccanismo di protezione dell'API contro sovraccarichi e abusi. Il servizio stabilisce un limite sul numero di richieste che possono essere effettuate da una singola fonte in un determinato intervallo di tempo. Ad esempio, le API popolari utilizzano tali limiti:
- Twitter API: 300 richieste ogni 15 minuti per accesso standard
- Instagram Graph API: 200 richieste all'ora per applicazione
- Google Maps API: dipende dal piano, solitamente 100-1000 richieste al giorno
- Wildberries API: limiti non ufficiali di circa 60 richieste al minuto da un singolo IP
- Avito API: 10 richieste al secondo per scraping degli annunci
Esistono diversi metodi per determinare la fonte delle richieste, ai quali viene applicato il rate limiting:
Indirizzo IP: il metodo più comune. L'API conta il numero di richieste da un IP specifico in una finestra temporale.
Chiave API: se utilizzi l'autenticazione tramite chiave, il limite è legato a essa indipendentemente dall'IP.
User-Agent e fingerprint: alcune API analizzano gli header del browser e creano un'impronta digitale del client.
Sessione (cookies): il limite può essere legato alla sessione dell'utente tramite i cookies.
Quando il limite viene superato, l'API restituisce lo stato HTTP 429 "Too Many Requests" e l'header Retry-After, che indica il tempo fino al ripristino del limite. Alcuni servizi utilizzano una "finestra mobile" (rolling window), dove il limite viene aggiornato gradualmente, mentre altri utilizzano una finestra fissa che si azzera a un orario specifico.
Perché i proxy non salvano automaticamente dal rate limiting
Molti sviluppatori pensano erroneamente che sia sufficiente collegare i proxy per inviare un numero illimitato di richieste. Nella pratica, si verificano i seguenti problemi:
Utilizzo di un solo proxy per tutte le richieste
Se il tuo script utilizza lo stesso indirizzo IP del proxy per tutte le richieste, l'API lo vede come un utente normale e applica i limiti standard. Ad esempio, hai configurato un parser dei prezzi con Wildberries tramite un proxy residenziale. Il parser effettua 100 richieste al minuto, ma il limite è di 60 richieste. Risultato: blocco dell'IP per 10-30 minuti.
Rotazione lenta degli IP
Alcuni utilizzano un pool di 5-10 proxy e passano tra di essi in modo sequenziale. Il problema è che ogni IP raggiunge comunque il limite più velocemente di quanto avvenga una rotazione completa. Supponiamo che tu abbia 10 proxy e un limite di 100 richieste all'ora per IP. Se effettui 1000 richieste all'ora, ogni proxy riceverà 100 richieste — esattamente al limite. Qualsiasi irregolarità nella distribuzione porterà a blocchi.
Ignorare altri fattori di identificazione
Anche con una rotazione IP ideale, potresti essere bloccato se:
- Tutte le richieste provengono dallo stesso User-Agent (ad esempio,
python-requests/2.28.0) - Viene utilizzata una sola chiave API per tutte le richieste
- Le richieste arrivano con una periodicità perfetta (ogni 0,5 secondi) — questo appare come un bot
- Gli indirizzi IP dei proxy si trovano nella stessa sottorete (ad esempio, tutti nell'intervallo 192.168.1.x)
Reputazione degli indirizzi IP
I proxy dei data center spesso finiscono nelle blacklist, poiché i loro IP sono utilizzati da centinaia di altri utenti per lo scraping. L'API può applicare limiti più severi a tali indirizzi o bloccarli immediatamente. Ad esempio, Instagram e Facebook bannano aggressivamente i data center, anche se non superi i limiti ufficiali.
Strategie di rotazione IP per bypassare i limiti
Una corretta rotazione dei proxy è la chiave per bypassare il rate limiting. Esaminiamo strategie efficaci a seconda del compito.
Rotazione dopo ogni richiesta
La strategia più aggressiva: ogni richiesta passa attraverso un nuovo IP. Adatta per compiti con limiti molto severi (1-5 richieste per IP) o quando è necessario distribuire al massimo il carico. Per questo vengono utilizzati proxy residenziali con rotazione automatica — forniscono un pool di milioni di IP, e ogni richiesta riceve automaticamente un nuovo indirizzo.
Esempio di utilizzo: scraping di Instagram tramite un'API non ufficiale, dove il limite è di 5 richieste al minuto da un singolo IP. Con la rotazione dopo ogni richiesta, puoi effettuare 300 richieste al minuto tramite 300 IP diversi.
Vantaggi: massima protezione dal rate limiting, ogni IP viene utilizzato al minimo.
Svantaggi: alto costo (i proxy residenziali sono più costosi), possibili ritardi nel cambio IP, più difficile mantenere le sessioni.
Rotazione temporale (sticky sessions)
L'indirizzo IP viene utilizzato per un certo periodo (5-30 minuti), quindi cambia in uno nuovo. Questa strategia è adatta per API che richiedono il mantenimento della sessione, o quando è necessario effettuare più richieste correlate da un unico "utente".
Calcolo del tempo ottimale di rotazione: se il limite dell'API è di 100 richieste all'ora e prevedi di effettuare 50 richieste tramite un IP, utilizza una sticky session di 30 minuti. In questo tempo effettuerai 25 richieste (con carico uniforme), che è la metà del limite.
Rotazione per pool con monitoraggio dei limiti
Strategia avanzata: il tuo script tiene traccia del numero di richieste da ciascun IP e passa automaticamente a un nuovo IP quando si avvicina al limite. Ad esempio, hai un pool di 20 proxy, il limite dell'API è di 100 richieste all'ora. Lo script monitora il contatore per ogni IP e passa al successivo al raggiungimento di 90 richieste.
Questa strategia richiede la programmazione della logica, ma offre la massima efficienza: utilizzi ogni proxy al massimo senza superare i limiti.
Rotazione geografica
Alcune API applicano limiti diversi a seconda della regione. Ad esempio, un servizio può limitare le richieste dagli Stati Uniti più severamente rispetto all'Europa. In tali casi, utilizza proxy provenienti da diversi paesi e distribuisci il carico tra di essi.
| Strategia di rotazione | Quando utilizzare | Tipo di proxy |
|---|---|---|
| Dopo ogni richiesta | Limiti severi (1-10 richieste/IP), scraping di social media | Residenziali con auto-rotazione |
| Per tempo (5-30 min) | Richiesta di sessione, limiti medi (50-200 richieste/ora) | Residenziali sticky o mobili |
| Per pool con monitoraggio dei limiti | Grande volume di scraping, limiti API noti | Qualsiasi tipo con pool di 10+ IP |
| Geografica | Limitazioni regionali, scraping di contenuti locali | Residenziali da diversi paesi |
Configurazione dei ritardi tra le richieste
Anche con una rotazione IP ideale, è importante configurare correttamente i ritardi tra le richieste. Richieste troppo rapide sembrano un attacco, anche se provengono da IP diversi.
Calcolo del ritardo minimo
Formula: ritardo = (finestra temporale in secondi / limite richieste) × fattore di sicurezza
Esempio: l'API consente 100 richieste all'ora (3600 secondi). Ritardo minimo = 3600 / 100 = 36 secondi. Aggiungiamo un fattore di sicurezza di 1.2: 36 × 1.2 = 43 secondi tra le richieste da un singolo IP.
Se utilizzi 10 proxy con rotazione, puoi effettuare richieste ogni 4.3 secondi (43 / 10), senza superare il limite su nessun IP.
Ritardi casuali (jitter)
Invece di un ritardo fisso di 5 secondi, utilizza intervalli casuali, ad esempio, da 3 a 7 secondi. Questo rende il tuo traffico simile alle azioni di un utente reale. Molti sistemi di protezione dai bot analizzano i modelli: se le richieste arrivano esattamente ogni 5.0 secondi, è sospetto.
Ritardo esponenziale in caso di errori
Quando ricevi un errore 429, non continuare a inviare richieste immediatamente. Utilizza un ritardo esponenziale: il primo tentativo dopo 1 secondo, il secondo dopo 2, il terzo dopo 4, il quarto dopo 8 e così via. Questa è una pratica standard che le API si aspettano dai clienti.
Consiglio: Controlla l'header Retry-After nella risposta dell'API. Indica il tempo esatto in cui puoi ripetere la richiesta. Utilizza questo valore invece di ritardi arbitrari.
Quale tipo di proxy scegliere per lavorare con l'API
La scelta del tipo di proxy influisce criticamente sul successo del bypass del rate limiting. Analizziamo i pro e i contro di ciascuna opzione per lavorare con l'API.
Proxy residenziali
I proxy residenziali utilizzano indirizzi IP di utenti reali, assegnati dai fornitori di servizi Internet. Per l'API, questo appare come una normale connessione domestica.
Vantaggi per l'API:
- Alta fiducia: le API raramente bloccano gli IP domestici
- Pool enormi: milioni di IP per la rotazione
- Diversità geografica: IP da diverse città e paesi
- Adatti per social media e API rigorose (Instagram, Facebook, TikTok)
Svantaggi:
- Alto costo: pagamento solitamente per traffico (da $5-15 per 1 GB)
- Velocità variabile: dipende dalla connessione dell'utente finale
- Instabilità: l'IP può disconnettersi in qualsiasi momento
Quando utilizzare: scraping di Instagram, Facebook, TikTok, lavoro con API di marketplace (Wildberries, Ozon), qualsiasi compito dove la reputazione dell'IP è critica.
Proxy mobili
I proxy mobili utilizzano IP di operatori mobili (4G/5G). Un singolo IP è spesso utilizzato da migliaia di utenti reali contemporaneamente, quindi le API raramente li bloccano.
Vantaggi per l'API:
- Massima fiducia: le API non possono bloccare gli IP degli operatori mobili
- Ideali per applicazioni mobili e API (Instagram, TikTok, Snapchat)
- Cambio automatico di IP al riconnessione (modalità aereo)
- Un IP può effettuare più richieste senza essere bannato
Svantaggi:
- Costo molto alto: da $50-150 per un singolo IP al mese
- Pool piccoli: difficile ottenere centinaia di IP mobili
- Velocità variabile: dipende dalla qualità della connessione mobile
Quando utilizzare: lavoro con API mobili, scraping di Instagram/TikTok in grandi volumi, quando è necessaria la massima protezione dai ban.
Proxy dei data center
I proxy dei data center sono indirizzi IP di server collocati in data center. Non sono associati a utenti reali.
Vantaggi per l'API:
- Basso costo: da $1-5 per IP al mese
- Alta velocità: canali 1-10 Gbps
- Stabilità: gli IP non si disconnettono casualmente
- Pool grandi: facile ottenere centinaia di IP
Svantaggi:
- Bassa fiducia: molte API bloccano i data center
- IP spesso nelle blacklist a causa di altri utenti
- Non adatti per social media e servizi rigorosi
Quando utilizzare: scraping di API pubbliche senza protezione rigorosa (meteo, tassi di cambio, notizie), lavoro con API proprie, test e sviluppo.
| Criterio | Residenziali | Mobili | Data center |
|---|---|---|---|
| Fiducia API | Alta | Massima | Bassa |
| Dimensione del pool | Milioni di IP | Centinaia di IP | Migliaia di IP |
| Velocità | Media (10-50 Mbit/s) | Media (5-100 Mbit/s) | Alta (100+ Mbit/s) |
| Costo | $5-15/GB | $50-150/IP/mese | $1-5/IP/mese |
| Per social media | ✅ Ottimo | ✅ Ideale | ❌ Non adatti |
| Per API pubbliche | ✅ Bene | ✅ Bene (costoso) | ✅ Ottimo |
Implementazione pratica: esempi di codice in Python
Esaminiamo esempi concreti di implementazione del bypass del rate limiting utilizzando proxy. Tutti gli esempi sono in Python con la libreria requests.
Rotazione semplice da un pool di proxy
Implementazione di base con rotazione ciclica degli IP da un elenco:
import requests
import time
from itertools import cycle
# Elenco dei proxy (formato: protocollo://utente:password@host:porta)
PROXY_LIST = [
'http://user1:pass1@proxy1.example.com:8080',
'http://user2:pass2@proxy2.example.com:8080',
'http://user3:pass3@proxy3.example.com:8080',
]
# Creiamo un iteratore ciclico
proxy_pool = cycle(PROXY_LIST)
def make_request(url):
proxy = next(proxy_pool) # Prendiamo il prossimo proxy dal pool
proxies = {
'http': proxy,
'https': proxy
}
try:
response = requests.get(url, proxies=proxies, timeout=10)
return response
except requests.exceptions.RequestException as e:
print(f"Errore con il proxy {proxy}: {e}")
return None
# Esempio di utilizzo
for i in range(10):
response = make_request('https://api.example.com/data')
if response and response.status_code == 200:
print(f"Richiesta {i+1}: successo")
time.sleep(2) # Ritardo tra le richieste
Rotazione con monitoraggio dei limiti
Versione più avanzata, che conta le richieste per ogni proxy e passa al limite:
import requests
import time
from collections import defaultdict
class ProxyRotator:
def __init__(self, proxy_list, max_requests_per_ip=90, time_window=3600):
self.proxy_list = proxy_list
self.max_requests = max_requests_per_ip # Limite di richieste per IP
self.time_window = time_window # Finestra temporale in secondi
self.request_counts = defaultdict(list) # Storia delle richieste per ogni IP
self.current_index = 0
def get_proxy(self):
"""Restituisce il proxy con il minor numero di richieste"""
current_time = time.time()
# Pulisce le registrazioni vecchie oltre la finestra temporale
for proxy in self.request_counts:
self.request_counts[proxy] = [
t for t in self.request_counts[proxy]
if current_time - t < self.time_window
]
# Cerca il proxy con il minor numero di richieste
available_proxies = []
for proxy in self.proxy_list:
count = len(self.request_counts[proxy])
if count < self.max_requests:
available_proxies.append((proxy, count))
if not available_proxies:
# Se tutti i proxy hanno esaurito il limite, aspetta
oldest_request = min(
min(times) for times in self.request_counts.values() if times
)
wait_time = self.time_window - (current_time - oldest_request) + 1
print(f"Tutti i proxy hanno esaurito il limite. Attesa di {wait_time:.0f} sec...")
time.sleep(wait_time)
return self.get_proxy()
# Seleziona il proxy con il minor numero di richieste
proxy = min(available_proxies, key=lambda x: x[1])[0]
self.request_counts[proxy].append(current_time)
return proxy
def make_request(self, url, **kwargs):
proxy = self.get_proxy()
proxies = {'http': proxy, 'https': proxy}
try:
response = requests.get(url, proxies=proxies, timeout=10, **kwargs)
return response
except requests.exceptions.RequestException as e:
print(f"Errore con il proxy {proxy}: {e}")
return None
# Esempio di utilizzo
PROXY_LIST = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
]
rotator = ProxyRotator(PROXY_LIST, max_requests_per_ip=100, time_window=3600)
for i in range(500): # Effettua 500 richieste
response = rotator.make_request('https://api.example.com/data')
if response and response.status_code == 200:
print(f"Richiesta {i+1}: successo")
time.sleep(1) # Ritardo minimo
Gestione degli errori 429 con ritardo esponenziale
Corretta gestione della risposta "Too Many Requests" tenendo conto dell'header Retry-After:
import requests
import time
def make_request_with_retry(url, proxies, max_retries=5):
"""Effettua una richiesta con ripetizioni automatiche in caso di errore 429"""
for attempt in range(max_retries):
try:
response = requests.get(url, proxies=proxies, timeout=10)
if response.status_code == 200:
return response
elif response.status_code == 429:
# Controlla l'header Retry-After
retry_after = response.headers.get('Retry-After')
if retry_after:
wait_time = int(retry_after)
print(f"Limite di richiesta. Attesa di {wait_time} sec (da Retry-After)")
else:
# Ritardo esponenziale: 2^attempt secondi
wait_time = 2 ** attempt
print(f"Limite di richiesta. Tentativo {attempt+1}, attesa di {wait_time} sec")
time.sleep(wait_time)
continue
else:
print(f"Errore HTTP {response.status_code}")
return None
except requests.exceptions.RequestException as e:
print(f"Errore di connessione: {e}")
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
return None
print(f"Superato il numero di tentativi ({max_retries})")
return None
# Esempio di utilizzo
proxies = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
response = make_request_with_retry('https://api.example.com/data', proxies)
if response:
print("Dati ricevuti:", response.json())
Utilizzo di proxy residenziali con rotazione automatica
Molti fornitori di proxy residenziali offrono un endpoint unico, che cambia automaticamente IP ad ogni richiesta. Esempio di configurazione:
import requests
import random
import time
# Proxy residenziali con rotazione automatica
# Formato: protocollo://username:password@gateway:porta
ROTATING_PROXY = 'http://customer-USER:PASS@proxy.provider.com:12321'
def make_request_rotating(url):
"""Richiesta tramite proxy rotante (nuovo IP ogni volta)"""
proxies = {
'http': ROTATING_PROXY,
'https': ROTATING_PROXY
}
# Aggiungi un User-Agent casuale per maggiore anonimato
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36',
]
headers = {
'User-Agent': random.choice(user_agents)
}
try:
response = requests.get(url, proxies=proxies, headers=headers, timeout=15)
return response
except requests.exceptions.RequestException as e:
print(f"Errore: {e}")
return None
# Effettua 100 richieste tramite IP diversi
for i in range(100):
response = make_request_rotating('https://api.example.com/data')
if response and response.status_code == 200:
print(f"Richiesta {i+1}: successo, IP cambiato")
# Ritardo casuale di 1-3 secondi
time.sleep(random.uniform(1, 3))
Monitoraggio dei limiti e gestione degli errori
Un lavoro efficace con l'API richiede un monitoraggio costante dei limiti e una corretta gestione degli errori. Ecco le pratiche chiave:
Analisi degli header di risposta
Molte API restituiscono informazioni sui limiti negli header di risposta. Header standard:
X-RateLimit-Limit— numero massimo di richieste nella finestraX-RateLimit-Remaining— quante richieste rimangonoX-RateLimit-Reset— tempo di ripristino del limite (timestamp Unix)Retry-After— dopo quanti secondi puoi ripetere la richiesta
Esempio di lettura di questi header:
response = requests.get(url, proxies=proxies)
# Controlla gli header dei limiti
limit = response.headers.get('X-RateLimit-Limit')
remaining = response.headers.get('X-RateLimit-Remaining')
reset_time = response.headers.get('X-RateLimit-Reset')
if remaining:
remaining = int(remaining)
if remaining < 10:
print(f"Attenzione! Rimangono solo {remaining} richieste")
if reset_time:
import datetime
reset_dt = datetime.datetime.fromtimestamp(int(reset_time))
print(f"Il limite si ripristinerà a {reset_dt}")
Logging e statistiche
Tieni un dettagliato registro delle statistiche delle richieste per ogni proxy. Questo aiuterà a identificare gli IP problematici e ottimizzare la rotazione:
import json
from datetime import datetime
class RequestLogger:
def __init__(self):
self.stats = {}
def log_request(self, proxy, status_code, response_time):
if proxy not in self.stats:
self.stats[proxy] = {
'total': 0,
'success': 0,
'rate_limited': 0,
'errors': 0,
'avg_response_time': 0
}
self.stats[proxy]['total'] += 1
if status_code == 200:
self.stats[proxy]['success'] += 1
elif status_code == 429:
self.stats[proxy]['rate_limited'] += 1
else:
self.stats[proxy]['errors'] += 1
# Aggiorna il tempo medio di risposta
current_avg = self.stats[proxy]['avg_response_time']
total = self.stats[proxy]['total']
self.stats[proxy]['avg_response_time'] = (
(current_avg * (total - 1) + response_time) / total
)
def print_stats(self):
print("\n=== Statistiche del proxy ===")
for proxy, data in self.stats.items():
success_rate = (data['success'] / data['total'] * 100) if data['total'] > 0 else 0
print(f"\nProxy: {proxy}")
print(f" Totale richieste: {data['total']}")
print(f" Successi: {data['success']} ({success_rate:.1f}%)")
print(f" Rate limited: {data['rate_limited']}")
print(f" Errori: {data['errors']}")
print(f" Tempo medio di risposta: {data['avg_response_time']:.2f}s")
# Utilizzo
logger = RequestLogger()
start_time = time.time()
response = requests.get(url, proxies=proxies)
response_time = time.time() - start_time
logger.log_request(proxy, response.status_code, response_time)
logger.print_stats()
Cambio automatico della strategia
Se ricevi costantemente errori 429, rallenta automaticamente le richieste o aumenta il pool di proxy:
class AdaptiveRateLimiter:
def __init__(self, initial_delay=1.0):
self.delay = initial_delay
self.consecutive_429 = 0
def on_success(self):
"""Richiesta riuscita - si può accelerare un po'"""
self.consecutive_429 = 0
self.delay = max(0.5, self.delay * 0.95) # Riduci il ritardo del 5%
def on_rate_limit(self):
"""Ricevuto 429 - è necessario rallentare"""
self.consecutive_429 += 1
self.delay *= 1.5 # Aumenta il ritardo di 1.5 volte
if self.consecutive_429 > 5:
print("ATTENZIONE: Troppi errori 429. Controlla le impostazioni!")
def wait(self):
"""Attesa prima della prossima richiesta"""
time.sleep(self.delay)
return self.delay
# Utilizzo
limiter = AdaptiveRateLimiter(initial_delay=2.0)
for i in range(1000):
response = make_request(url)
if response.status_code == 200:
limiter.on_success()
elif response.status_code == 429:
limiter.on_rate_limit()
delay = limiter.wait()
print(f"Richiesta {i+1}, ritardo: {delay:.2f}s")
Gestione dei captcha e di altri blocchi
Alcune API mostrano un captcha invece di un blocco diretto quando si superano i limiti. Segni:
- Codice di stato 403 con corpo della risposta contenente "captcha" o "recaptcha"
- Redirect a una pagina con captcha (stato 302)
- Header speciali come
X-Captcha-Required: true
In tali casi è necessario:
- Interrompere immediatamente l'uso di questo IP
- Passare a un altro proxy dal pool
- Aumentare i ritardi tra le richieste
- Aggiungere maggiore varietà negli User-Agent e in altri header
Importante: Se ti trovi regolarmente di fronte a captcha utilizzando proxy residenziali, il problema è probabilmente nei modelli di comportamento (header identici, richieste troppo rapide), e non negli indirizzi IP.
Conclusione
Bypassare il rate limiting API utilizzando proxy non è solo una configurazione tecnica, ma una strategia complessiva che include la scelta corretta del tipo di proxy, la configurazione della rotazione IP, la gestione dei ritardi e il monitoraggio dei limiti. Le conclusioni chiave dell'articolo:
- I proxy da soli non risolvono il problema del rate limiting — è necessaria una strategia di rotazione corretta
- Per social media e API rigorose, utilizza proxy residenziali o mobili; per API pubbliche, i data center sono adatti
- Calcola la dimensione del pool di proxy in base ai limiti API e alla velocità di scraping desiderata
- Controlla sempre i limiti e adatta la tua strategia di conseguenza