Tutoriales

Califique la limitación de sus propias solicitudes de resolución de CAPTCHA

CaptchaAI maneja alta concurrencia, pero es posible que desees limitarlo tú mismo. Motivos: control de costos, evitar picos involuntarios debido a errores, compartir recursos de manera justa entre equipos o igualar los límites de tasa establecidos por los sitios de destino. Esta guía implementa tres patrones de limitación de velocidad del lado del cliente paraCaptchaAI.

Por qué limitar la tarifa del lado del cliente

Escenario Sin limite Con limite
El error causa un bucle de resolución infinito Quema a través del equilibrio Se detiene en el límite configurado
Varios equipos comparten una clave API Gasto descoordinado Asignación justa por equipo
Prohibiciones de sitios objetivo en > 100 req/min Las cuentas se bloquean Se mantiene por debajo del umbral
El presupuesto es de $50/month Podría superar en una tarde Se aplica un límite máximo

Patrón 1: Cubo de fichas

Un depósito de tokens permite ráfagas al tiempo que aplica una tasa promedio. Los tokens se reponen a una tasa fija; cada solicitud consume un token.

Cubo de tokens de Python

# token_bucket_solver.py
import os
import time
import threading
import requests

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

class TokenBucket:
    """Token bucket rate limiter."""

    def __init__(self, rate, capacity):
        """
        rate: tokens added per second
        capacity: max tokens (burst size)
        """
        self.rate = rate
        self.capacity = capacity
        self.tokens = capacity
        self.last_refill = time.monotonic()
        self.lock = threading.Lock()

    def acquire(self, timeout=30):
        """Wait for a token. Returns True if acquired, False on timeout."""
        deadline = time.monotonic() + timeout
        while True:
            with self.lock:
                self._refill()
                if self.tokens >= 1:
                    self.tokens -= 1
                    return True

            if time.monotonic() >= deadline:
                return False
            time.sleep(0.1)

    def _refill(self):
        now = time.monotonic()
        elapsed = now - self.last_refill
        self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
        self.last_refill = now

# Allow 10 solves/minute with burst of 5
limiter = TokenBucket(rate=10/60, capacity=5)

def solve_rate_limited(sitekey, pageurl):
    """Solve with rate limiting."""
    if not limiter.acquire(timeout=60):
        raise Exception("Rate limit: could not acquire token within 60s")

    session = requests.Session()
    resp = session.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": "1",
    })
    result = resp.json()

    if result.get("status") != 1:
        raise Exception(f"Submit failed: {result.get('request')}")

    task_id = result["request"]
    time.sleep(15)

    for _ in range(25):
        poll = session.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get",
            "id": task_id, "json": "1",
        })
        poll_result = poll.json()
        if poll_result.get("status") == 1:
            return poll_result["request"]
        if poll_result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Error: {poll_result.get('request')}")
        time.sleep(5)

    raise Exception("Timeout")

Patrón 2: Mostrador de ventana corrediza

Realiza un seguimiento del número de solicitudes dentro de un período de tiempo fijo. Más simple que el depósito de fichas pero sin control de ráfagas.

Ventana deslizante de JavaScript

// sliding_window_solver.js
const axios = require('axios');

const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';

class SlidingWindowLimiter {
  constructor(maxRequests, windowMs) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
    this.timestamps = [];
  }

  async acquire(timeoutMs = 60000) {
    const deadline = Date.now() + timeoutMs;

    while (Date.now() < deadline) {
      // Remove expired timestamps
      const cutoff = Date.now() - this.windowMs;
      this.timestamps = this.timestamps.filter(t => t > cutoff);

      if (this.timestamps.length < this.maxRequests) {
        this.timestamps.push(Date.now());
        return true;
      }

      // Wait until the oldest request exits the window
      const waitMs = Math.min(
        this.timestamps[0] + this.windowMs - Date.now() + 10,
        deadline - Date.now()
      );
      if (waitMs > 0) await new Promise(r => setTimeout(r, waitMs));
    }
    return false;
  }
}

// Allow 20 solves per 5 minutes
const limiter = new SlidingWindowLimiter(20, 5 * 60 * 1000);

