API Tutorials

CaptchaAI API Anahtar Rotasyonu: Çoklu Anahtar Yönetimi

Yüksek hacimli bir CAPTCHA çözüm hattını kesintisiz tutmanın yolu, isteği tek bir API anahtarına bağımlı bırakmamaktır. Tek anahtarla çalışan bir sistem üç noktadan durabilir:

  • Bakiye biter: hesabın bakiyesi tükendiği anda gönderimler ERROR_ZERO_BALANCE döner ve hat durur.
  • İstek sınırına takılır: planın eşzamanlı thread kapasitesi dolduğunda yeni istekler beklemeye girer.
  • Anahtar devre dışı kalır: yanlış anahtar, izin verilmeyen bir IP ya da hesabın askıya alınması hattı tamamen kesintiye uğratır.

Anahtar rotasyonu yükü birden çok anahtara dağıtır ve biri başarısız olunca otomatik olarak sıradakine geçer. Bu rehberde üç stratejiyi çalışan kodla kuracaksınız: anahtarları sırayla dolaşan round-robin, bakiyeye göre ağırlıklı rotasyon ve hata anında devreye giren yük devretme.

Türkiye'deki geliştiriciler CaptchaAI planlarını USD olarak ödediği için thread tabanlı öngörülebilir maliyet zaten bir avantajdır; rotasyon buna çalışma süresi güvencesi ekler.


Rotasyon ne zaman gerekli?

Tek anahtar, düşük hacimli ve tek müşterili işler için fazlasıyla yeterlidir. Rotasyonu şu durumlarda devreye almak mantıklıdır:

  • Günde binlerce çözüm gönderen, sürekli çalışan bir hattınız varsa.
  • Birden çok müşterinin ya da projenin işini ayrı hesaplara ayırıyorsanız.
  • Kesintisiz çalışma süresi bir hizmet taahhüdüyse ve tek anahtarın durması kabul edilemezse.

Bu koşulların hiçbiri yoksa tek bir plan ve tek anahtar sizi yormadan iş görür; rotasyonu ihtiyaç büyüdüğünde sonradan da ekleyebilirsiniz.


Hangi stratejiyi seçmeli?

Üç yaklaşımın üçü de aynı sorunu çözer ama farklı senaryolara oturur:

Strateji En uygun olduğu durum Ek maliyet
Round-robin Anahtarların bakiyesi ve kapasitesi benzer Neredeyse sıfır; birkaç satır kod
Bakiyeye göre ağırlıklı Hesapların bakiyesi dengesiz getbalance çağrıları ve durum takibi
Yük devretme Kesintisiz çalışma süresi önceliğiniz Yeniden deneme mantığı ve hata sınıflandırması

Çoğu üretim hattı bu üçünü birlikte kullanır: temel dağıtım için round-robin ya da ağırlıklı seçim, üstüne hata anında devreye giren yük devretme.


Round-robin: anahtarları sırayla kullanma

En basit strateji, anahtarları eşit sırayla dolaşmaktır. itertools.cycle her istekte sıradaki anahtarı verir; anahtarların bakiyesi benzerse bu kadarı yeterlidir. Bu yaklaşım iki durumda iyi çalışır:

  • Anahtarların bakiyesi ve thread kapasitesi birbirine yakın olduğunda.
  • Yükü eşit dağıtmanın, bakiye takibinden daha önemli olduğu basit hatlarda.

Tek dikkat noktası, birden fazla thread aynı anda anahtar istediğinde sayacın tutarlı ilerlemesidir; sonraki bölümlerde bunu bir lock ile güvenceye alıyoruz.

Python

import itertools
import requests

API_KEYS = [
    "KEY_ACCOUNT_1",
    "KEY_ACCOUNT_2",
    "KEY_ACCOUNT_3",
]

key_cycle = itertools.cycle(API_KEYS)


def get_next_key():
    return next(key_cycle)


def solve_captcha(sitekey, page_url):
    api_key = get_next_key()
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "json": "1",
    })
    data = resp.json()
    if data["status"] != 1:
        raise Exception(f"[{api_key[:8]}...] {data['request']}")

    print(f"Submitted with key {api_key[:8]}...")
    return data["request"], api_key


task_id, used_key = solve_captcha("6Le-SITEKEY", "https://example.com")

Bakiyeye göre ağırlıklı rotasyon

Round-robin, boşalmak üzere olan bir anahtara da diğerleri kadar trafik gönderir. Ağırlıklı rotasyon bakiyeyi getbalance ile okur ve trafiği bakiyeyle orantılı dağıtır. Örneğin birden çok e-ticaret müşterisi için ödeme akışı QA otomasyonu yürüten bir ajans, her müşterinin işini ayrı bir hesabın anahtarına bağlar; bakiyesi azalan hesap kendiliğinden geri plana düşer, dolu hesaplar yükü taşımayı sürdürür.

Aşağıdaki KeyRotator sınıfı her anahtar için bakiye, hata sayısı ve devre dışı durumunu tutar; get_key yalnızca bakiyesi olan anahtarlar arasından ağırlıklı rastgele seçim yapar.

