Comparisons

CaptchaAI Enterprise Plan ROI Analysis for High-Volume Operations

CaptchaAI's ENTERPRISE plan ($300/month, 200 threads) is the right size when your workload keeps roughly 150 to 200 CAPTCHAs in flight at once, which on CaptchaAI's own sizing math is about 7.5M to 10M reCAPTCHA v2 solves a month. Below are the per-thread price against the neighbouring tiers, the 10M-solve bill at per-solve vendors, a capacity and payback worksheet in Python and JavaScript, and a utilisation monitor for after you buy. The constraint that decides everything: you are buying concurrency, not volume, so ENTERPRISE only beats the cheaper tiers if your client can keep more than 150 tasks running.

A naming note: ENTERPRISE here is a CaptchaAI thread tier, not Google's reCAPTCHA Enterprise. Every CaptchaAI plan solves reCAPTCHA v2 Enterprise and v3 Enterprise on its ordinary threads with no type surcharge. If you are costing those challenges rather than this plan, read the reCAPTCHA Enterprise solving cost comparison.

What the $300 buys

CaptchaAI bills per concurrent thread. A thread is one CAPTCHA in flight: when a solve finishes, the thread takes the next task. Every plan includes unlimited solves per thread for the billing month, with no per-CAPTCHA fees, no daily caps and no surcharge by CAPTCHA type.

The plan cards on captchaai.com/pricing list three things for every tier: the monthly price, the thread count and unlimited solving. They do not list a separate support level or an SLA for ENTERPRISE. If procurement needs contractual terms, confirm them with CaptchaAI before you buy.

Prepaying changes the price. At the time of writing, the SEMI-ANNUAL option is 5% off and ANNUAL is 10% off, and promotions sometimes change those percentages. The annual plan guide covers when prepaying makes sense.

Price per thread: ENTERPRISE against the tiers below it

Divide each list price by its thread count and the ladder flattens out at ENTERPRISE:

Plan Monthly price Threads Price per thread Suggested reCAPTCHA v2 volume per month Effective per 1K solves
ADVANCE $90 50 $1.80 250K – 1M from $0.09
PREMIUM $170 100 $1.70 1M – 5M from $0.034
CORPORATE $240 150 $1.60 5M – 7.5M from $0.032
ENTERPRISE $300 200 $1.50 7.5M – 10M from $0.030
VIP-1 $1,500 1,000 $1.50 10M – 50M from $0.030

Two things stand out. First, ENTERPRISE is where the per-thread price stops falling. VIP-1 ($1,500/month, 1,000 threads) costs the same per thread, so VIP buys more threads in one plan, not cheaper ones (the VIP plans guide covers the rest). Second, the step up is cheap at the margin. Moving from CORPORATE ($240) to ENTERPRISE ($300) adds 50 threads for $60 more, which works out to $1.20 for each extra thread.

The volume bands come from a planning figure of 50,000 reCAPTCHA v2 solves per thread per month: 150 threads × 50K = 7.5M and 200 × 50K = 10M. That figure sits between two reference points. If every solve took the full published ceiling of under 60 seconds, a thread that never idles would clear 43,200 a month. At a realistic average of around 30 seconds, it would clear roughly 80,000. Use the band as a starting point and your own measured solve times as the answer.

Who actually needs 150 to 200 threads

Threads in use equal the rate at which you start solves multiplied by how long each one holds a thread. Monthly volume only matters through that rate. Two cases show why:

  • 8M reCAPTCHA v2 a month, spread over 24 hours. That is about 3.1 solves per second. At a 30-second average solve, about 93 threads are busy at once. That fits PREMIUM's 100 threads with almost no room for bursts, so CORPORATE is the safer fit. If your solves run close to the 60-second ceiling, you need about 186, which is ENTERPRISE.
  • The same 8M, compressed into 16 working hours. The rate rises to about 4.6 per second: roughly 139 threads at a 30-second average and 278 near the ceiling. That is more than one ENTERPRISE plan can run.