async function solveRateLimited(sitekey, pageurl) {
  const acquired = await limiter.acquire(60000);
  if (!acquired) throw new Error('Rate limit exceeded');

  const submit = await axios.get('https://ocr.captchaai.com/in.php', {
    params: {
      key: API_KEY, method: 'userrecaptcha',
      googlekey: sitekey, pageurl, json: '1',
    },
  });

  if (submit.data.status !== 1) throw new Error(submit.data.request);
  await new Promise(r => setTimeout(r, 15000));

  for (let i = 0; i < 25; i++) {
    const poll = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
    });
    if (poll.data.status === 1) return poll.data.request;
    if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
    await new Promise(r => setTimeout(r, 5000));
  }
  throw new Error('Timeout');
}

Patrón 3: Limitador basado en el presupuesto

Establezca un presupuesto de costos diario y deténgalo cuando lo alcance:

# budget_limiter.py
import os
import time
from datetime import date

class BudgetLimiter:
    """Limit daily CAPTCHA spending."""

    def __init__(self, daily_budget, cost_per_solve=0.003):
        self.daily_budget = daily_budget
        self.cost_per_solve = cost_per_solve
        self.daily_spend = 0.0
        self.current_date = date.today()

    def can_solve(self):
        """Check if budget allows another solve."""
        if date.today() != self.current_date:
            self.daily_spend = 0.0
            self.current_date = date.today()

        return self.daily_spend + self.cost_per_solve <= self.daily_budget

    def record_solve(self):
        """Record a successful solve against the budget."""
        self.daily_spend += self.cost_per_solve

    @property
    def remaining_budget(self):
        return max(0, self.daily_budget - self.daily_spend)

    @property
    def remaining_solves(self):
        return int(self.remaining_budget / self.cost_per_solve)

# $5/day budget
budget = BudgetLimiter(daily_budget=5.00, cost_per_solve=0.003)

def solve_with_budget(sitekey, pageurl):
    if not budget.can_solve():
        raise Exception(
            f"Daily budget exhausted. Remaining: ${budget.remaining_budget:.2f}"
        )

    # ... solve logic ...
    token = "..."  # actual solve
    budget.record_solve()
    return token

Elegir un patrón

Patrón Mejor para Complejidad
Cubo de fichas Control de velocidad suave con margen de ráfaga Medio
ventana corredera Recuento de solicitudes simples por ventana de tiempo Bajo
Limitador de presupuesto Control de costes por día/week/month Bajo
Combinado (tarifa + presupuesto) Sistemas de producción Medio

Solución de problemas

Problema causa Solución
Todas las solicitudes en cola, ninguna en ejecución Tarifa demasiado baja para la demanda Aumentar la velocidad o el tamaño de la ventana
El limitador de presupuesto se reinicia al mediodía Cambio o reinicio del reloj del sistema Mantener el gasto diario en un archivo o base de datos
El cubo de fichas se drena en explosión Capacidad demasiado pequeña para el flujo de trabajo Aumentar parámetro de capacidad
El limitador de velocidad bloquea las solicitudes de sondeo Limitador aplicado también a las consultas. Limite solo las solicitudes de envío, no las consultas

Preguntas frecuentes

¿Debo limitar la calificación de la solicitud de envío o de la solicitud de consulta?

Límite de tarifa solo para la solicitud enviar (in.php). El sondeo (res.php) debería ejecutarse libremente ya que no crea nuevas tareas ni cuesta dinero.

¿Cómo comparto los límites de tarifas entre varios trabajadores?

Utilice la limitación de velocidad respaldada por Redis. Bibliotecas como redis-rate-limiter (Python) o rate-limiter-flexible (Node.js) admiten la limitación de velocidad distribuida con operaciones atómicas.

¿Puede CaptchaAI decirme mi tasa de uso actual?

Utilice el punto final del saldo para realizar un seguimiento de los gastos. Para un uso detallado, implemente sus propios contadores como se muestra en esta guía.

Artículos relacionados

Próximos pasos

Controle su tasa de resolución de CAPTCHA y su presupuesto.obtenga su clave API CaptchaAI.

Guías relacionadas:

Los comentarios están deshabilitados para este artículo.