Sebagian besar implementasi pembatasan laju (rate limit) API dapat dengan mudah dilewati dengan rotasi proxy naif — tetapi rotasi yang sama justru memicu heuristik anti-penyalahgunaan dalam hitungan menit. Tantangan sebenarnya bukan hanya tetap berada di bawah batas, tetapi melakukannya tanpa terlihat seperti bot. Hal ini memerlukan pemahaman tentang dua cakupan pembatasan laju yang berbeda (per-IP dan per-key), menerapkan backoff dengan jitter, merandomisasi rotasi pool, dan membentuk waktu permintaan agar meniru lalu lintas organik. Pola token bucket yang dilapiskan di atas pool proxy memberikan fondasi yang kokoh.
Per-IP vs Per-Key: Dua Sumbu Pembatasan Laju
Pembatasan laju beroperasi pada setidaknya dua dimensi independen: IP sumber dan kunci API (atau token). Satu kunci mungkin mengizinkan 5.000 permintaan per jam (batas terautentikasi GitHub), tetapi kunci yang sama dari satu IP mungkin dibatasi pada batas lonjakan yang lebih rendah. Permintaan tidak terautentikasi bahkan lebih dibatasi — biasanya 60 permintaan per jam per IP. Mengabaikan dimensi IP adalah cara tercepat untuk memicu respons 429 Too Many Requests (RFC 6585). Pool proxy harus mendistribusikan permintaan ke beberapa IP untuk menghindari memenuhi satu asal mana pun, tetapi setiap IP masih berbagi kunci yang sama. Jika batas global kunci adalah 5.000/jam dan Anda memiliki 50 proxy, setiap proxy hanya dapat melakukan 100 permintaan per jam sebelum penghitung tingkat kunci terpicu. Petakan kedua batas tersebut sebelum menulis satu baris kode pun.
Backoff dengan Jitter: Perbedaan antara Sopan dan Terprediksi
Backoff eksponensial tanpa jitter adalah sidik jari. Server melihat permintaan tiba pada interval yang berlipat ganda sempurna dan menandainya sebagai otomatis. Perbaikannya adalah jitter penuh: sleep(random.uniform(0, min(cap, base * 2 ** attempt))). Ini menyebarkan percobaan ulang di seluruh jendela waktu, membuat pola tidak dapat dibedakan dari lonjakan pengguna nyata. Dokumentasi AWS sendiri tentang backoff eksponensial merekomendasikan jitter untuk alasan ini. Cuplikan Python berikut mengimplementasikan token bucket yang mengisi ulang pada laju tetap, tidur dengan jitter saat kosong, dan merotasi proxy pada setiap akuisisi yang berhasil:
import time
import random
from collections import deque
class TokenBucketProxyPool:
def __init__(self, proxies, rate, burst):
self.proxies = deque(proxies)
self.rate = rate # tokens per second
self.burst = burst
self.tokens = burst
self.last_refill = time.monotonic()
def refill(self):
now = time.monotonic()
elapsed = now - self.last_refill
self.tokens = min(self.burst, self.tokens + elapsed * self.rate)
self.last_refill = now
def acquire(self):
while True:
self.refill()
if self.tokens >= 1:
self.tokens -= 1
self.proxies.rotate(1) # simple rotation, but see section below
return self.proxies[0]
else:
sleep_time = (1 - self.tokens) / self.rate
jitter = random.uniform(0, sleep_time * 0.5)
time.sleep(sleep_time + jitter)
# Usage: pool = TokenBucketProxyPool(proxies, rate=2.0, burst=10)
# proxy = pool.acquire() # blocks until token available
Rotasi Pool Proxy: Acak, Jangan Round-Robin
Round-robin berurutan melalui daftar proxy dapat dideteksi. Server yang melihat permintaan dari 1.2.3.4, lalu 5.6.7.8, lalu 9.10.11.12 secara bersamaan akan mengkorelasikan pola di seluruh IP. Sebagai gantinya, pilih proxy secara acak dengan pengembalian, dan tambahkan cooldown per-proxy. Direktori proxy publik melaporkan tingkat kegagalan 60–80% — proxy yang mengembalikan 429 atau kesalahan koneksi harus diturunkan ke antrean mati setidaknya selama 60 detik. Implementasikan pemilihan acak berbobot di mana proxy yang baru saja berhasil lebih mungkin dipilih. Token bucket di atas menggunakan rotate(1) sederhana untuk kejelasan; dalam produksi, ganti dengan random.choice() dan lacak jumlah kegagalan per-proxy.
Pembentukan Permintaan: Tiru Rata-rata Tanpa Melewati Batas
Bahkan dengan token bucket dan pool proxy yang sempurna, laju permintaan konstan tepat 2,0 per detik tidaklah alami. Pengguna nyata berhenti sejenak, mengelompokkan, dan memvariasikan interval antar-permintaan. Tambahkan penundaan acak kecil (misalnya, time.sleep(random.uniform(0.1, 0.5))) sebelum setiap panggilan acquire(), dan variasikan laju token bucket sebesar ±10% setiap beberapa menit. Namun, jangan menambahkan penundaan yang melebihi jendela batas laju yang didokumentasikan API — itu akan mengalahkan tujuannya. Tujuannya adalah untuk tetap berada dalam batas sementara tampak seperti sekelompok klien yang sah. Pembentukan berlebihan (misalnya, menyisipkan penundaan pengetikan seperti manusia) adalah upaya yang sia-sia; API peduli pada frekuensi permintaan, bukan waktu antar-tekanan tombol.
Mitigasi Etis: Ketahui Di Mana Batasnya
Setiap teknik yang dijelaskan di sini adalah optimasi yang sah — sampai melanggar Ketentuan Layanan (ToS) API. Sebagian besar ToS secara eksplisit melarang "menghindari batas laju" atau "menggunakan cara otomatis untuk mengakses layanan." Membaca ToS bukanlah opsional. Jika API mengizinkan 100 permintaan per menit per kunci, menggunakan 50 proxy masing-masing melakukan 2 permintaan per menit adalah sesuai. Menggunakan 100 proxy masing-masing melakukan 100 permintaan per menit tidak — itu adalah penyalahgunaan, tidak peduli seberapa cerdas jitter Anda. Perbedaannya adalah niat dan volume. Token bucket yang dilapiskan di atas pool proxy adalah alat; alat yang sama yang secara etis mengikis direktori publik juga dapat digunakan untuk DDoS API kecil. Dokumentasikan target batas laju Anda, audit log Anda untuk 429, dan jangan pernah melebihi batas per-kunci yang didokumentasikan. Jika Anda membutuhkan lebih banyak throughput, mintalah batas yang lebih tinggi atau bayar untuk paket khusus.