FunCaptcha, now branded as Arkose Labs, is one of the hardest CAPTCHA types to automate around. Unlike reCAPTCHA or Turnstile — where you submit a token extracted from a page — FunCaptcha presents interactive visual mini-games and uses behavioral signals to score the session. This guide covers how FunCaptcha works, which solvers reliably handle it, and how to integrate one into your automation workflow.
What Is FunCaptcha?
FunCaptcha (Arkose Labs) is a challenge-response system built around forced interaction. Users are shown a short mini-game — typically rotating a 3D object to match an orientation, identifying which image matches a prompt, or solving a simple spatial puzzle. The system scores not just the answer but the interaction pattern: cursor movement, completion time, and device fingerprint.
Arkose Labs positions itself differently from traditional CAPTCHAs. The product is sold to enterprise clients under a fraud-prevention and account-takeover-prevention framing. This means:
- The challenge difficulty can scale up dynamically based on risk signals
- High-volume automation attempts are more likely to receive harder variants
- The session token (
fc-token) is tied to the browser environment and expires quickly
Where you'll encounter FunCaptcha:
- Twitter / X — account creation, login
- Roblox — account creation, item purchase flows
- EA Games — account registration
- Epic Games — account creation, sign-in
- PayPal — some verification flows
- Dropbox, Snapchat, LinkedIn — sporadic deployment
How FunCaptcha Works (Technical)
The FunCaptcha flow from an automation perspective:
- The target page loads Arkose Labs JavaScript (
api.funcaptcha.com/fc/api/). - The JS initializes a challenge session using a public key (a GUID, e.g.
B7D8911C-5CC8-A9A3-35B0-554ACEE604DA). - An iframe renders the interactive challenge.
- On successful completion, the JS writes an
fc-tokento a hidden form field. - The form submits the token alongside other fields; the origin server validates it with Arkose Labs.
The public key is unique to each integration and is embedded in the page HTML or JavaScript. It is different from a reCAPTCHA site key in that the same public key can serve different challenge difficulties depending on the IP and session context.
Token lifetime: FunCaptcha tokens are short-lived (typically 2–3 minutes). Solve immediately before form submission.
Service URL: Some FunCaptcha deployments use a custom subdomain (surl parameter, e.g. https://client-api.arkoselabs.com). Pass this to the solver to avoid token mismatch errors.
Solver Support for FunCaptcha
Not all CAPTCHA solvers support FunCaptcha. The challenge is harder to solve than reCAPTCHA v2 because it requires vision AI or human workers to interpret visual mini-games. The table below reflects current CaptchaRank benchmark data and publicly reported performance signals.
| Solver | FunCaptcha Support | Success Rate | Avg Solve Time | Notes |
|---|---|---|---|---|
| CaptchaAI | ✅ Full | ~96% | 12–18s | Highest benchmark success rate; surl support |
| 2Captcha | ✅ Full | ~91% | 18–35s | Widest type coverage; long-running reliability |
| Anti-Captcha | ✅ Full | ~89% | 20–40s | Reliable for high volume; human workers |
| CapSolver | ✅ Full | ~93% | 15–25s | Good automation-grade speed; API-first |
| CapMonster Cloud | ✅ Full | ~87% | 20–40s | Affordable at scale; lower success on hard variants |
| NopeCHA | ❌ Limited | N/A | N/A | Primarily browser extension; no stable FunCaptcha API |
| DeathByCaptcha | ✅ Partial | ~82% | 25–45s | Legacy support; not recommended for FunCaptcha at volume |
Success rates are based on CaptchaRank benchmark data and publicly reported performance signals. Rates vary with challenge difficulty tiers and originating IP reputation.
How to Solve FunCaptcha in Python
The examples below use CaptchaAI, which uses a 2Captcha-compatible API format. Switching to 2Captcha or Anti-Captcha is one line change (api_key and the endpoint host).
Step 1 — Find the Public Key
The public key is embedded in the page. Inspect the Arkose Labs widget initialization:
import re
import requests
def get_funcaptcha_key(page_url: str) -> str:
"""Extract the Arkose Labs public key from a page."""
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
html = requests.get(page_url, headers=headers).text
# Common patterns
patterns = [
r'data-pkey=["\']([A-F0-9\-]{36})["\']',
r'"public_key"\s*:\s*"([A-F0-9\-]{36})"',
r'funcaptcha.*?key["\s:=]+["\']([A-F0-9\-]{36})["\']',
]
for pattern in patterns:
match = re.search(pattern, html, re.IGNORECASE)
if match:
return match.group(1)
raise ValueError("FunCaptcha public key not found on page")
Step 2 — Submit and Poll the Solver
import requests
import time
def solve_funcaptcha(
api_key: str,
page_url: str,
public_key: str,
service_url: str | None = None,
) -> str:
"""
Solve FunCaptcha using CaptchaAI (2Captcha-compatible API).
Returns the fc-token string.
"""
payload = {
"key": api_key,
"method": "funcaptcha",
"publickey": public_key,
"pageurl": page_url,
"json": 1,
}
if service_url:
payload["surl"] = service_url # Required for custom Arkose deployments
# Submit task
r = requests.post("https://ocr.captchaai.com/in.php", data=payload, timeout=30)
r.raise_for_status()
result = r.json()
if result.get("status") != 1:
raise RuntimeError(f"Submit failed: {result}")
task_id = result["request"]
# Poll for result (FunCaptcha is slower than reCAPTCHA — start polling at 15s)
time.sleep(15)
for _ in range(30):
r = requests.get(
"https://ocr.captchaai.com/res.php",
params={"key": api_key, "action": "get", "id": task_id, "json": 1},
timeout=30,
)
data = r.json()
if data.get("status") == 1:
return data["request"] # This is the fc-token
if data.get("request") not in ("CAPCHA_NOT_READY", "CAPTCHA_NOT_READY"):
raise RuntimeError(f"Unexpected response: {data}")
time.sleep(5)
raise TimeoutError("FunCaptcha solve timed out after 165 seconds")
Step 3 — Inject the Token
FunCaptcha tokens are injected differently depending on the target site. The most common patterns:
from playwright.sync_api import sync_playwright
def submit_with_funcaptcha_token(
page_url: str,
public_key: str,
api_key: str,
service_url: str | None = None,
) -> None:
token = solve_funcaptcha(api_key, page_url, public_key, service_url)
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(page_url, wait_until="networkidle")
# Inject token into the hidden field FunCaptcha uses
page.evaluate(f"""
const fields = document.querySelectorAll(
'input[name="fc-token"], input[id="FunCaptcha-Token"], input[name="arkoseToken"]'
);
fields.forEach(f => f.value = '{token}');
""")
# Dispatch change event so the page recognizes the token
page.evaluate("""
document.querySelectorAll('input[name="fc-token"]')
.forEach(f => f.dispatchEvent(new Event('change', {bubbles: true})));
""")
# Submit the form
page.click('button[type="submit"]')
page.wait_for_load_state("networkidle")
browser.close()
Using 2Captcha as a Drop-In Alternative
# Replace the endpoint host only
r = requests.post("https://2captcha.com/in.php", data=payload, timeout=30)
# All other parameters are identical
Common Errors and Fixes
ERROR_CAPTCHA_UNSOLVABLE
The solver failed to complete the mini-game. This is most common when Arkose Labs is serving a harder challenge tier to the request IP. Fixes:
- Use a residential proxy when submitting the page request (not the solver request)
- Switch to a higher-accuracy solver for this type (CaptchaAI, CapSolver)
- Retry — challenge difficulty varies per session
Token not accepted / "invalid token" on submission
- The fc-token expired before submission (> 2–3 minutes between solve and submit)
- The service_url (surl) was not passed when the site uses a custom Arkose deployment
- The token was injected into the wrong field name — inspect the form for the correct hidden field
Solver returns token but challenge still shows
Some implementations call arkose.run() from JS on page load and validate the token client-side before form submit. Use Playwright's page.evaluate() to call the Arkose resolution callback directly, or trigger the form programmatically after injection.
High failure rate (< 80% success)
- Confirm you're passing the correct publickey — the key differs between staging and production environments on some sites
- For Roblox, EA, and similar high-enforcement sites, expect higher rejection rates even with valid tokens on flagged IPs
When to Use Which Solver
| Scenario | Recommended Solver | Reason |
|---|---|---|
| Standard automation (moderate volume) | CaptchaAI | Highest success rate on FunCaptcha per CaptchaRank benchmark |
| Very high volume (> 10,000/day) | 2Captcha or Anti-Captcha | Deep worker pool; consistent at scale |
| Cost-sensitive projects | CapMonster Cloud | Lowest price per solve; acceptable for lower enforcement tiers |
| API-first pipelines | CapSolver | Clean JSON API, no legacy form encoding |
| One-off / manual testing | Any with trial credits | 2Captcha and Anti-Captcha both offer small free credits |
FunCaptcha vs Other CAPTCHA Types
FunCaptcha is notably harder to automate than reCAPTCHA v2 or Turnstile because:
- No static token: there is no predictable site key that maps to a static challenge — the session context affects the challenge type
- Behavioral scoring: time-to-complete and interaction pattern are part of the signal
- Enterprise client control: the site owner can raise enforcement levels, increasing failure rates even for known-good solvers
If you encounter a site protected by Arkose Labs in a high-enforcement mode, expect solve times of 20–40 seconds and success rates lower than other CAPTCHA types. Budget for retries in your pipeline.
Explore More in This Hub
- Best FunCaptcha Solver — ranked comparison of all solvers for FunCaptcha with pricing
- CAPTCHA Solver API Integration Guide — language-agnostic integration patterns
- CAPTCHA Solving in Python: Quick Start — 15-minute setup guide
- Best CAPTCHA Solvers Ranked — full leaderboard across all CAPTCHA types
Benchmark data sourced from CaptchaRank live performance monitoring. Success rates reflect current data and are updated as solver performance changes.