Fast types change the picture. In theory, at the published ceilings, 200 threads that never idle could clear about 51.8M Cloudflare Turnstile solves or about 129.6M reCAPTCHA v3 solves a month. For image, v3 or Turnstile workloads, ENTERPRISE is usually a burst-concurrency purchase rather than a volume one. The thread count guide walks through the full sizing formula with headroom.

Capacity and payback calculator

Save this as capacity_roi.py. It prints theoretical monthly capacity per CAPTCHA type for a given thread count and operating hours, then how many threads your own volume keeps busy at your measured solve time, then the payback worksheet. Capacity assumes every thread is busy every second of the operating window and every solve takes its full published ceiling. Faster solves raise the real figure; idle gaps lower it.

"""ENTERPRISE capacity and ROI worksheet. Capacity figures are theoretical."""
import argparse
import math

# Published CaptchaAI speed ceilings, in seconds per solve.
CEILINGS_S = {
    "Image/OCR": 0.5,
    "Grid image": 1,
    "BLS": 1,
    "reCAPTCHA v3 (incl. Enterprise)": 4,
    "Cloudflare Turnstile": 10,
    "GeeTest v3": 12,
    "Cloudflare Challenge": 15,
    "Invisible reCAPTCHA v2": 30,
    "reCAPTCHA v2 (incl. Enterprise)": 60,
}
PLAN_PRICE = 300                 # ENTERPRISE, monthly list price in USD
V2_PLANNING_PER_THREAD = 50_000  # reCAPTCHA v2 solves per thread per 24/7 month


def capacity(threads, hours_per_day, days):
    busy_seconds = hours_per_day * 3600 * days
    return {name: int(threads * busy_seconds / s) for name, s in CEILINGS_S.items()}


def main():
    p = argparse.ArgumentParser()
    p.add_argument("--threads", type=int, default=200)
    p.add_argument("--hours", type=float, default=24, help="operating hours per day")
    p.add_argument("--days", type=int, default=30)
    p.add_argument("--volume", type=int, default=8_000_000, help="solves per month today")
    p.add_argument("--avg-solve", type=float, default=30, help="measured seconds per solve")
    p.add_argument("--rate", type=float, default=0.80, help="what you pay per 1,000 solves")
    p.add_argument("--build-hours", type=float, default=60, help="one-time engineering")
    p.add_argument("--upkeep-hours", type=float, default=4, help="engineering per month")
    p.add_argument("--hourly", type=float, default=90, help="loaded cost per engineer hour")
    a = p.parse_args()

    print(f"Theoretical capacity: {a.threads} threads, {a.hours:g} h/day, {a.days} days,")
    print("every solve taking its full published ceiling")
    for name, solves in capacity(a.threads, a.hours, a.days).items():
        print(f"  {name:<34}{solves:>15,}")
    planning = a.threads * V2_PLANNING_PER_THREAD * (a.hours / 24) * (a.days / 30)
    print(f"  {'reCAPTCHA v2 planning figure':<34}{planning:>15,.0f}")

    # Threads busy = solves started per second x seconds each solve holds a thread.
    starts_per_s = a.volume / (a.hours * 3600 * a.days)
    busy = math.ceil(starts_per_s * a.avg_solve)
    print(f"\nThreads busy on average   {busy:>11,}  ({a.avg_solve:g} s per solve, plan has {a.threads})")
    if busy > a.threads:
        print("  The plan cannot carry this volume: spread it over more hours or add threads.")

    current = a.volume / 1000 * a.rate
    running = PLAN_PRICE + a.upkeep_hours * a.hourly
    saving = current - running
    build = a.build_hours * a.hourly
    payback = build / saving if saving > 0 else math.inf
    print(f"Per-solve spend today     ${current:>10,.0f}/month")
    print(f"ENTERPRISE plus upkeep    ${running:>10,.0f}/month")
    print(f"Monthly saving            ${saving:>10,.0f}")
    print(f"One-time build cost       ${build:>10,.0f}")
    print(f"Payback                   {payback:>11.1f} months")


