A batch of 10,000 reCAPTCHA v2 tasks costs differently than 10,000 image CAPTCHAs. Without cost tracking, a misconfigured loop can drain your balance before you notice. This guide shows how to estimate costs before a batch starts, track spending during execution, and set alerts when budgets are exceeded.
Cost Per CAPTCHA Type
Pricing varies by CAPTCHA complexity. Use these approximate rates for planning (check CaptchaAI pricing for current rates):
| CAPTCHA Type | Approximate Cost | Notes |
|---|---|---|
| Image/Text OCR | ~$0.001–0.003 | Cheapest; high volume |
| reCAPTCHA v2 | ~$0.002–0.004 | Most common |
| reCAPTCHA v3 | ~$0.003–0.005 | Score-based |
| reCAPTCHA Enterprise | ~$0.005–0.008 | Higher complexity |
| hCaptcha | ~$0.003–0.005 | Similar to reCAPTCHA v2 |
| Cloudflare Turnstile | ~$0.003–0.005 | Token-based |
| GeeTest v3 | ~$0.004–0.006 | Multi-step challenge |
Python: Cost Estimator and Budget Guard
import requests
import time
from dataclasses import dataclass
API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
# Cost per 1,000 tasks (adjust to current pricing)
COST_PER_1K = {
"userrecaptcha": 3.00, # reCAPTCHA v2
"userrecaptcha_v3": 4.00, # reCAPTCHA v3
"turnstile": 3.50, # Cloudflare Turnstile
"hcaptcha": 3.50, # hCaptcha
"base64": 1.50, # Image/OCR
"geetest": 5.00, # GeeTest
}
@dataclass
class CostEstimate:
task_count: int
method: str
cost_per_task: float
total_estimated: float
retry_buffer: float # Extra for retries
total_with_buffer: float
def __str__(self):
return (
f"Estimate: {self.task_count} × ${self.cost_per_task:.4f} "
f"= ${self.total_estimated:.2f} "
f"(+{self.retry_buffer:.0%} retry buffer = ${self.total_with_buffer:.2f})"
)
def estimate_cost(task_count, method="userrecaptcha", retry_rate=0.15):
"""Estimate total cost for a batch before starting."""
per_1k = COST_PER_1K.get(method, 3.00)
per_task = per_1k / 1000
base_cost = task_count * per_task
buffer_cost = base_cost * (1 + retry_rate)
return CostEstimate(
task_count=task_count,
method=method,
cost_per_task=per_task,
total_estimated=base_cost,
retry_buffer=retry_rate,
total_with_buffer=buffer_cost,
)
def get_balance():
"""Check current CaptchaAI balance."""
resp = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "getbalance", "json": 1,
}, timeout=10).json()
return float(resp.get("request", 0))
class BudgetGuard:
"""Track spending and enforce budget limits during batch processing."""
def __init__(self, budget_limit, method="userrecaptcha", alert_threshold=0.8):
self.budget_limit = budget_limit
self.alert_threshold = alert_threshold
self.per_task = COST_PER_1K.get(method, 3.00) / 1000
self.tasks_submitted = 0
self.tasks_solved = 0
self.tasks_failed = 0
self.estimated_spent = 0.0
self.alert_sent = False
def can_submit(self):
"""Check if budget allows another task."""
projected = self.estimated_spent + self.per_task
if projected > self.budget_limit:
print(f"BUDGET EXCEEDED: ${self.estimated_spent:.2f} / ${self.budget_limit:.2f}")
return False
return True
def record_submit(self):
"""Record a submitted task."""
self.tasks_submitted += 1
self.estimated_spent += self.per_task
# Alert at threshold
if not self.alert_sent and self.estimated_spent >= self.budget_limit * self.alert_threshold:
pct = (self.estimated_spent / self.budget_limit) * 100
print(f"BUDGET ALERT: {pct:.0f}% used (${self.estimated_spent:.2f} / ${self.budget_limit:.2f})")
self.alert_sent = True
def record_result(self, solved):
"""Record a task result."""
if solved:
self.tasks_solved += 1
else:
self.tasks_failed += 1
def summary(self):
"""Return a spending summary."""
return {
"submitted": self.tasks_submitted,
"solved": self.tasks_solved,
"failed": self.tasks_failed,
"estimated_spent": round(self.estimated_spent, 4),
"budget_limit": self.budget_limit,
"budget_remaining": round(self.budget_limit - self.estimated_spent, 4),
"cost_per_solved": round(
self.estimated_spent / self.tasks_solved, 4
) if self.tasks_solved else 0,
}
def solve_with_budget(tasks, budget_limit, method="userrecaptcha"):
"""Process tasks with budget enforcement."""
# Pre-flight checks
estimate = estimate_cost(len(tasks), method)
print(estimate)
balance = get_balance()
print(f"Current balance: ${balance:.2f}")
if estimate.total_with_buffer > balance:
print(f"WARNING: Estimated cost ${estimate.total_with_buffer:.2f} exceeds balance ${balance:.2f}")
return []
effective_budget = min(budget_limit, balance)
guard = BudgetGuard(effective_budget, method)
results = []
for i, task in enumerate(tasks):
if not guard.can_submit():
print(f"Budget limit reached after {i} tasks")
break
guard.record_submit()
try:
params = {"key": API_KEY, "method": method, "json": 1}
if method == "userrecaptcha":
params["googlekey"] = task["sitekey"]
params["pageurl"] = task["pageurl"]
elif method == "turnstile":
params["sitekey"] = task["sitekey"]
params["pageurl"] = task["pageurl"]
resp = requests.post(SUBMIT_URL, data=params, timeout=30).json()
if resp.get("status") != 1:
guard.record_result(False)
results.append({"index": i, "status": "failed", "error": resp.get("request")})
continue
task_id = resp["request"]
solved = False
for _ in range(60):
time.sleep(5)
poll = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "get",
"id": task_id, "json": 1,
}, timeout=15).json()
if poll.get("request") == "CAPCHA_NOT_READY":
continue
if poll.get("status") == 1:
guard.record_result(True)
results.append({"index": i, "status": "solved", "token": poll["request"]})
solved = True
break
guard.record_result(False)
results.append({"index": i, "status": "failed", "error": poll.get("request")})
break
if not solved and results[-1]["index"] != i:
guard.record_result(False)
results.append({"index": i, "status": "failed", "error": "timeout"})
except Exception as e:
guard.record_result(False)
results.append({"index": i, "status": "failed", "error": str(e)})
print(f"\nSpending summary: {guard.summary()}")
return results
# Usage
tasks = [
{"sitekey": "SITE_KEY", "pageurl": f"https://example.com/page{i}"}
for i in range(100)
]
# Estimate before running
est = estimate_cost(100, "userrecaptcha")
print(est)
# Estimate: 100 × $0.0030 = $0.30 (+15% retry buffer = $0.35)
# Run with $0.50 budget limit
results = solve_with_budget(tasks, budget_limit=0.50, method="userrecaptcha")
JavaScript: Cost Tracker
const API_KEY = "YOUR_API_KEY";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
const COST_PER_1K = {
userrecaptcha: 3.0,
turnstile: 3.5,
hcaptcha: 3.5,
base64: 1.5,
};
function estimateCost(taskCount, method = "userrecaptcha", retryRate = 0.15) {
const perTask = (COST_PER_1K[method] || 3.0) / 1000;
const base = taskCount * perTask;
return {
taskCount,
perTask: perTask.toFixed(4),
baseCost: base.toFixed(2),
withRetries: (base * (1 + retryRate)).toFixed(2),
};
}
class BudgetTracker {
constructor(limit, method = "userrecaptcha", alertPct = 0.8) {
this.limit = limit;
this.perTask = (COST_PER_1K[method] || 3.0) / 1000;
this.spent = 0;
this.alertPct = alertPct;
this.alertFired = false;
this.solved = 0;
this.failed = 0;
}
canSubmit() {
return this.spent + this.perTask <= this.limit;
}
recordSubmit() {
this.spent += this.perTask;
if (!this.alertFired && this.spent >= this.limit * this.alertPct) {
console.warn(`BUDGET ALERT: ${((this.spent / this.limit) * 100).toFixed(0)}% used`);
this.alertFired = true;
}
}
recordResult(success) {
if (success) this.solved++;
else this.failed++;
}
summary() {
return {
spent: `$${this.spent.toFixed(4)}`,
remaining: `$${(this.limit - this.spent).toFixed(4)}`,
solved: this.solved,
failed: this.failed,
costPerSolved: this.solved ? `$${(this.spent / this.solved).toFixed(4)}` : "N/A",
};
}
}
async function checkBalance() {
const url = `${RESULT_URL}?key=${API_KEY}&action=getbalance&json=1`;
const resp = await (await fetch(url)).json();
return parseFloat(resp.request);
}
// Usage
const estimate = estimateCost(500, "userrecaptcha");
console.log("Estimate:", estimate);
const tracker = new BudgetTracker(2.0, "userrecaptcha");
// Use tracker.canSubmit() / tracker.recordSubmit() / tracker.recordResult() during batch processing
Budget Planning Table
| Batch Size | reCAPTCHA v2 | Image/OCR | Turnstile | With 15% Retry Buffer |
|---|---|---|---|---|
| 100 | $0.30 | $0.15 | $0.35 | $0.35–0.40 |
| 1,000 | $3.00 | $1.50 | $3.50 | $3.45–4.03 |
| 10,000 | $30.00 | $15.00 | $35.00 | $34.50–40.25 |
| 100,000 | $300.00 | $150.00 | $350.00 | $345–402 |
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Cost higher than estimated | Retry rate exceeds buffer | Increase retry buffer to 25–30% for challenging CAPTCHAs |
| Balance drained unexpectedly | No budget guard in place | Add BudgetGuard with hard limit before production runs |
| Cost per solved task too high | Many failures not counted | Track both submitted and solved counts — optimize failure rate first |
| Estimate doesn't match actual | Using wrong cost-per-1K rate | Check current CaptchaAI pricing page and update COST_PER_1K |
| Alert fires too late | Threshold set too high | Lower alert_threshold to 0.5 (50%) for tighter budgets |
FAQ
How accurate are cost estimates?
Within 10–20% for typical batches. The main variable is retry rate — challenging sites or bad proxies increase retries. Run a small test batch (50–100 tasks) to measure your actual retry rate before committing to large batches.
Should I check balance before every task?
No — balance API calls add latency. Track spending locally with a BudgetGuard and check actual balance every 100–500 tasks or at the start/end of each batch.
How do I set up spending alerts for a team?
Run a scheduled balance check (every hour) that compares current balance to previous. If the delta exceeds a threshold (e.g., $5/hour), trigger a notification via Slack webhook, email, or PagerDuty.
Next Steps
Keep your CAPTCHA solving costs predictable — get your CaptchaAI API key and implement budget tracking from the start.
Related guides:
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.