1. How Arkose Labs FunCaptcha Operates
Arkose Labs (formerly FunCaptcha) is the primary anti-bot defense guarding enterprise platforms including Twitter / X, OpenAI / ChatGPT, Microsoft Outlook, LinkedIn, and Roblox. Its verification puzzles feature:
- 3D Animal & Object Rotation: Users must rotate a 3D animal model until it faces in the direction specified by an arrow icon or hand.
- Pattern & Coordinate Matching: Matching dice numbers, identifying shadows, or selecting pairs of identical items across 5 to 10 sequential rounds.
- Encrypted Data Blobs (
data[blob]): High-risk platforms generate an ephemeral cryptographic session blob that ties the CAPTCHA solution directly to the client session.
Because standard computer vision algorithms struggle with 3D rotation perspectives, 2Captcha uses a hybrid system combining specialized computer vision models with real-time human verification, achieving an industry-leading 99.2% pass rate.
2. Installing Required Dependencies
Install the official 2captcha-python library and playwright:
pip install 2captcha-python playwright
playwright install chromium3. Basic Task Dispatch via 2Captcha SDK
The TwoCaptcha SDK provides native support for FunCaptcha parameters:
import os
from twocaptcha import TwoCaptcha
api_key = os.getenv("TWOCAPTCHA_API_KEY")
solver = TwoCaptcha(api_key)
try:
print("Dispatching FunCaptcha task to 2Captcha...")
result = solver.funcaptcha(
sitekey="2CB16598-CB82-4CF7-B332-5DE690AE6F0F", # Public Key (pk)
url="https://target-portal.com/signup",
surl="https://client-api.arkoselabs.com",
# Optional data blob required by Twitter/X, OpenAI, etc.:
# data={"blob": "captured_blob_hex_value"}
)
token = result["code"]
print("FunCaptcha solved successfully!")
print(f"Token: {token[:45]}...")
except Exception as e:
print(f"Failed to solve FunCaptcha: {e}")4. Complete Playwright Automation Pipeline
The production script below uses Playwright network listeners to intercept the Arkose API handshake, extract the public key and session blob, dispatch the puzzle to 2Captcha, and populate input[name="fc-token"]:
import asyncio
import os
import re
from playwright.async_api import async_playwright
from twocaptcha import TwoCaptcha
API_KEY = os.getenv("TWOCAPTCHA_API_KEY")
solver = TwoCaptcha(API_KEY)
TARGET_URL = "https://example-portal.com/login"
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context()
page = await context.new_page()
captured_blob = None
captured_pk = None
# Listen to Arkose API network requests to extract public key and data blob
async def handle_request(request):
nonlocal captured_blob, captured_pk
if "arkoselabs.com" in request.url:
pk_match = re.search(r"[?&]pk=([A-Fa-f0-9-]+)", request.url)
if pk_match:
captured_pk = pk_match.group(1)
post_data = request.post_data
if post_data and "data%5Bblob%5D=" in post_data:
blob_match = re.search(r"data%5Bblob%5D=([^&]+)", post_data)
if blob_match:
captured_blob = blob_match.group(1)
page.on("request", handle_request)
print(f"Navigating to {TARGET_URL}...")
await page.goto(TARGET_URL, wait_until="networkidle")
# Wait for FunCaptcha iframe or container
await page.wait_for_selector('iframe[src*="arkoselabs.com"], #fc-token, input[name="fc-token"]', timeout=12000)
# Fallback public key extraction from DOM if not caught in network
if not captured_pk:
captured_pk = await page.evaluate("""() => {
const el = document.querySelector("[data-pkey]");
return el ? el.getAttribute("data-pkey") : null;
}""")
print(f"Detected Arkose FunCaptcha public key: {captured_pk}")
# Request solution from 2Captcha
print("Dispatching challenge to 2Captcha API...")
solve_kwargs = {
"sitekey": captured_pk,
"url": TARGET_URL,
"surl": "https://client-api.arkoselabs.com"
}
if captured_blob:
solve_kwargs["data"] = {"blob": captured_blob}
result = await asyncio.to_thread(solver.funcaptcha, **solve_kwargs)
token = result["code"]
print(f"Received FunCaptcha token: {token[:40]}...")
# Inject token into fc-token hidden field
await page.evaluate("""(tokenVal) => {
let tokenInput = document.querySelector('input[name="fc-token"], #fc-token');
if (!tokenInput) {
tokenInput = document.createElement("input");
tokenInput.type = "hidden";
tokenInput.name = "fc-token";
tokenInput.id = "fc-token";
document.forms[0].appendChild(tokenInput);
}
tokenInput.value = tokenVal;
tokenInput.dispatchEvent(new Event("input", { bubbles: true }));
tokenInput.dispatchEvent(new Event("change", { bubbles: true }));
}""", token)
print("Submitting form with solved token...")
await page.click('button[type="submit"]')
await page.wait_for_load_state("networkidle")
print("Successfully bypassed Arkose FunCaptcha!")
await browser.close()
if __name__ == "__main__":
asyncio.run(main())5. Common Pitfalls & Best Practices
- Always Capture
data[blob]on Twitter & Outlook: If the target platform issues an encrypted blob, submitting without it causes instant rejection. Use Playwright request interception as demonstrated above. - Match Egress IP with Proxy: If the target website tracks IP reputation closely, pass your proxy credentials into 2Captcha so the human worker or solver completes the challenge from the exact same residential IP pool.
- Verify Service Subdomain: Most sites use
https://client-api.arkoselabs.com, but custom enterprise clients may use custom CNAME subdomains likehttps://arkose.target.com. Check network traffic for the origin.
6. Why 2Captcha is Rated #1 for Arkose FunCaptcha Resolution
2Captcha Arkose Benchmark Telemetry
Rank #1 Overall- Average Solving Speed: 14.8 seconds for multi-round 3D rotation puzzles
- Pass Rate: 99.2% first-attempt token acceptance on Twitter, OpenAI & Outlook
- Pricing: $1.50 - $2.99 per 1,000 solves with true Pay-As-You-Go ($0.0015/solve)
- Blob Encryption Support: Full compatibility with ephemeral session blobs and custom proxy routing