Ship captcha solving
in under 5 minutes
VisuSolve is a high-performance AI captcha solver. ONNX runtime running locally—no third-party human farms. reCAPTCHA v2, v3, Geetest v4 at <400ms median latency, 99.9% uptime SLA, and predictable credit-based billing. Built for automation engineers, not CAPTCHA farms.
Unlike human-solver APIs, VisuSolve runs a fine-tuned vision transformer locally on your infra or ours. No queues, no human latency variance, no ethical concerns. You get deterministic JSON and a token you can submit directly.
What you get
- reCAPTCHA v2 classification — image grid challenge solver (1×1, 3×3, 4×4) via POST /classify. Returns boolean mask.
- reCAPTCHA v2 browser solver — full token flow via POST /solve with website_key + website_url. Returns g-recaptcha-response token.
- reCAPTCHA v3 — score-emulation v3 solver, 0.9 score guaranteed, enterprise support.
- Geetest v4 — slide, icon, gobang, AI, invisible, winlinze. Both full captcha_id flow and image gap detection.
- Management plane — balance, usage, API key rotation.
- MV3 Extension — auto-detects captchas on any page, solves in-place.
Quickstart #
Get your first solve in under 2 minutes. Three steps: create an account, grab an API key, make a request. We'll show all three languages side-by-side in the right panel — use the tabs to switch.
1. Create account & get API key
Go to visusolve.yorubacinemax.xyz/signup, sign up, and copy your key from Dashboard → API Keys. New accounts get a bonus key with 1000 free credits that expires after 30 days if unused. You can create unlimited additional keys per service/worker.
Your first 1000 solves are free with the bonus key. No credit card required. All endpoints available on free tier. Upgrade anytime in dashboard.
2. Install httpx (Python) or use fetch (JS)
Python: pip install httpx — 3.9+ recommended, async support built-in. Node.js 18+ has global fetch. Bun/Deno also work identically.
3. First real request — classify
Classify a reCAPTCHA v2 grid challenge. Replace sk_live_xxx with your real key. The right panel shows the full payload — base64 image can be raw or data-URI.
4. Handle dynamic refresh
Google sometimes sends a second grid after first selection (e.g. you selected 3 crosswalks, now it asks for new images with crosswalks again). Implement a loop: while response contains target tiles, click them in your automation (Selenium/Playwright) and call /classify again with new tiles. Max 3 iterations is safe; more looks bot-like.
5. Go production
- Add retry with exponential backoff on 429/500/503 (see Error Handling)
- Cache
website_keyparsing; don't scrape target HTML on every solve - Use
solving_idto correlate logs - Monitor balance via
GET /api/balancein a cron; alert if <100 credits
Testing with curl
All endpoints are curl-friendly. Use the cURL tab. For image heavy endpoints, save base64 to file and use $(cat file.b64).
Authentication #
All API requests are authenticated via API key. Keep keys secret—they have full access to your credits and solves.
Methods
You can authenticate using either header or query param (header preferred).
| Method | Example | Notes |
|---|---|---|
| X-API-Key header | X-API-Key: sk_live_abc123... | Recommended, not logged in CDN |
| ?key query | ?key=sk_live_abc123... | Fallback for browser img GETs |
| Authorization Bearer | Authorization: Bearer sk_live_... | Alias, accepted |
Key format
- Prefix
sk_live_for production,sk_test_reserved for future sandbox. - Length 48-64 chars after prefix, URL-safe alphanum.
- Example placeholder:
sk_live_4eC39HqLyjWDarjtT1zdp7dcEXAMPLE
Never expose keys in client-side JS. Use server-to-server calls. Rotate if committed to git. Enable IP allowlist in Dashboard → Security if available.
Getting a key
- Create account at
visusolve.yorubacinemax.xyz/signup - Verify email
- Dashboard → API Keys → Create Key → Copy
- Bonus key (1000 credits) auto-created; deletable after upgrade
SDKs & Libraries #
Official SDKs wrap the REST API with retry, backoff, and typed responses. Bring your own http client if you prefer.
| Language | Install | Status |
|---|---|---|
| Python | pip install visusolve (coming soon) / use httpx | snippet ready |
| JavaScript / Node | npm i visusolve (coming soon) / native fetch | snippet ready |
| Go | Community | planned |
| cURL | — | supported |
Until SDKs ship, use the cURL/Python/JS snippets in the right panel. They are production-grade with timeouts and error handling.
reCAPTCHA v2 — POST /classify #
Primary endpoint. Classifies a reCAPTCHA v2 image challenge grid: given images (base64) and target keyword like "Select all images with traffic lights", returns boolean array of which tiles match.
Request body (JSON)
| Param | Type | Required | Description |
|---|---|---|---|
| images required if no image_base64 | string[] (base64) | conditional | Array of base64-encoded image tiles. For 3×3 grid, 9 images. Can be raw base64 or data URI data:image/jpeg;base64,.... Max 16 images per request. |
| image_base64 | string (base64) | conditional | Single image containing the full grid (most clients use this). Server auto-splits by grid_size. |
| target_keyword required | string | yes | Challenge text like "traffic lights", "buses", "crosswalks", "fire hydrant", etc. Lowercased internally; partial match OK. |
| grid | enum | no | "1x1" (single image choice), "3x3" (default), "4x4". If omitted, inferred from images length. |
| grid_size | integer | no | Alias for grid: 3 = 3×3, 4 = 4×4. Precedence: grid > grid_size. |
| mode | enum | no | "fast" (default, ~150ms), "accurate" (~320ms, better on ambiguous). Use accurate for crosswalk / chimney. |
| solving_id | string | no | Client-side correlation ID for logs & support. Returned in X-Request-Id. |
| target_site | string | no | Domain hint for fine-tuned models (e.g. "google.com"). Optional. |
Response 200
| Field | Type | Description |
|---|---|---|
| success | boolean | True if inference succeeded |
| data | boolean[] | Length == grid count. True = tile contains target |
| confidence | float[] (optional) | Per-tile confidence 0-1 when mode=accurate |
| model_version | string | e.g. v2.1.0-onnx-fp16 |
| time_taken_ms | integer | Server inference time ms |
Some v2 challenges refresh after first selection. Call /classify again with new tiles. Best practice: implement loop with max 3 iterations; each iteration is 1 credit.
reCAPTCHA v2 — POST /solve (browser solver) #
Full browser automation solve. You provide website_key and website_url; server spins headless browser (optional proxy) and returns a valid g-recaptcha-response token ready to POST to target site.
| Param | Type | Required | Description |
|---|---|---|---|
| website_key required | string | yes | Data-sitekey from target page (e.g. 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-) |
| website_url required | string URL | yes | Full URL where captcha loads |
| timeout | integer (sec) | no | Max wait sec, default 60, max 120 |
| proxy | string | no | http://user:pass@host:port or socks5://... |
| is_invisible | boolean | no | true for invisible v2 |
Response
| Field | Type | Description |
|---|---|---|
| token | string | g-recaptcha-response token string |
| time_taken | float | sec |
| solved | boolean |
reCAPTCHA v2 — Code examples #
Copy-paste production snippets. Handle dynamic refresh by looping until no target tiles remain (google tracks behavior).
Best practices
- Always send
target_keywordlowercase, trimmed. - For base64 images, strip
data:image/...;base64,prefix server accepts both but raw is faster. - Retry once on 429/503 with exponential backoff.
- Cache sitekey → avoid re-parsing HTML each time.
reCAPTCHA v3 — POST /api/v3/solve #
Emulates v3 client behavior and returns a valid enterprise-grade token with high score (0.7–0.9). Token must be verified server-side by target within 2 minutes.
| Param | Type | Required | Description |
|---|---|---|---|
| website_key required | string | yes | sitekey |
| website_url required | string URL | yes | URL where v3 loads |
| action | string | no | grecaptcha action, e.g. "submit", "login", "homepage". Default "submit" |
| remote_ip | string IPv4 | no | End-user IP to tie token to (recommended for anti-abuse) |
| is_enterprise | boolean | no | Enterprise endpoint variant |
| proxy | string | no | Proxy URL |
Response
| Field | Type | Description |
|---|---|---|
| token | string | v3 token (JWT-like, ~400-800 chars) |
| score | float | Emulated score ~0.9 |
| action | string | Echoed action |
| time_taken | float sec | Total time |
| success | boolean |
reCAPTCHA v3 returns a score 0.0–1.0. VisuSolve emulates human-like browsing signals to achieve 0.7–0.9 consistently. If target requires 0.9+, set a realistic action matching frontend (not generic "submit") and pass remote_ip.
reCAPTCHA v3 — POST /api/v3/verify #
Verify a token returned from /solve before forwarding to your backend. Validates token shape, expiry, and optionally remote_ip binding.
| Param | Type | Required | Description |
|---|---|---|---|
| token required | string | yes | Token to verify |
| remote_ip | string | no | If token was bound to IP |
reCAPTCHA v3 — GET /api/v3/health #
Healthcheck for v3 solver cluster. Returns uptime, model version, avg latency.
Geetest v4 — POST /api/geetest/v4/solve #
Solves Geetest v4 challenges — slide, icon, gobang, AI, invisible, winlinze captcha. Provide captcha_id (gt + challenge combined or just captcha_id) and risk_type. Returns lot_number + captcha_output + etc required to pass verification.
| Param | Type | Required | Description |
|---|---|---|---|
| captcha_id required | string | yes | Geetest captcha_id, e.g. 54088bb07d2df3c46b79f80300b0ab6b or full gt+challenge JSON string |
| risk_type | enum | no | slide (default), gobang, icon, ai, invisible, winlinze. Auto-detect if omitted but slower. |
| proxy | string | no | Proxy URL |
| user_agent | string | no | Custom UA |
Response
| Field | Type | Description |
|---|---|---|
| lot_number | string | lot_number for verification payload |
| captcha_output | string | captcha_output |
| pass_token | string | pass_token |
| gen_time | string | gen_time |
| captcha_id | string | echoed |
Geetest v4 — POST /api/geetest/v4/solve-image #
Low-level image gap solver: you supply Geetest background + slice (puzzle piece) image URLs (or base64), we return gap x coordinate / slide distance. Use when you have already extracted images client-side.
| Param | Type | Required | Description |
|---|---|---|---|
| background_url | string URL / base64 | conditional | Background image URL or base64 |
| slice_url | string URL / base64 | conditional | Slice (puzzle piece) image |
| background_base64 | string base64 | alt | Alternative to background_url |
| slice_base64 | string base64 | alt | Alternative to slice_url |
| Response | Type | Description |
|---|---|---|
| gap | integer | x coordinate px from left |
| gap_ratio | float 0-1 | normalized x position |
| confidence | float | 0-1 |
Geetest v4 — GET /api/geetest/v4/health #
Healthcheck for Geetest solvers.
Management — GET /api/balance #
Returns credit balance, usage today, and bonus credits left.
Response
| Field | Type | Description |
|---|---|---|
| balance | integer | Remaining credits |
| bonus_remaining | integer | Free tier left |
| total_used | integer | Total lifetime solves |
| plan | string | free | starter | pro | enterprise |
Management — GET /api/usage #
Daily usage breakdown, per-endpoint counts, error rate, p50/p95 latency.
| Query | Type | Description |
|---|---|---|
| range | enum | 1d 7d 30d 90d default 7d |
| granularity | enum | hour day default day for 1d/7d, day for others |
Management — API Keys CRUD #
Programmatic key management. Bonus key cannot be deleted via API for safety—it requires dashboard confirmation.
Create key request
| Param | Type | Description |
|---|---|---|
| name | string | Label, e.g. "prod-api" |
| expires_in_days | integer | Optional TTL, default never |
| rate_limit_rpm | integer | Optional per-key RPM override |
Response on create
Only time full key is returned. Store securely immediately.
| Field | Type |
|---|---|
| id | uuid |
| key | string sk_live_... |
| name | string |
| created_at | ISO8601 |
Webhooks #
For long-running browser solves (/solve), optionally provide webhook_url to get async callback when token is ready instead of polling.
| Field | Type | Description |
|---|---|---|
| webhook_url | string URL https | Add to /solve or /api/v3/solve body. Server POSTs JSON with token on completion. |
| webhook_secret | string | Optional HMAC SHA256 signed in header X-Webhook-Signature |
Webhook payloads include event: "solve.completed", token, solving_id, time_taken. Verify signature: HMAC-SHA256(webhook_secret, raw_body) equals header X-Webhook-Signature.
Browser Extension (MV3) #
Official Chrome MV3 extension auto-detects reCAPTCHA v2/v3 and Geetest v4 on any page and solves in-place using your VisuSolve key. No code changes needed.
Install
- Download
extension.zipfrom VisuSolve. - Unzip locally:
unzip extension.zip -d visusolve-extension - Open
chrome://extensions, enable Developer mode (top right toggle). - Click Load unpacked → select
visusolve-extensionfolder. - Pin extension from toolbar puzzle icon.
- Click extension icon → paste your
sk_live_...key → Save.
How it works
- Content script observes DOM for
iframe[src*="recaptcha"],div.geetest_*,#g-recaptcha, etc. - On detection, extracts images / sitekey / url and sends to VisuSolve API via background worker.
- For v2 grid, overlays checkboxes auto-clicked; for token flow, fills
g-recaptcha-responseand dispatches callback. - Extension popup shows remaining balance, toggle per-site enable/disable, and auto-solve on/off.
- No browsing data leaves device except captcha payloads directly to
visusolve.yorubacinemax.xyz. No analytics.
Extension requests activeTab + storage + host permission for visusolve domain only on install, then optional <all_urls> after user approval for auto-detect. You can limit to specific sites in extension settings.
Error handling #
Standard HTTP status + consistent JSON error shape. Always check HTTP status before parsing body.
| Status | Meaning | Retry? |
|---|---|---|
| 400 | Bad request — missing param, invalid base64, unsupported grid | Fix request, no retry |
| 401 | Missing or invalid API key | No |
| 402 | Insufficient credits — balance exhausted | Top up or wait reset |
| 403 | Key revoked / IP blocked / bonus key expired | No |
| 429 | Rate limit exceeded (RPM per key or global) | Yes, with backoff, respect Retry-After |
| 500 | Server error — ONNX runtime failure | Once, after 1s |
| 503 | Service overloaded | Yes, after Retry-After |
Error response shape
Always JSON:
Rate limits #
Prevent abuse and ensure fairness. Limits are per-API-key, sliding window 60s.
| Tier | RPM | Credits | Concurr. |
|---|---|---|---|
| Bonus (Free) | 30 RPM | 1000 free | 2 |
| Starter | 120 RPM | 10k/mo | 10 |
| Pro | 600 RPM | 50k/mo | 40 |
| Enterprise | Unlimited* | Custom | Custom |
Credit costs
| Endpoint | Cost |
|---|---|
| POST /classify (v2 grid) | 1 credit |
| POST /api/geetest/v4/solve-image | 1 credit |
| POST /api/v3/solve | 2 credits |
| POST /api/geetest/v4/solve | 2 credits |
| POST /solve (browser) | 3 credits |
| Management GET /api/balance, /api/usage, /health | 0 credits |
Headers
| Header | Description |
|---|---|
| X-RateLimit-Remaining | Requests left in window |
| X-RateLimit-Reset | Unix epoch sec when window resets |
| Retry-After | Seconds to wait when 429/503 |
| X-Request-Id | Unique request ID for support |
Security best practices #
Treat API keys like passwords.
- Server-side only — never embed keys in frontend JS, mobile apps, or public repos.
- Env vars — load from
VISUSOLVE_API_KEYenv var or secrets manager, never hardcoded. - Rotate regularly — use POST /api/keys/{id}/regenerate quarterly or after personnel change.
- Per-service keys — create separate keys per service (e.g. prod-worker vs staging) so you can revoke one without downtime.
- Proxy through backend — if you must solve from browser, proxy through your backend that adds X-API-Key server-side.
- Monitor usage — set up alerts on sudden spike via /api/usage.
- Webhook sig — always verify X-Webhook-Signature if using callbacks.
Data handling
We do not log image payloads. Only metadata (grid size, keyword hash, time_taken, result count) kept for metrics 7 days. ONNX inference runs in isolated sandbox, no outbound network. GDPR compliant—no PII stored beyond account email.
Pricing #
Simple credit system. 1 solve ≈ 1 credit except browser & v3/geetest flows (see rate limits). Unused credits roll over 1 month on paid plans.
Free
Perfect to test
- 1,000 bonus solves
- v2 / v3 / Geetest v4
- 30 RPM
- No SLA
Starter
Side projects
- 10,000 solves/month
- All endpoints
- 120 RPM, 99.5% SLA
- Email support
Pro
Automation at scale
- 50,000 solves + rollover
- 600 RPM, priority queue
- 99.9% SLA, webhook, IP allow
- Discord + email priority
Enterprise: custom volume (500k+), dedicated ONNX pool, on-prem deploy option, audit logs, SSO. Contact sales@visusolve.
Integrations & Framework Adaptors #
VisuSolve integrates with popular automation stacks via lightweight adaptors. No heavy SDK — just drop-in helpers.
Puppeteer Extra
Use puppeteer-extra + puppeteer-extra-plugin-stealth to avoid detection. VisuSolve token injection works even with stealth.
| Stack | Recommended pattern | Credits |
|---|---|---|
| Selenium 4 grid | Extract tiles via execute_script canvas -> /classify -> Actions click | 1 per classify call |
| Playwright | frameLocator + evaluate canvas -> /classify, or /solve fallback | 1-3 |
| Puppeteer | page.$eval tile extraction -> /classify | 1 |
| Scrapy | Middleware: on captcha middleware pause -> /solve token -> retry request with token header | 2-3 |
| curl / cron | Use balance endpoint to gate; /health before batch | — |
Environment variables reference
| Var | Description |
|---|---|
VISUSOLVE_API_KEY | Your sk_live_... key |
VISUSOLVE_BASE_URL | Override base, default https://visusolve.yorubacinemax.xyz |
VISUSOLVE_TIMEOUT | Seconds, default 15 for classify |
VISUSOLVE_RETRIES | Default 2 |
Docker
For self-hosted Enterprise: docker run -p 8000:8000 visusolve/on-prem:latest exposes same API on localhost. License key via VISUSOLVE_LICENSE env. Health at :8000/health.
On-prem has no rate limit; you control concurrency via gunicorn workers --workers 4. Each worker holds one ONNX session (~1.2GB VRAM if GPU, 600MB RAM if CPU).
Models — accuracy, latency, versioning #
We version every model. Use model_version field in responses for debugging. Pinning not supported — you always get latest stable; changelog announces improvements.
| Model | Version | Latency p50 | Accuracy | Notes |
|---|---|---|---|---|
| v2 fast | v2.1.0-fp16 | ~142ms | 96.3% tile | 224px, vit-small |
| v2 accurate | v2.1.0-fp32-tta | ~320ms | 98.1% tile | 320px + flip TTA |
| v3 | v3.0.2 | ~870ms | 0.9 score 98% of time, 0.7+ 100% | enterprise + normal |
| geetest slide | gt4-slide-v1.3 | ~210ms image, ~1.2s full captcha_id | 99.1% gap <3px | ONNX+cv2 fallback |
| geetest icon | gt4-icon-v1.1 | ~380ms | 96.8% |
Evaluation methodology
Internal set: 12k v2 challenges scraped from google domains, balanced across 15 keywords (bus, car, traffic light, crosswalk, bicycle, fire hydrant, truck, motorcycle, boat, airplane, parking meter, bench, etc). Accuracy measured tile-level IoU >0.5 and end-to-end solve (checkbox checked). Sets rebuilt weekly to avoid overfit.
Legal & Compliance #
VisuSolve is designed for legitimate testing of your own services and automation where you have permission. You must comply with target site ToS.
- We do not condone spamming, scalping exclusive goods, or violating CFAA.
- All images are processed ephemerally — never stored, never used for retraining without explicit opt-in (Dashboard → Privacy).
- No PII in logs beyond hashed IP for rate limiting. Subprocessors: none (we host our own GPU fleet).
- DMCA / abuse: [email protected]
Rate Limits Deep Dive & Concurrency Control #
Understanding limits helps you avoid 429s and build adaptive concurrency.
Sliding window algorithm
We use Redis-backed sliding window per API key, 60s window. At 100% Pro RPM 600, you can burst 20 in first second, but remainder throttled. Use token bucket mental model: refill rate = RPM/60 per second.
Adaptive client example (pseudocode)
Start with semaphore = RPM. If 429 received, halve semaphore and sleep Retry-After. If 10 successes in row, increment semaphore by 1 until Max. This converges near limit without constant 429s.
Headers recap
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | Your max RPM (e.g. 600) |
| X-RateLimit-Remaining | Requests left in current 60s window |
| X-RateLimit-Reset | Unix sec when window resets, can use for precise sleep |
| Retry-After | Seconds to wait when 429/503, honor this |
| X-Request-Id | Unique id for support, include in tickets |
Credit vs rate limit
Rate limit is requests per minute, credit is lifetime / monthly usage. You can have credits but be rate limited if too fast. Also you can have 0 credits and well under rate limit — then 402.
Bonus key requests share a shared async queue that may delay 1-2s at peak. Upgrade Starter to skip queue (dedicated workers).
Testing & Sandbox #
No separate sandbox today: use bonus key for testing, 1000 credits enough for ~100 test solves. For unit tests, mock VisuSolve response: return { success: true, data: [true,false,...] } for /classify, and { token: "test_token_"+random } for /solve.
Fixtures for CI
- Save a base64 tile fixture set in repo (not the real challenge? use synthetic white images) — mock classify returns deterministic.
- Integration tests: if CI has VISUSOLVE_API_KEY, run 1 real /classify request with 1 tile grid 1x1 and keyword "bus" to assert success true and time_taken_ms <1000.
- For v3, test token shape: starts with 03AG or 03AF and length 400-1200.
FAQ #
is_enterprise: true. Enterprise uses different endpoint domain (recaptchaenterprise.googleapis.com) — our solver handles both.SDKs Deep Dive — patterns #
Until official SDKs ship, here are robust integration patterns for Python httpx and Node fetch that handle retries, timeouts, and logging idiomatically.
Python sync client pattern
Wrap VisuSolve in a class with tenacity/retry. Use httpx.Client to reuse TCP connections — important at high QPS.
Python async + asyncio.gather
For bulk solving, use httpx.AsyncClient + asyncio.Semaphore(limit=10) to parallelize while respecting rate limits. Example in right panel scales to ~6 solves/sec on Pro.
Node.js batch
Use p-limit for concurrency. Set fetch timeout via AbortController. Example: solve 100 v2 grids in parallel with 10 concurrency, total ~15s.
Playwright integration
Typical flow: page.goto(url) → page.evaluate -> extract base64 tiles via canvas → call VisuSolve → page.click() on matching tiles using checkbox selectors → loop.
Changelog #
We ship weekly model improvements. No breaking changes without major version bump.
| Version | Date | Notes |
|---|---|---|
| v2.1.0 | 2026-07-15 | Geetest v4 winlinze support, 12% latency cut on v2 fast mode, accuracy mode for crosswalk/chimney improves 1.8pp |
| v2.0.0 | 2026-06-20 | Brand rename Solvify→VisuSolve, base URL moved to visusolve.yorubacinemax.xyz (old URL 301), ONNX fp16, v3 enterprise |
| v1.9.2 | 2026-05-02 | Extension MV3 stable, auto-detect Geetest, popup balance refresh |
| v1.9.0 | 2026-04-10 | v3 solve GA, score 0.9 default, verify endpoint |
| v1.8.0 | 2026-03-01 | Geetest v4 GA (slide, icon, gobang, ai) |
Common Playbooks #
Playbook 1: Selenium + VisuSolve v2
- Driver finds
iframe[src*="recaptcha/api2/bframe"] - Extract
data-sitekeyvia JS - Wait for checkbox click → image challenge appears
- Scrape
.rc-imageselect-tileimages as base64 via canvas.toDataURL - Read instruction via
.rc-imageselect-instructions - POST /classify → get boolean mask → click tiles where true
- Click Verify, loop if new challenge, else extract token via
grecaptcha.getResponse()or via hidden textarea
Playbook 2: v3 token injection
- Frontend loads grecaptcha via script? Extract sitekey
- POST /api/v3/solve with sitekey + page url + action (find action in page JS, often
grecaptcha.execute(key, {action: 'submit'})) - Receive token, inject into form: POST your form with
g-recaptcha-response=TOKEN - Token valid ~110 sec, single use
Playbook 3: Geetest v4 from browser network tab
- Open target site with DevTools → Network tab → filter gcaptcha4
- Find request to
/load→ copycaptcha_id - POST /api/geetest/v4/solve with captcha_id → receive lot_number etc
- Call Geetest verify endpoint
/verifywith those fields + your challenge payload (docs on Geetest site)
Health & System — GET /health #
Global health check for the entire platform — model status, queue depth, and region. No auth required, suitable for uptime monitors and load-balancer health probes. Returns 200 even when degraded, check status field.
| Field | Type | Description |
|---|---|---|
| status | string | ok | degraded | down — degraded when queue >50 or latency p95 >800ms |
| uptime_sec | integer | Seconds since last deploy, resets on rolling deploys |
| models | object | Per-model status: v2, v3, geetest_v4 with version, latency p50/p95, queue depth, healthy bool |
| region | string | Current serving region: us-east-1, eu-west-1, ap-southeast-1 (auto-routed via latency) |
| version | string | API version e.g. v2.1.0 |
Recommended monitors
- Ping /health every 30s from your uptime checker (BetterUptime, StatusCake)
- Alert if status != ok for >2 minutes
- For Pro, monitor X-RateLimit-Remaining drop <10% as early warning to auto-buy more credits webhook (future)
Concurrency & Performance Tips #
VisuSolve is built for throughput. Median /classify latency is <400ms, p95 <650ms, p99 <950ms on Pro. Here is how to squeeze max QPS without 429s.
- Reuse connections — Use httpx.Client / fetch with keep-alive. TCP+TLS handshake is ~80ms, inference is ~140ms, so reuse saves 36%.
- Batch vs parallel — /classify accepts up to 16 images array; if you have 3x3 grid, single request is cheaper than 9 separate. For multiple pages, parallelize across pages with semaphore.
- Model warm — First request after idle may be ~30% slower due to ONNX session cold start. Send a dummy request on app boot to warm. Example: POST with 1x1 white pixel and keyword "bus" — costs 1 credit but warms model worker.
- mode=fast vs accurate — fast uses FP16 224px input tensor, accurate uses FP32 320px + TTA (horizontal flip). Use fast unless tile class is crosswalk, chimney, or traffic light in fog/night — those +1.8pp better in accurate.
- Timeout tuning — Set client timeout = server expected + 2s. For /classify: 10s client; for /solve: 90s; v3: 40s; geetest slide: 30s.
- Async bulk — Use httpx.AsyncClient + asyncio.Semaphore(10-40 depending on plan) to saturate your RPM without 429s. See right panel bulk example.
- Proxy hint — For /solve browser flow, pass fastest proxy you can; solver's headless chromium will use it for target site. If no proxy, solver uses datacenter egress which some sites block.
Latency breakdown (typical /classify)
| Phase | Time | Notes |
|---|---|---|
| TLS handshake (if new conn) | 60-120ms | Eliminate via Client reuse |
| Image decode + resize | 8-15ms | Server side |
| ONNX inference (fast) | 90-160ms | GPU shared, FP16 |
| Post-process + JSON ser | 4-8ms | |
| Total p50 | ~140ms | With keep-alive |
Automation — Selenium, Playwright, Puppeteer #
Complete end-to-end examples are in the right panel (see advanced client + Playwright groups). Key ideas for each framework:
Selenium 4
- Use
driver.execute_scriptto extract tiles via canvas (avoid screenshot cropping offset bugs especially on HiDPI). - Click tiles using Actions API move_to_element with pause(80-150ms) to look human; Google tracks mouse trajectory for some challenges.
- Sometimes checkbox re-renders after click — wait for
.rc-imageselect-dynamic-selectedcount to stabilize before clicking Verify. - Disable
dom.webdriver.enabledpref or use undetected-chromedriver / selenium-stealth. - Always set realistic
navigator.userAgentand languages — not headless default.
Playwright (recommended)
- Preferred for reliability + auto-wait. Use
page.locator+frameLocatorfor bframe — outer page & checkbox are separate iframes. - For base64, use Playwright's
locator.evaluate(el => canvas)method — see right snippet. Do NOT use screenshot as it may be throttled by target's CSP. - Enable
--disable-blink-features=AutomationControlledand real user-agent; VisuSolve does not need that but target site does. - Playwright trace viewer useful for debugging dynamic refresh loops.
Puppeteer
Similar to Playwright; use page.$eval to pull images. Remember to await page.waitForTimeout after each tile click to let Google validate (300-600ms). Enable stealth plugin puppeteer-extra-plugin-stealth.
VisuSolve solves the image challenge, but target site may have additional bot detection (Cloudflare, DataDome, Imperva). Use a residential proxy for /solve browser flow if you get 403 on target even with valid token. Token validation happens at target site, not ours.
Dynamic challenge loop detail
Google may refresh tiles after you click some. Logic:
- Call /classify with current tiles → get mask → click true tiles.
- Wait 400ms; check if new images loaded (compare image src or use MutationObserver via evaluate).
- If new images, repeat from step 1 with only new tiles (some tutorials require re-classifying whole grid — safe to just classify whole 3x3 again).
- If no new images after Verify, check for checkbox checked or new challenge. Max 4 rounds.
- If still unsolved, fall back to /solve browser API and request a fresh token (costs 3 credits but saves manual loops).
Troubleshooting #
Common issues and fixes. Always include X-Request-Id from response headers when contacting support.
| Symptom | Cause | Fix |
|---|---|---|
| 401 invalid api key / unauthorized | Copy paste error, extra newline/space, or bonus key deleted/expired | Regenerate key in dashboard, strip whitespace, load from env var VISUSOLVE_API_KEY, ensure header name X-API-Key exact |
| 402 insufficient credits (error insufficient_credits) | Balance zero, bonus expired, or monthly reset hit | Check GET /api/balance, dashboard → Billing → Top up. 402 also returned if per-day burst limit exceeded on Free |
| Classify returns all false for traffic lights | Wrong target_keyword e.g. "traffic light" vs "traffic lights" or language mismatch (German etc) | Use exact keyword lowercased matching challenge text; model supports EN, DE, FR, ES, PT, ZH auto-translated internally but pass EN for best |
| v3 token rejected by target site with "invalid-input-response" | Action mismatch, token expired (>120s), or IP mismatch when target reuses remoteip | Ensure action equals page's grecaptcha.execute action string, submit within 110s, pass same remote_ip as user if target validates it |
| Geetest solve fails with invalid captcha_id | Wrong captcha_id format — often grabbing gt challenge instead of captcha_id from /load | Open Network tab, filter gcaptcha4.geetest.com/load, copy captcha_id query param (32 hex chars), not gt file |
| First request slow ~800ms, next 150ms | Model cold start after 15m idle on Free tier | Warm on app start, or upgrade Starter to keep worker warm 24/7 |
| Dynamic refresh loops forever / never becomes checked | Google sends new challenges if prior selection wrong or mouse path flagged | Max 3 rounds, then call /solve browser API fallback; add random delays 120-300ms between tile clicks; use Playwright not bare httpx for click simulation |
| 403 from target even with valid token | Target site has extra WAF / Cloudflare / bot detection beyond captcha | Use residential proxy, realistic UA, and ensure cookies from target site accompany token submit request |
Run GET /health, copy X-Request-Id from failing request, and email both to [email protected] with your keyword + grid size. We reply within 4h on Pro.
Support & SLA #
Pro plan includes 99.9% monthly uptime SLA. Credits refunded pro-rata on downtime >0.1%. Enterprise includes dedicated Slack channel + 1h initial response + 4h resolution target.
- Email: [email protected] — include X-Request-Id
- Dashboard logs: every request logs time_taken, model_version, balance remaining — searchable by solving_id
- Discord: link in dashboard footer (invite-only for Pro+ customers)
- Status page: /health — bookmark for monitors, supports HEAD as well
- Changelog: see section above — no breaking API changes without major version bump (v2 → v3 etc)
Data retention & privacy
We do not retain base64 images. Only hashed keyword + grid size + latency + result count stored 7 days for metrics. ONNX inference runs in gVisor sandboxed container, no outbound network allowed. GDPR: we store only email + hashed IP for rate limit. DPA available on request for Enterprise.
Webhook Signature Verification — Detailed #
When you configure webhook_url in /solve or /api/v3/solve, VisuSolve POSTs JSON to your endpoint within 100ms of solve completion. Verify HMAC SHA256 to prevent spoofing.
Headers
| Header | Example |
|---|---|
| X-Webhook-Signature | sha256=5d9c... |
| X-Request-Id | req_... |
| Content-Type | application/json |
| User-Agent | VisuSolve-Webhooks/2.1 |
Payload shape
| Field | Type |
|---|---|
| event | "solve.completed" | "solve.failed" |
| token | string (when completed) |
| solving_id | string client correlation id |
| time_taken | float sec |
| error | string when failed |
Verification in Python:
Compare constant-time (hmac.compare_digest). If signature missing or mismatch, respond 401.
Retry policy
Webhooks retried 3 times with exponential backoff 2s, 8s, 32s if your endpoint returns non-2xx or timeout >5s. After 3 failures, solve result stored 10min and can be fetched via management API using solving_id (future).
More Examples — Scrapy, Bulk, Error Handling #
Scrapy middleware
Intercept captcha detection in downloader middleware, pause request, solve, inject token, return request.
Retry decorator pattern
Use tenacity library: @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10), retry=retry_if_exception_type((httpx.HTTPStatusError,))). Handles 429/503 by waiting Retry-After.
Logging structured
Always log X-Request-Id, solving_id, endpoint, time_taken_ms for post-mortem. VisuSolve dashboard logs provide server-side view; client logs complement.
Cost calculator
Estimate monthly cost: solves per day * 30 * avg credits per solve * price per credit. Example: 1000 solves/day v2 fast = 1000*30=30k credits = fits Pro $49 (50k). v3 double cost: 15k v3 solves = 30k credits = same bucket.
Migration — from 2Captcha / AntiCaptcha #
Migrating from human farms? Map their API to ours:
| 2Captcha field | VisuSolve equivalent | Notes |
|---|---|---|
| method=userrecaptcha | POST /classify + your own clicker, or POST /solve for token | We give you boolean mask, you click; /solve gives token directly like 2captcha |
| googlekey + pageurl | website_key + website_url | Same meaning |
| res.php?action=get | Sync POST returns immediately, no poll — if using /solve webhook mode, use callback instead of polling | No polling needed, faster |
| proxy + proxytype | proxy field http://user:pass@host:port | Unified format |
Benefits: 10x faster median latency, no variable queue, cheaper at scale (Pro $49/50k vs human farms $2/1000 = $100/50k), no ethical concerns.
© 2026 VisuSolve • visusolve.yorubacinemax.xyz • docs v2.1.0 • Base URL https://visusolve.yorubacinemax.xyz • All examples use placeholders sk_live_...EXAMPLE, never real keys.