CapMonster CAPTCHA Solver
by @easonc13
Solve CAPTCHAs (reCAPTCHA v2/v3, hCaptcha, Cloudflare Turnstile, image CAPTCHAs) using CapMonster Cloud API. Use when browser automation encounters CAPTCHA c...
clawhub install capmonsterπ About This Skill
name: capmonster description: Solve CAPTCHAs (reCAPTCHA v2/v3, hCaptcha, Cloudflare Turnstile, image CAPTCHAs) using CapMonster Cloud API. Use when browser automation encounters CAPTCHA challenges.
CapMonster Cloud CAPTCHA Solver
Solve CAPTCHAs programmatically via CapMonster Cloud API during browser automation.
API Info
https://api.capmonster.cloud${CAPMONSTER_API_KEY}tools/capmonster-cloud/capmonster_api.pyPricing (per 1000 solves)
| Type | Price | Avg Time | |------|-------|----------| | reCAPTCHA v2 | $0.60 | 10-30s | | reCAPTCHA v3 | $0.90 | 5-15s | | hCaptcha | $1.50 | 10-30s | | Cloudflare Turnstile | $1.20 | 5-15s | | Image CAPTCHA | $0.04 | 2-5s |
Quick Reference
Check Balance
curl -s -X POST https://api.capmonster.cloud/getBalance \
-H "Content-Type: application/json" \
-d '{"clientKey": "${CAPMONSTER_API_KEY}"}' | jq .balance
Using Python Client
import sys
sys.path.insert(0, '/Users/eason/clawd/tools/capmonster-cloud')
from capmonster_api import CapMonsterClientclient = CapMonsterClient("${CAPMONSTER_API_KEY}")
Check balance
print(f"Balance: ${client.get_balance()}")Solve reCAPTCHA v2
token = client.solve_recaptcha_v2(
website_url="https://example.com/page-with-captcha",
website_key="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
)
Complete Workflow: Browser Automation + CAPTCHA Solving
Step 1: Detect CAPTCHA on Page
Take a browser snapshot and check for CAPTCHA indicators:
// Via browser action=act evaluate
browser action=act profile=chrome request={
"kind": "evaluate",
"fn": "(() => {
const indicators = {
recaptchaV2: !!document.querySelector('.g-recaptcha, [data-sitekey], iframe[src*=\"recaptcha\"]'),
recaptchaV3: !!document.querySelector('script[src*=\"recaptcha/api.js?render=\"]'),
hcaptcha: !!document.querySelector('.h-captcha, [data-hcaptcha-sitekey], iframe[src*=\"hcaptcha\"]'),
turnstile: !!document.querySelector('.cf-turnstile, [data-sitekey], iframe[src*=\"turnstile\"]')
};
return JSON.stringify(indicators);
})()"
}
Step 2: Extract Site Key
reCAPTCHA v2:
// Option A: From data-sitekey attribute
document.querySelector('[data-sitekey]')?.dataset.sitekey// Option B: From iframe src
document.querySelector('iframe[src*="recaptcha"]')?.src.match(/k=([^&]+)/)?.[1]
// Option C: From grecaptcha object
window.___grecaptcha_cfg?.clients?.[0]?.Y?.Y?.sitekey
reCAPTCHA v3:
// From script src
document.querySelector('script[src*="recaptcha/api.js?render="]')?.src.match(/render=([^&]+)/)?.[1]// Or from grecaptcha config
window.___grecaptcha_cfg?.clients?.[0]?.Y?.Y?.sitekey
hCaptcha:
document.querySelector('[data-hcaptcha-sitekey], .h-captcha[data-sitekey]')?.dataset.sitekey ||
document.querySelector('[data-hcaptcha-sitekey]')?.getAttribute('data-hcaptcha-sitekey')
Cloudflare Turnstile:
document.querySelector('.cf-turnstile[data-sitekey], [data-turnstile-sitekey]')?.dataset.sitekey
Step 3: Submit to CapMonster API
Using curl (shell):
# Create task
TASK_ID=$(curl -s -X POST https://api.capmonster.cloud/createTask \
-H "Content-Type: application/json" \
-d '{
"clientKey": "${CAPMONSTER_API_KEY}",
"task": {
"type": "RecaptchaV2TaskProxyless",
"websiteURL": "https://scholar.google.com/",
"websiteKey": "SITEKEY_HERE"
}
}' | jq -r .taskId)echo "Task ID: $TASK_ID"
Using Python:
import sys
sys.path.insert(0, '/Users/eason/clawd/tools/capmonster-cloud')
from capmonster_api import CapMonsterClientclient = CapMonsterClient("${CAPMONSTER_API_KEY}")
For reCAPTCHA v2
token = client.solve_recaptcha_v2(
website_url="https://scholar.google.com/",
website_key="EXTRACTED_SITEKEY"
)For reCAPTCHA v3
token = client.solve_recaptcha_v3(
website_url="https://example.com/",
website_key="EXTRACTED_SITEKEY",
min_score=0.7,
page_action="submit" # Check page source for action name
)For hCaptcha
token = client.solve_hcaptcha(
website_url="https://example.com/",
website_key="EXTRACTED_SITEKEY"
)For Turnstile
token = client.solve_turnstile(
website_url="https://example.com/",
website_key="EXTRACTED_SITEKEY"
)
Step 4: Poll for Solution
Using curl:
# Poll until ready (max 120 seconds)
for i in {1..60}; do
RESULT=$(curl -s -X POST https://api.capmonster.cloud/getTaskResult \
-H "Content-Type: application/json" \
-d "{\"clientKey\": \"${CAPMONSTER_API_KEY}\", \"taskId\": $TASK_ID}")
STATUS=$(echo "$RESULT" | jq -r .status)
if [ "$STATUS" = "ready" ]; then
TOKEN=$(echo "$RESULT" | jq -r '.solution.gRecaptchaResponse // .solution.token')
echo "Token: $TOKEN"
break
fi
echo "Status: $STATUS, waiting..."
sleep 2
done
The Python client handles polling automatically with solve_and_wait().
Step 5: Inject Solution into Page
reCAPTCHA v2/v3:
// Via browser evaluate
browser action=act profile=chrome request={
"kind": "evaluate",
"fn": "(() => {
const token = 'CAPMONSTER_TOKEN_HERE';
// Method 1: Set textarea value (most common)
const textarea = document.querySelector('#g-recaptcha-response, [name=\"g-recaptcha-response\"]');
if (textarea) {
textarea.value = token;
textarea.style.display = 'block'; // Some sites hide it
}
// Method 2: Also set any iframe response
document.querySelectorAll('iframe[src*=\"recaptcha\"]').forEach(iframe => {
try {
const doc = iframe.contentDocument || iframe.contentWindow.document;
const ta = doc.querySelector('#g-recaptcha-response');
if (ta) ta.value = token;
} catch(e) {}
});
// Method 3: Trigger callback if exists
if (typeof ___grecaptcha_cfg !== 'undefined') {
const clients = ___grecaptcha_cfg.clients;
for (let cid in clients) {
const client = clients[cid];
// Find callback
const callback = client?.Y?.Y?.callback || client?.Y?.callback;
if (typeof callback === 'function') {
callback(token);
}
}
}
return 'Token injected';
})()"
}
hCaptcha:
browser action=act profile=chrome request={
"kind": "evaluate",
"fn": "(() => {
const token = 'CAPMONSTER_TOKEN_HERE';
// Set response textarea
const textarea = document.querySelector('[name=\"h-captcha-response\"], [name=\"g-recaptcha-response\"]');
if (textarea) textarea.value = token;
// Trigger callback
if (typeof hcaptcha !== 'undefined') {
// Find widget ID
const widget = document.querySelector('.h-captcha');
const widgetId = widget?.dataset.hcaptchaWidgetId || 0;
// Some sites have custom callbacks
}
return 'hCaptcha token injected';
})()"
}
Cloudflare Turnstile:
browser action=act profile=chrome request={
"kind": "evaluate",
"fn": "(() => {
const token = 'CAPMONSTER_TOKEN_HERE';
// Set the hidden input
const input = document.querySelector('[name=\"cf-turnstile-response\"]');
if (input) input.value = token;
// Also set any callback data attribute
const container = document.querySelector('.cf-turnstile');
if (container && container.dataset.callback) {
const callbackName = container.dataset.callback;
if (typeof window[callbackName] === 'function') {
windowcallbackName;
}
}
return 'Turnstile token injected';
})()"
}
Step 6: Submit the Form
After injecting the token, submit the form:
// Click submit button
browser action=act profile=chrome request={"kind":"click","ref":"submit button ref"}// Or trigger form submission
browser action=act profile=chrome request={
"kind": "evaluate",
"fn": "document.querySelector('form').submit()"
}
Google Scholar Specific
Google Scholar uses invisible reCAPTCHA v2 that triggers on suspicious activity.
Detection
// Check if blocked
const isBlocked = document.body.textContent.includes('unusual traffic') ||
document.body.textContent.includes('ζεηη³»η΅±') ||
!!document.querySelector('iframe[src*="recaptcha"]');
Extract Site Key
// Scholar's recaptcha iframe
const iframe = document.querySelector('iframe[src*="recaptcha"]');
const sitekey = iframe?.src.match(/k=([^&]+)/)?.[1];
// Usually: 6LfwuyUTAAAAAOAmoS0fdqijC2PbbdH4kjq62Y1b
Solve and Submit
import sys
sys.path.insert(0, '/Users/eason/clawd/tools/capmonster-cloud')
from capmonster_api import CapMonsterClientclient = CapMonsterClient("${CAPMONSTER_API_KEY}")
Scholar's known sitekey (may change)
token = client.solve_recaptcha_v2(
website_url="https://scholar.google.com/scholar?q=test",
website_key="6LfwuyUTAAAAAOAmoS0fdqijC2PbbdH4kjq62Y1b"
)print(f"Token: {token[:50]}...")
Then inject via browser automation.
Image CAPTCHA
For traditional image CAPTCHAs (text recognition):
import base64
import sys
sys.path.insert(0, '/Users/eason/clawd/tools/capmonster-cloud')
from capmonster_api import CapMonsterClientclient = CapMonsterClient("${CAPMONSTER_API_KEY}")
From file
text = client.solve_image_captcha(image_path="/tmp/captcha.png")From base64
with open("/tmp/captcha.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
text = client.solve_image_captcha(image_base64=b64)print(f"CAPTCHA text: {text}")
All-in-One Shell Script
Save as solve-recaptcha.sh:
#!/bin/bash
Usage: ./solve-recaptcha.sh
API_KEY="${CAPMONSTER_API_KEY}"
WEBSITE_URL="$1"
SITEKEY="$2"
Create task
echo "Creating task..."
RESPONSE=$(curl -s -X POST https://api.capmonster.cloud/createTask \
-H "Content-Type: application/json" \
-d "{
\"clientKey\": \"$API_KEY\",
\"task\": {
\"type\": \"RecaptchaV2TaskProxyless\",
\"websiteURL\": \"$WEBSITE_URL\",
\"websiteKey\": \"$SITEKEY\"
}
}")TASK_ID=$(echo "$RESPONSE" | jq -r .taskId)
ERROR_ID=$(echo "$RESPONSE" | jq -r .errorId)
if [ "$ERROR_ID" != "0" ]; then
echo "Error: $(echo "$RESPONSE" | jq -r .errorDescription)"
exit 1
fi
echo "Task ID: $TASK_ID"
Poll for result
echo "Waiting for solution..."
for i in {1..60}; do
RESULT=$(curl -s -X POST https://api.capmonster.cloud/getTaskResult \
-H "Content-Type: application/json" \
-d "{\"clientKey\": \"$API_KEY\", \"taskId\": $TASK_ID}")
STATUS=$(echo "$RESULT" | jq -r .status)
if [ "$STATUS" = "ready" ]; then
TOKEN=$(echo "$RESULT" | jq -r '.solution.gRecaptchaResponse')
COST=$(echo "$RESULT" | jq -r '.cost')
echo "β
Solved! Cost: \$$COST"
echo ""
echo "TOKEN:"
echo "$TOKEN"
exit 0
fi
printf "."
sleep 2
doneecho ""
echo "β Timeout after 120 seconds"
exit 1
Troubleshooting
Token Invalid / Expired
websiteURL matches the actual page URLERROR_CAPTCHA_UNSOLVABLE
ERROR_RECAPTCHA_TIMEOUT
Token Injected but Form Fails
Finding the Callback
// Check grecaptcha config for callback
JSON.stringify(___grecaptcha_cfg, (k, v) => typeof v === 'function' ? '[Function]' : v, 2)// Or check data-callback attribute
document.querySelector('[data-callback]')?.dataset.callback
Best Practices
1. Extract sitekey fresh - Don't hardcode, sites may rotate
2. Use correct URL - Must match the page showing CAPTCHA
3. Inject quickly - Tokens expire in ~120 seconds
4. Check for v3 - Some sites use v3 (invisible), need pageAction
5. Monitor balance - Set up low-balance alerts
6. Retry on failure - CapMonster may fail occasionally
Cost Estimation
| Use Case | CAPTCHAs/day | Monthly Cost | |----------|--------------|--------------| | Light (research) | 10 | ~$0.18 | | Medium (scraping) | 100 | ~$1.80 | | Heavy (automation) | 1000 | ~$18.00 |
Current balance is ~$10, good for ~16,000 reCAPTCHA v2 solves.
π Tips & Best Practices
1. Extract sitekey fresh - Don't hardcode, sites may rotate
2. Use correct URL - Must match the page showing CAPTCHA
3. Inject quickly - Tokens expire in ~120 seconds
4. Check for v3 - Some sites use v3 (invisible), need pageAction
5. Monitor balance - Set up low-balance alerts
6. Retry on failure - CapMonster may fail occasionally