import random
import requests
import threading

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


class KeyRotator:
    def __init__(self, keys):
        self.keys = {k: {"balance": 0, "failures": 0, "disabled": False} for k in keys}
        self._lock = threading.Lock()
        self.refresh_balances()

    def refresh_balances(self):
        for key in self.keys:
            try:
                resp = requests.get(RESULT_URL, params={
                    "key": key, "action": "getbalance", "json": "1"
                }, timeout=10).json()
                if resp["status"] == 1:
                    self.keys[key]["balance"] = float(resp["request"])
                    self.keys[key]["disabled"] = False
                else:
                    self.keys[key]["disabled"] = True
            except Exception:
                self.keys[key]["disabled"] = True

    def get_key(self):
        with self._lock:
            available = {
                k: v for k, v in self.keys.items()
                if not v["disabled"] and v["balance"] > 0.01
            }
            if not available:
                raise Exception("No API keys with balance available")

            # Weighted random by balance
            keys = list(available.keys())
            weights = [available[k]["balance"] for k in keys]
            return random.choices(keys, weights=weights, k=1)[0]

    def report_failure(self, key, error_code):
        with self._lock:
            self.keys[key]["failures"] += 1
            if error_code in ("ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST",
                              "ERROR_ZERO_BALANCE", "ERROR_IP_NOT_ALLOWED"):
                self.keys[key]["disabled"] = True
                print(f"[rotator] Disabled key {key[:8]}...: {error_code}")

    def report_success(self, key, cost=0.003):
        with self._lock:
            self.keys[key]["balance"] -= cost
            self.keys[key]["failures"] = 0


rotator = KeyRotator(["KEY_1", "KEY_2", "KEY_3"])

# Usage
api_key = rotator.get_key()
# ... solve captcha ...
rotator.report_success(api_key)

Hata durumunda yük devretme

Yük devretme, bir anahtar hata döndürdüğünde isteği sıradaki anahtarla yeniden dener. max_attempts denemenin üst sınırını belirler; bu sayıya ulaşıldığında istek gerçekten başarısız sayılır ve çağıran kod hatayı görür. report_failure yalnızca kalıcı hatalarda (yanlış anahtar, sıfır bakiye, izin verilmeyen IP) anahtarı devre dışı bırakır; geçici ağ hatalarında anahtar havuzda kalır, çünkü bir sonraki denemede yeniden çalışabilir.

Python

def solve_with_failover(sitekey, page_url, max_attempts=3):
    for attempt in range(max_attempts):
        api_key = rotator.get_key()
        try:
            resp = requests.post(SUBMIT_URL, data={
                "key": api_key,
                "method": "userrecaptcha",
                "googlekey": sitekey,
                "pageurl": page_url,
                "json": "1",
            }, timeout=15)
            data = resp.json()

            if data["status"] != 1:
                rotator.report_failure(api_key, data["request"])
                continue

            rotator.report_success(api_key)
            return data["request"], api_key

        except requests.RequestException:
            rotator.report_failure(api_key, "NETWORK_ERROR")
            continue

    raise Exception(f"All {max_attempts} keys failed")

JavaScript

const axios = require('axios');

class KeyRotator {
  constructor(keys) {
    this.keys = keys.map(k => ({ key: k, disabled: false, failures: 0 }));
    this.index = 0;
  }

  getKey() {
    const available = this.keys.filter(k => !k.disabled);
    if (available.length === 0) throw new Error('No API keys available');
    const entry = available[this.index % available.length];
    this.index++;
    return entry.key;
  }

  disable(key, reason) {
    const entry = this.keys.find(k => k.key === key);
    if (entry) {
      entry.disabled = true;
      console.log(`[rotator] Disabled ${key.substring(0, 8)}...: ${reason}`);
    }
  }
}

const rotator = new KeyRotator(['KEY_1', 'KEY_2', 'KEY_3']);

async function solveWithFailover(sitekey, pageurl, maxAttempts = 3) {
  for (let i = 0; i < maxAttempts; i++) {
    const apiKey = rotator.getKey();
    try {
      const resp = await axios.post('https://ocr.captchaai.com/in.php', null, {
        params: { key: apiKey, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: 1 }
      });
      if (resp.data.status !== 1) {
        rotator.disable(apiKey, resp.data.request);
        continue;
      }
      return { taskId: resp.data.request, apiKey };
    } catch (err) {
      rotator.disable(apiKey, 'NETWORK_ERROR');
    }
  }
  throw new Error('All keys failed');
}

Anahtarları ortam değişkenlerinden yükleme

API anahtarlarını asla kaynak koda gömmeyin. Virgülle ayrılmış tek bir ortam değişkeninde tutun; böylece anahtarlar sürüm kontrolüne sızmaz ve anahtar eklemek için kodu değil yalnızca ortam değişkenini güncellersiniz. Üretimde bu değişkeni bir gizli bilgi yöneticisiyle (secret manager) besleyebilirsiniz.

import os