if __name__ == "__main__":
    main()

The defaults are example inputs, not benchmarks: 8M solves a month at $0.80 per 1,000 (CapSolver's listed reCAPTCHA v2 rate on 2026-09-29), 60 build hours, 4 upkeep hours a month and $90 per engineer hour. Replace each one with your own. With them, the capacity block shows about 1.04 billion image solves, 51.8M Turnstile and 8.64M reCAPTCHA v2 at the ceiling, against the 10M v2 planning figure. The busy-threads line reads 93 at a 30-second average, and 186 with --avg-solve 60. The worksheet prints $6,400 of per-solve spend, $660 of running cost, a $5,740 monthly saving, a $5,400 build and a payback of 0.9 months. Run python capacity_roi.py --hours 16 --volume 2000000 --rate 0.6 and only 35 threads are busy while payback stretches to 10 months: that workload belongs on ADVANCE ($90/month, 50 threads), or on PREMIUM if its solves run near the 60-second ceiling (70 threads busy), not on ENTERPRISE.

The same worksheet for Node.js teams takes --name=value flags:

// ENTERPRISE capacity and ROI worksheet (Node.js 18+). Capacity figures are theoretical.
// Usage: node capacity_roi.mjs --threads=200 --hours=16 --volume=8000000 --avg-solve=30 --rate=0.8
const CEILINGS_S = {
  'Image/OCR': 0.5,
  'Grid image': 1,
  BLS: 1,
  'reCAPTCHA v3 (incl. Enterprise)': 4,
  'Cloudflare Turnstile': 10,
  'GeeTest v3': 12,
  'Cloudflare Challenge': 15,
  'Invisible reCAPTCHA v2': 30,
  'reCAPTCHA v2 (incl. Enterprise)': 60
}
const PLAN_PRICE = 300 // ENTERPRISE, monthly list price in USD
const V2_PLANNING_PER_THREAD = 50_000

const args = Object.fromEntries(
  process.argv.slice(2).map((arg) => arg.replace(/^--/, '').split('='))
)
const num = (name, fallback) => (args[name] === undefined ? fallback : Number(args[name]))

const threads = num('threads', 200)
const hours = num('hours', 24)
const days = num('days', 30)
const busySeconds = hours * 3600 * days
const fmt = (n) => Math.round(n).toLocaleString('en-US')

console.log(`Theoretical capacity: ${threads} threads, ${hours} h/day, ${days} days`)
for (const [name, seconds] of Object.entries(CEILINGS_S)) {
  console.log(`  ${name.padEnd(34)}${fmt((threads * busySeconds) / seconds).padStart(15)}`)
}
const planning = threads * V2_PLANNING_PER_THREAD * (hours / 24) * (days / 30)
console.log(`  ${'reCAPTCHA v2 planning figure'.padEnd(34)}${fmt(planning).padStart(15)}`)

// Threads busy = solves started per second x seconds each solve holds a thread.
const volume = num('volume', 8_000_000)
const avgSolve = num('avg-solve', 30)
const busy = Math.ceil((volume / busySeconds) * avgSolve)
console.log(`\nThreads busy on average ${fmt(busy).padStart(6)}  (${avgSolve} s per solve, plan has ${threads})`)
if (busy > threads) {
  console.log('  The plan cannot carry this volume: spread it over more hours or add threads.')
}

const current = (volume / 1000) * num('rate', 0.8)
const hourly = num('hourly', 90)
const running = PLAN_PRICE + num('upkeep-hours', 4) * hourly
const saving = current - running
const build = num('build-hours', 60) * hourly
const payback = saving > 0 ? (build / saving).toFixed(1) : 'never'
console.log(`Per-solve spend today   $${fmt(current)}/month`)
console.log(`ENTERPRISE plus upkeep  $${fmt(running)}/month`)
console.log(`Monthly saving          $${fmt(saving)}`)
console.log(`One-time build cost     $${fmt(build)}`)
console.log(`Payback                 ${payback} months`)

The 10M-solve bill at per-solve vendors

These are the reference reCAPTCHA v2 rows used across CaptchaAI's comparison pages, last refreshed 2026-05-21. Every row prices 10M solves a month, which the planning figure above fits on one ENTERPRISE plan:

Vendor Reference rate per 1K Vendor cost at 10M CaptchaAI plan CaptchaAI cost You save
2Captcha ~$2.99 ~$29,900 ENTERPRISE (200 threads) $300 ~99%
Anti-Captcha ~$1.80 ~$18,000 ENTERPRISE (200 threads) $300 ~98%
CapSolver ~$1.50 ~$15,000 ENTERPRISE (200 threads) $300 ~98%
CapMonster Cloud ~$1.00 ~$10,000 ENTERPRISE (200 threads) $300 ~97%
DeathByCaptcha ~$2.89 ~$28,900 ENTERPRISE (200 threads) $300 ~99%
NextCaptcha ~$1.20 ~$12,000 ENTERPRISE (200 threads) $300 ~97%

Per-solve prices move, and several have fallen since that refresh. On 2026-09-29, the vendors' own pricing pages listed reCAPTCHA v2 per 1,000 at $1 to $2.99 for 2Captcha, $0.95 to $2 for Anti-Captcha, $0.80 for CapSolver and $0.60 for CapMonster Cloud. DeathByCaptcha and NextCaptcha were not re-checked. Even at $0.60, 10M solves cost about $6,000 a month, so the conclusion holds while the multiple shrinks. That is why the worksheet takes the rate from your own invoice rather than from this table.

What the build costs you

Keeping 200 tasks in flight at once is an engineering job, and its hours belong in the payback line:

  • A concurrency cap equal to your threads. An asyncio.Semaphore or a fixed worker pool, sized from the threads value that threadsinfo returns, so you never submit more than the plan runs. The aiohttp async guide shows the semaphore pattern.
  • A deliberate connection pool. aiohttp's TCPConnector allows 100 simultaneous connections by default (aiohttp client reference), which is half of ENTERPRISE's 200 threads. Tasks spend most of their life waiting between polls, so the default rarely binds, but a latency spike will queue polls behind it. Set limit on purpose.
  • Poll scheduling. CaptchaAI's docs say to wait 15 to 20 seconds before the first reCAPTCHA v2 poll to res.php, then poll every 5 seconds while the answer is CAPCHA_NOT_READY. With 200 tasks in flight, that is up to 40 poll requests a second on top of submits. Add jitter so a burst of submits does not turn into synchronised waves of polls.
  • Error paths that respect threads. On ERROR_ZERO_BALANCE, which on CaptchaAI means no free threads or no active plan, call threadsinfo and back off instead of resubmitting in a loop. On ERROR_CAPTCHA_UNSOLVABLE, stop polling that ID and allow at most one fresh submission.
  • Timeouts that do not leak threads. res.php has no documented action that cancels a task. If a worker gives up polling and resubmits, nothing stops the first task, so it keeps its thread until it finishes and one CAPTCHA holds two threads. Set the client timeout above the type's solve ceiling, not below it.

Retries cost differently under the two billing models. At a per-solve vendor, a retry costs money. On a thread plan, it costs thread time, which is cheap until you are near the cap and then delays every other task.

When ENTERPRISE is the wrong buy

  • Your measured peak stays under 150 threads. CORPORATE ($240/month, 150 threads) or PREMIUM ($170/month, 100 threads) covers it, and the unused ENTERPRISE threads are pure overhead.
  • You need more than 200. CaptchaAI's pricing section invites customers to order multiple plans to get more threads. Threads from every active plan on the account add up into one pool. Five ENTERPRISE plans (5 × $300) give 1,000 threads for the same money as VIP-1 ($1,500/month, 1,000 threads). Between 200 and 1,000 threads there is no single plan, so stacking is the only way to buy 400 or 600, and at $1.50 a thread, ENTERPRISE units are the cheapest blocks to stack. The stacking guide covers how combined plans behave.
  • Your volume is seasonal. Say you need 200 threads for three peak months and 100 for the other nine. Keep PREMIUM ($170/month, 100 threads) all year and stack a second one only at peak: those three months cost $340 each, $40 more than one ENTERPRISE ($300/month, 200 threads), and the other nine cost $130 less each.
  • Your failures are upstream. If tokens are rejected because of wrong parameters or session mismatches, more threads only let you fail faster. Fix acceptance first.

Watching utilisation after you buy

threadsinfo tells you whether the plan fits. Send key and action=threadsinfo to https://ocr.captchaai.com/res.php. The documented response is a JSON object such as {"threads":"200","working_threads":153}, where threads arrives as a string and working_threads as an integer (CaptchaAI API docs). The official Python SDK wraps the same call as threads_info().

Alert when utilisation stays above 80% for a sustained window, not on a single spike. The monitors below sample every 30 seconds and alert after ten samples in a row above the threshold, which is five minutes.

"""Alert when CaptchaAI thread utilisation stays above 80% (Python 3.8+, stdlib only)."""
import json
import os
import sys
import time
import urllib.parse
import urllib.request
from collections import deque

API_KEY = os.environ.get("CAPTCHAAI_API_KEY", "YOUR_API_KEY")
RES_URL = "https://ocr.captchaai.com/res.php"
THRESHOLD = 0.80  # alert when utilisation is above 80% ...
WINDOW = 10       # ... for 10 samples in a row
INTERVAL_S = 30   # 10 x 30 s = 5 minutes sustained
FATAL = {"ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED"}


def threads_info():
    body = urllib.parse.urlencode({"key": API_KEY, "action": "threadsinfo"}).encode()
    with urllib.request.urlopen(RES_URL, data=body, timeout=15) as resp:
        text = resp.read().decode().strip()
    try:
        data = json.loads(text)
    except ValueError:
        raise RuntimeError(text[:80])  # plain-text error code or HTML error page
    if not isinstance(data, dict) or "threads" not in data or "working_threads" not in data:
        raise RuntimeError(str(data.get("request", text[:80]) if isinstance(data, dict) else data))
    return int(data["threads"]), int(data["working_threads"])  # threads arrives as a string


def main():
    if API_KEY == "YOUR_API_KEY":
        sys.exit("Set CAPTCHAAI_API_KEY first.")
    recent = deque(maxlen=WINDOW)
    while True:
        try:
            threads, working = threads_info()
        except RuntimeError as exc:
            if str(exc) in FATAL:
                sys.exit(f"Stopping: {exc}")
            print(f"threadsinfo failed: {exc}", file=sys.stderr)
        except OSError as exc:  # network errors and HTTP errors
            print(f"network error: {exc}", file=sys.stderr)
        else:
            util = working / threads if threads else 0.0
            recent.append(util)
            print(f"{time.strftime('%H:%M:%S')} {working}/{threads} threads busy ({util:.0%})")
            if len(recent) == WINDOW and min(recent) > THRESHOLD:
                print(f"ALERT: above {THRESHOLD:.0%} for {WINDOW * INTERVAL_S} s", file=sys.stderr)
                recent.clear()  # re-arm: alert again only after another full window
        time.sleep(INTERVAL_S)


if __name__ == "__main__":
    main()

The Node.js version uses the built-in fetch, so it needs no dependencies:

// Alert when CaptchaAI thread utilisation stays above 80% (Node.js 18+, no dependencies).
const API_KEY = process.env.CAPTCHAAI_API_KEY ?? 'YOUR_API_KEY'
const RES_URL = 'https://ocr.captchaai.com/res.php'
const THRESHOLD = 0.8
const WINDOW = 10
const INTERVAL_MS = 30_000
const FATAL = new Set(['ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST', 'IP_BANNED'])

async function threadsInfo () {
  const res = await fetch(RES_URL, {
    method: 'POST',
    body: new URLSearchParams({ key: API_KEY, action: 'threadsinfo' }),
    signal: AbortSignal.timeout(15_000)
  })
  const text = (await res.text()).trim()
  let data
  try {
    data = JSON.parse(text)
  } catch {
    throw new Error(text.slice(0, 80)) // plain-text error code or HTML error page
  }
  if (data === null || typeof data !== 'object' || !('threads' in data) || !('working_threads' in data)) {
    throw new Error(String(data?.request ?? text))
  }
  return { threads: Number(data.threads), working: Number(data.working_threads) }
}

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

async function main () {
  if (API_KEY === 'YOUR_API_KEY') throw new Error('Set CAPTCHAAI_API_KEY first.')
  let recent = []
  for (;;) {
    try {
      const { threads, working } = await threadsInfo()
      const util = threads ? working / threads : 0
      recent = [...recent, util].slice(-WINDOW)
      console.log(`${new Date().toISOString()} ${working}/${threads} threads busy (${Math.round(util * 100)}%)`)
      if (recent.length === WINDOW && Math.min(...recent) > THRESHOLD) {
        console.error(`ALERT: above ${THRESHOLD * 100}% for ${(WINDOW * INTERVAL_MS) / 1000} s`)
        recent = [] // re-arm: alert again only after another full window
      }
    } catch (err) {
      if (FATAL.has(err.message)) throw err
      console.error(`threadsinfo failed: ${err.message}`)
    }
    await sleep(INTERVAL_MS)
  }
}

main().catch((err) => {
  console.error(`Stopping: ${err.message}`)
  process.exit(1)
})

How to act on the readings:

  • Sustained above 80%. New tasks are about to wait for a free thread. Depending on how the queue handles it, saturation can show up as ERROR_ZERO_BALANCE on submit or simply as longer CAPCHA_NOT_READY stretches. This monitor catches both, because either way working_threads sits at the cap. Smooth the bursts, add a plan, or move up the ladder.
  • A month-long peak that never passes 150. CORPORATE ($240/month, 150 threads) would have carried the same load.
  • working_threads near zero while your workers report busy. Your tasks are not reaching CaptchaAI. Check the client, not the plan.

FAQ

Does the ENTERPRISE plan add reCAPTCHA Enterprise support?

No. The two names are unrelated. Every plan solves reCAPTCHA v2 Enterprise and v3 Enterprise: you submit method=userrecaptcha with enterprise=1 (plus version=v3 for v3 Enterprise), then read result and user_agent from res.php with json=1 and send that same User-Agent to the target site with the token. Size v2 Enterprise like standard v2, since both have the same under-60-second ceiling.

Does ENTERPRISE come with priority support or an SLA?

The published plan cards list price, threads and unlimited solving, and nothing else. If you need a support commitment or uptime terms in writing, get them from CaptchaAI before purchase.

Is stacking ENTERPRISE plans cheaper than VIP-1?

Per thread, neither is cheaper. VIP-1 ($1,500/month, 1,000 threads) and ENTERPRISE ($300/month, 200 threads) both work out to $1.50 a thread. The difference is granularity: stacking buys threads in blocks of 200, so two ENTERPRISE plans ($300 each) give you 400 threads, where the smallest VIP plan sells 1,000. At exactly 1,000 threads the bill is identical, and the choice comes down to renewing one plan or five.

What happens if my client sends more than 200 tasks at once?

The extra tasks have no free thread, and CaptchaAI's sources disagree on what you see next. The API docs say in.php answers ERROR_ZERO_BALANCE ("You don't have free threads"), while a comment in the official SDK says the API queues the task silently, so it just spends longer at CAPCHA_NOT_READY. Plan for both: cap client-side concurrency at the threads value from threadsinfo, and back off on ERROR_ZERO_BALANCE instead of resubmitting. The thread limit guide covers the diagnosis.

Next step

Measure your peak concurrency and your real solve times first, then run the worksheet with your own invoice rate. If the answer lands between 150 and 200 threads, check current ENTERPRISE pricing against the other thread plans.

Comments are disabled for this article.