Konkurensi yang tidak terkontrol mengirimkan permintaan secepat mungkin. Hal ini menyebabkan ERROR_TOO_MUCH_REQUESTS, saldo API terbuang, dan biaya yang tidak dapat diprediksi. Keranjang token memungkinkan Anda menetapkan kecepatan yang tepat – “tidak lebih dari 20 kiriman per detik” – sambil tetap memungkinkan semburan singkat ketika kapasitas tersedia.
Cara Kerja Token Bucket
[Bucket] capacity=20, refill=10/sec
Time 0: ████████████████████ 20 tokens available
→ 15 requests consume 15 tokens
Time 0: █████ 5 tokens remain
Time 1s: ███████████████ 15 tokens (5 + 10 refilled)
→ 15 requests consume 15 tokens
Time 1s: (empty) 0 tokens
Time 2s: ██████████ 10 tokens (0 + 10 refilled)
→ Request waits if bucket is empty
Properti utama:
- Kapasitas — ukuran burst maksimum
- Tingkat isi ulang – permintaan berkelanjutan per detik
- Permintaan menunggu ketika keranjang kosong (tidak ada penolakan, hanya pembatasan)
Implementasi Python
Bucket Token yang Aman untuk Thread
import time
import threading
class TokenBucket:
def __init__(self, capacity, refill_rate):
"""
Args:
capacity: Maximum tokens (burst size)
refill_rate: Tokens added per second
"""
self.capacity = capacity
self.refill_rate = refill_rate
self.tokens = capacity
self.last_refill = time.monotonic()
self.lock = threading.Lock()
def acquire(self, timeout=None):
"""Block until a token is available."""
deadline = time.monotonic() + timeout if timeout else float("inf")
while True:
with self.lock:
self._refill()
if self.tokens >= 1:
self.tokens -= 1
return True
# Check timeout
if time.monotonic() >= deadline:
return False
# Wait before retrying (avoid busy loop)
time.sleep(min(1.0 / self.refill_rate, 0.1))
def _refill(self):
now = time.monotonic()
elapsed = now - self.last_refill
new_tokens = elapsed * self.refill_rate
self.tokens = min(self.capacity, self.tokens + new_tokens)
self.last_refill = now
Pemecah CAPTCHA dengan Tarif Terbatas
import os
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
# Allow 10 submissions/sec with burst of 20
rate_limiter = TokenBucket(capacity=20, refill_rate=10)
def solve_captcha_rate_limited(sitekey, pageurl):
"""Solve with rate limiting on submission."""
# Wait for token before submitting
rate_limiter.acquire()
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
})
data = resp.json()
if data.get("status") != 1:
raise RuntimeError(data.get("request"))
captcha_id = data["request"]
# Polling doesn't need rate limiting (separate concern)
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": captcha_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") != "CAPCHA_NOT_READY":
raise RuntimeError(result.get("request"))
raise TimeoutError("Solve timeout")
# Run 100 tasks through rate limiter
tasks = [
{"sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"pageurl": f"https://example.com/p/{i}"}
for i in range(100)
]
with ThreadPoolExecutor(max_workers=30) as executor:
futures = {
executor.submit(
solve_captcha_rate_limited, t["sitekey"], t["pageurl"]
): t for t in tasks
}
for future in as_completed(futures):
task = futures[future]
try:
solution = future.result()
print(f"[OK] {task['pageurl']}")
except Exception as e:
print(f"[ERR] {task['pageurl']}: {e}")
Implementasi JavaScript
Keranjang Token Asinkron
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity;
this.refillRate = refillRate; // tokens per second
this.tokens = capacity;
this.lastRefill = Date.now();
this.waitQueue = [];
}
_refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
async acquire() {
this._refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return;
}
// Wait until a token is available
const waitTime = ((1 - this.tokens) / this.refillRate) * 1000;
await new Promise((resolve) => setTimeout(resolve, waitTime));
this._refill();
this.tokens -= 1;
}
}
Pemecah Batch dengan Tarif Terbatas
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const rateLimiter = new TokenBucket(20, 10); // 20 burst, 10/sec sustained
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveCaptchaLimited(sitekey, pageurl) {
// Wait for rate limit token
await rateLimiter.acquire();
const submitResp = await axios.post(
"https://ocr.captchaai.com/in.php",
null,
{
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
json: 1,
},
}
);
if (submitResp.data.status !== 1) {
throw new Error(submitResp.data.request);
}
const captchaId = submitResp.data.request;
for (let i = 0; i < 60; i++) {
await sleep(5000);
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
});
if (result.data.status === 1) return result.data.request;
if (result.data.request !== "CAPCHA_NOT_READY") {
throw new Error(result.data.request);
}
}
throw new Error("TIMEOUT");
}
// Solve 100 tasks — rate limiter ensures max 10 submissions/sec
async function batchSolve(tasks) {
const results = await Promise.allSettled(
tasks.map((t) => solveCaptchaLimited(t.sitekey, t.pageurl))
);
const solved = results.filter((r) => r.status === "fulfilled").length;
const failed = results.filter((r) => r.status === "rejected").length;
console.log(`Solved: ${solved}, Failed: ${failed}`);
}
Memilih Parameter
| Beban kerja | Kapasitas (meledak) | Kecepatan isi ulang (berkelanjutan) |
|---|---|---|
| Goresan ringan | 5 | 2/sec |
| Otomatisasi standar | 20 | 10/sec |
| Pipa bervolume tinggi | 50 | 30/sec |
| Throughput maksimum | 100 | 50/sec |
Aturan praktis:
- Atur kapasitas ke 2× kecepatan isi ulang (memungkinkan semburan 2 detik)
- Mulailah secara konservatif, tingkatkan sambil memantau tingkat kesalahan
- Hanya submit yang dibatasi tarifnya — jajak pendapat bersifat ringan dan dapat dibatasi sendiri
Token Bucket vs Algoritma Lainnya
| Algoritma | Perilaku | Terbaik untuk |
|---|---|---|
| ember token | Tingkat halus dengan tunjangan burst | Panggilan API CAPTCHA |
| Ember bocor | Tingkat keluaran tetap, tidak ada ledakan | Persyaratan tarif yang ketat |
| Memperbaiki jendela | Hitung per timing window, tepian meledak | Penghitung sederhana |
| Jendela geser | Hitung selama periode bergulir | Penegakan tarif yang akurat |
Token bucket adalah default terbaik – ini memungkinkan lonjakan alami (scraper menemukan 20 CAPTCHA sekaligus) sambil menerapkan tarif berkelanjutan.
Pemecahan masalah
| Masalah | Sebab | Perbaiki |
|---|---|---|
| Permintaan masih dibatasi | Pembatas tarif disetel lebih tinggi dari yang diizinkan API | Tingkat isi ulang yang lebih rendah agar sesuai dengan batas CaptchaAI |
| Latensi tinggi berdasarkan permintaan | Token habis, menunggu diisi ulang | Tingkatkan kapasitas untuk skenario burst |
| Memori bertambah | Tunggu antrian terakumulasi | Tetapkan ukuran antrian maksimum; menolak permintaan berlebih |
| Pembatas nilai tidak dibagikan ke seluruh proses | Hanya dalam memori | Gunakan keranjang token berbasis Redis untuk pembatasan tarif terdistribusi |
Pertanyaan Umum
Haruskah saya membatasi submit, jajak pendapat, atau keduanya?
submit dengan batas tarif saja. Permintaan pemungutan suara ringan dan dapat dilakukan sendiri melalui time.sleep(5). Pemungutan suara yang terlalu dibatasi akan meningkatkan penyelesaian latensi tanpa manfaat.
Bagaimana cara menangani ERROR_TOO_MUCH_REQUESTS meskipun ada batasan tarif?
Batas tarif Anda ditetapkan terlalu tinggi. Turunkan tingkat isi ulang. Periksa juga apakah beberapa proses berbagi kunci API yang sama – tingkat agregat di semua proses.
Bisakah saya menggunakan pembatas tarif per jenis CAPTCHA?
Ya – buat keranjang token terpisah untuk jenis CAPTCHA yang berbeda. Hal ini mencegah tugas reCAPTCHA v2 bervolume tinggi membuat submit Turnstile kelaparan.
Artikel Terkait
- Panggilan Captcha Api Pola circuit breaker
Langkah Selanjutnya
Membangun penyelesaian CAPTCHA dengan tingkat pengendalian —dapatkan kunci API CaptchaAI Andadan menerapkan tingkat permintaan yang berkelanjutan.
Panduan terkait:
- Nilai Membatasi Permintaan Anda Sendiri
- Batasan Nilai dan Pembatasan
- Menyelesaikan 10.000 Tugas per Jam