API_KEYS = os.environ["CAPTCHAAI_KEYS"].split(",")
# Set: CAPTCHAAI_KEYS=key1,key2,key3
rotator = KeyRotator(API_KEYS)
const API_KEYS = process.env.CAPTCHAAI_KEYS.split(',');
const rotator = new KeyRotator(API_KEYS);

Bakiyeleri düzenli aralıklarla yenileme

Saatlerce çalışan işlemlerde arka planda çalışan bir daemon thread'i bakiyeleri düzenli aralıklarla yeniler; boşalan anahtarları devre dışı bırakır, yeniden doldurulanları geri açar. Aşağıdaki örnek beş dakikada bir yeniler ve her anahtarın güncel durumunu yazdırır; aralığı hattınızın hacmine göre ayarlayın.

import threading

def periodic_refresh(rotator, interval=300):
    def refresh():
        while True:
            rotator.refresh_balances()
            for key, info in rotator.keys.items():
                print(f"  {key[:8]}...: ${info['balance']:.2f} "
                      f"{'(disabled)' if info['disabled'] else '(active)'}")
            threading.Event().wait(interval)

    t = threading.Thread(target=refresh, daemon=True)
    t.start()

periodic_refresh(rotator, interval=300)  # every 5 minutes

Üretim için notlar

Rotasyonu canlıya alırken birkaç ayrıntı hattın kararlılığını belirler:

  • Her gönderimi anahtarla birlikte loglayın. Hangi anahtarın hangi hatayı döndürdüğünü görmeden sorun gidermek zordur; örneklerdeki key[:8]... kısaltması anahtarı sızdırmadan iz bırakır.
  • Thread kapasitesini planla eşleştirin. Rotasyon anahtar sayısını artırır, ama her hesabın eşzamanlı thread kapasitesi kendi planına bağlıdır; örneğin ADVANCE ($90/ay, 50 thread) tek başına yüksek eşzamanlılık taşır, rotasyon ise farklı hesaplar arasında yedeklilik ekler.
  • Kalıcı ve geçici hatayı ayırın. Ağ zaman aşımı gibi geçici hatalarda anahtarı havuzda tutun; yalnızca ERROR_WRONG_USER_KEY veya ERROR_ZERO_BALANCE gibi kalıcı hatalarda devre dışı bırakın, yoksa sağlıklı anahtarları gereksiz yere kaybedersiniz.

Sorun giderme

Sorun Sebep Düzeltme
Tüm anahtarlar devre dışı Tüm hesaplarda bakiye sıfır Hesaplara bakiye yükleyin; ERROR_ZERO_BALANCE dönüşünü kontrol edin
Hep aynı anahtar kullanılıyor Round-robin indeksi ilerlemiyor Lock ile thread güvenliğini doğrulayın
Anahtar yanlışlıkla devre dışı bırakıldı Geçici hata kalıcı sanılıyor Yalnızca ERROR_WRONG_USER_KEY, ERROR_ZERO_BALANCE, ERROR_IP_NOT_ALLOWED hatalarında devre dışı bırakın

SSS

Round-robin mu, ağırlıklı rotasyon mu kullanmalıyım?

Bakiyeler benzerse round-robin yeterlidir ve en basit kurulumdur. Bakiyeler dengesizse ağırlıklı rotasyon trafiği daha verimli dağıtır.

Anahtar rotasyonu çözüm başarı oranını artırır mı?

Hayır. Rotasyon başarı oranını değil çalışma süresini hedefler: bir anahtar durduğunda hat çalışmaya devam eder, ama isteklerin çözüm oranını değiştirmez.

Bir anahtarın devre dışı kaldığını nasıl anlarım?

Bunu getbalance çağrısı ya da gönderim yanıtındaki hata kodları gösterir. ERROR_ZERO_BALANCE, ERROR_WRONG_USER_KEY ve ERROR_IP_NOT_ALLOWED gibi kalıcı hatalar, anahtarı devre dışı bırakma sinyalidir.

Rotasyon için birden fazla plan mı gerekir?

Gerekmez. Tek bir plan daha fazla eşzamanlı thread verir; örneğin ADVANCE ($90/ay, 50 thread) tek anahtarla yüksek hacim taşır. Rotasyon ise farklı hesapların anahtarları arasında yedeklilik sağlar; ikisi birbirini tamamlar.


Özet: rotasyonu kurma adımları

Bir hattı tek anahtardan çok anahtarlı, kendini toparlayan bir yapıya taşımak dört adımdır:

  1. Anahtarları virgülle ayrılmış bir ortam değişkeninden yükleyin.
  2. Bakiyeler benzerse round-robin, dengesizse bakiyeye göre ağırlıklı seçimi kurun.
  3. Gönderimleri, kalıcı hatalarda anahtarı devre dışı bırakan bir yük devretme döngüsüne sarın.
  4. Bakiyeleri arka planda düzenli aralıklarla yenileyin ve her anahtarın durumunu loglayın.

CAPTCHA çözümünüzü çoklu anahtarla ölçeklendirin

API anahtarınızı buradan alın: captchaai.com.


İlgili kılavuzlar

Bu makale için yorumlar devre dışı bırakılmıştır.