CapSkip Python SDK — Captcha Solver for Python
Solve reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, GeeTest, ALTCHA and image captchas from Python.
Official Python client for CapSkip, a local captcha solver that runs on your own machine. Licensed once, not billed per solve.
pip install capskip
How it works
CapSkip is a desktop app. It does the solving on your machine and exposes the standard captcha-solver HTTP API — the same in.php / res.php endpoints every 2captcha-compatible client already speaks — on 127.0.0.1:8080.
This SDK is a thin wrapper over that API, with the method names you would expect: normal(), recaptcha(), turnstile(), geetest(), altcha(). Nothing leaves your network, and there is no credit balance to keep an eye on.
Supported captcha types
| Captcha | Method |
|---|---|
| Image captcha solver (distorted text / OCR) | solver.normal(file) |
| reCAPTCHA v2 solver (checkbox) | solver.recaptcha(sitekey, url) |
| reCAPTCHA v2 invisible solver | solver.recaptcha(..., invisible=1) |
| reCAPTCHA Enterprise solver | solver.recaptcha(..., enterprise=1) |
| reCAPTCHA v3 solver | solver.recaptcha(..., version="v3", action="submit") |
| reCAPTCHA v3 Enterprise | solver.recaptcha(..., version="v3", enterprise=1) |
| Cloudflare Turnstile solver (widget) | solver.turnstile(sitekey, url) |
| Cloudflare Turnstile (challenge page) | solver.turnstile(..., data=..., pagedata=...) |
| GeeTest v3 solver (slide puzzle) | solver.geetest(gt, challenge, url) |
| ALTCHA solver (proof-of-work) | solver.altcha(url, challenge_url=...) |
hCaptcha and FunCaptcha/Arkose are not supported. hCaptcha is the one people misidentify most often, since it also puts a data-sitekey on the widget — check for class="h-captcha" or a js.hcaptcha.com script before reaching for recaptcha().
Point the SDK at the live captcha demo pages to sanity-check your setup against real widgets.
Quick start (5 minutes)
1. Install the CapSkip captcha solver
Download and run the CapSkip desktop app — CapSkipInstaller.msi. Leave it running in the background.
In CapSkip settings, note:
- API port (default:
8080) - API key (optional — if validation is disabled, any string works)
2. Install the SDK
pip install capskip
Or from source:
git clone https://github.com/capskip/capskip-python.git
cd capskip-python
pip install -e .
3. Solve your first captcha
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
result = solver.recaptcha(
sitekey="YOUR_SITEKEY",
url="https://example.com/page-with-recaptcha",
)
print(result["code"]) # g-recaptcha-response token
Prerequisite: CapSkip must be running before you call the SDK. If you see a connection error, see Troubleshooting.
Why solve captchas locally
Cloud captcha APIs charge per solve, which turns a retry loop into an expense and routes every page URL and sitekey you touch through someone else's queue.
CapSkip flips that around:
- Unlimited solving — one license, no per-captcha charge, no balance to top up
- Runs on
127.0.0.1— the SDK never talks to a third-party server - No per-key rate limit — throughput is whatever your machine can manage
- Fast — image captchas come back in well under a second; a typical reCAPTCHA v2 lands in 30–45 seconds
Coming from 2captcha or Anti-Captcha
CapSkip answers on the same in.php / res.php endpoints, so it works as a 2captcha API alternative: an existing integration usually needs nothing more than its host pointed at 127.0.0.1:8080. The migration notes cover the details, if you would rather keep your current client library than switch to this one.
Documentation
| Guide | Description |
|---|---|
| Tutorial | Complete walkthrough of every captcha type, sync and async |
| Getting Started | Full setup: CapSkip app, SDK install, first script |
| API Reference | All classes, methods, parameters, and return values |
| Examples | Ready-to-run scripts for every captcha type |
| Troubleshooting | Connection errors, timeouts, proxy issues |
| Contributing | Development setup, tests, pull requests |
| Changelog | Release history |
Configuration
from capskip import CapSkip
solver = CapSkip(
apiKey="capskip", # your CapSkip API key (or any string if validation is off)
host="127.0.0.1", # CapSkip host
port=8080, # CapSkip port from app settings
defaultTimeout=120, # seconds — image captcha polling timeout
recaptchaTimeout=300, # seconds — reCAPTCHA / Turnstile / GeeTest polling timeout
# (ALTCHA uses defaultTimeout — it is CPU work, not a browser solve)
pollingInterval=5, # max seconds between res.php polls (starts at 0.25s, backs off to this)
)
Use environment variables in production:
# Linux / macOS
export CAPSKIP_API_KEY="your-key"
export CAPSKIP_HOST="127.0.0.1"
export CAPSKIP_PORT="8080"
# Windows PowerShell
$env:CAPSKIP_API_KEY = "your-key"
$env:CAPSKIP_HOST = "127.0.0.1"
$env:CAPSKIP_PORT = "8080"
import os
from capskip import CapSkip
solver = CapSkip(
apiKey=os.getenv("CAPSKIP_API_KEY", "capskip"),
host=os.getenv("CAPSKIP_HOST", "127.0.0.1"),
port=int(os.getenv("CAPSKIP_PORT", "8080")),
)
Usage examples
Image captcha
result = solver.normal("captcha.png")
result = solver.normal("https://example.com/captcha.jpg")
result = solver.normal("data:image/png;base64,iVBORw0KGgo...")
print(result["code"])
reCAPTCHA v2 / v3
# reCAPTCHA v2
result = solver.recaptcha(sitekey="...", url="https://example.com")
# reCAPTCHA v3
result = solver.recaptcha(
sitekey="...",
url="https://example.com",
version="v3",
action="submit",
score=0.7,
)
Cloudflare Turnstile
result = solver.turnstile(
sitekey="0x4AAAAAAA...",
url="https://example.com",
)
GeeTest v3
gt is static per site, but challenge is single-use and expires in about a
minute — fetch a fresh pair right before solving.
result = solver.geetest(
gt="81388ea1fc187e0c335c0a8907ff2625",
challenge="7cf6a8b1a2c34d5e6f7089abcdef0123",
url="https://example.com/login",
)
# Post these back exactly as the site's own front-end would
result["challenge"], result["validate"], result["seccode"]
ALTCHA
ALTCHA is proof-of-work, not recognition — there is nothing to read, so a solve is deterministic and takes milliseconds. Give CapSkip the endpoint that serves the challenge, or the challenge document itself.
result = solver.altcha(
url="https://example.com/signup",
challenge_url="https://example.com/captcha/api/altcha/challenge",
)
# Post this back in the form field the widget uses, named `altcha`
result["token"]
Challenges expire fast — some sites inside two minutes — so fetch one immediately before solving and submit the token promptly.
With a proxy (reCAPTCHA, Turnstile, GeeTest & ALTCHA only)
# Proxy is not supported for image captcha
result = solver.recaptcha(
sitekey="...",
url="https://example.com",
proxy={"type": "HTTPS", "uri": "user:pass@1.2.3.4:3128"},
)
result = solver.turnstile(
sitekey="...",
url="https://example.com",
proxy={"type": "HTTP", "uri": "1.2.3.4:3128"},
)
Async (parallel solving)
import asyncio
from capskip import AsyncCapSkip
async def main():
solver = AsyncCapSkip()
r1, r2 = await asyncio.gather(
solver.recaptcha(sitekey="...", url="https://a.com"),
solver.turnstile(sitekey="...", url="https://b.com"),
)
print(r1["code"], r2["code"])
asyncio.run(main())
More examples: examples/
Selenium, Playwright and Scrapy
The SDK hands back a token; your existing browser tooling does the driving. The shape is the same whichever you use:
- Read the sitekey off the page (
data-sitekey, or the widget's config object). - Call the matching solve method with that sitekey and the page URL.
- Write the token into the response field and submit.
Selenium
sitekey = driver.find_element(By.CSS_SELECTOR, "[data-sitekey]").get_attribute("data-sitekey")
token = solver.recaptcha(sitekey=sitekey, url=driver.current_url)["code"]
driver.execute_script(
"document.getElementById('g-recaptcha-response').value = arguments[0];", token
)
driver.find_element(By.CSS_SELECTOR, "form").submit()
Playwright
from capskip import AsyncCapSkip
solver = AsyncCapSkip()
sitekey = await page.get_attribute("[data-sitekey]", "data-sitekey")
result = await solver.recaptcha(sitekey=sitekey, url=page.url)
await page.evaluate(
"t => document.getElementById('g-recaptcha-response').value = t", result["code"]
)
Scrapy and plain requests work the same way — solve first, then send the token as whatever form field the site expects. Longer walkthroughs: Selenium and Playwright.
Return value
Every solve method returns:
{
"captchaId": "12345", # internal ID from CapSkip
"code": "TOKEN_OR_TEXT" # solution — text for image, token for reCAPTCHA/Turnstile
"userAgent": "..." # Turnstile only — use when submitting challenge-page tokens
}
GeeTest additionally expands its answer into challenge, validate, and
seccode, while code keeps the raw JSON string. ALTCHA adds token (the same
string as code) and number, the counter that solved it.
Error handling
from capskip import CapSkip, ValidationException, NetworkException, ApiException, TimeoutException
try:
result = solver.recaptcha(sitekey="...", url="...")
except ValidationException:
pass # invalid parameters
except NetworkException:
pass # CapSkip not running, or captcha not ready (manual polling)
except ApiException:
pass # API returned an error code
except TimeoutException:
pass # polling timeout exceeded
FAQ
How do I solve a captcha in Python?
Install the CapSkip desktop app, pip install capskip, then call the method that matches the widget — recaptcha(), turnstile(), geetest(), altcha() or normal(). Each one polls until CapSkip has an answer, then returns a token, or the recognized text in the case of an image captcha.
Is this a free captcha solver?
The SDK itself is MIT-licensed and free. Solving needs the CapSkip app, which is bought once rather than metered per captcha, so your cost stops scaling with volume.
Which captchas can it solve?
reCAPTCHA v2 (checkbox and invisible), reCAPTCHA v3, reCAPTCHA Enterprise, Cloudflare Turnstile, GeeTest v3, ALTCHA, and image/text captchas. Not hCaptcha, and not FunCaptcha/Arkose.
Does it work with Selenium and Playwright?
Yes — see above. The SDK never touches a browser itself, so it drops into whatever stack you already have, Scrapy and plain requests included.
Why is my reCAPTCHA v3 score low?
Google derives v3 scores from IP reputation, cookies and browsing history. A solver returns a valid token, but it cannot change how Google grades that token — score= is forwarded as the target you want, not a guarantee. If a site enforces a high threshold, solve through a cleaner IP using the proxy argument.
Can I use it as a 2captcha alternative?
Yes. CapSkip serves the same endpoints, so you can either move to this SDK or repoint an existing 2captcha client at 127.0.0.1:8080.
Does the captcha have to be on a public page?
For widget captchas, yes — CapSkip loads the URL you pass it. Image captchas only need the image, and that can be a local file.
Is asyncio supported?
AsyncCapSkip is a full async client. Use it with asyncio.gather to run several solves at once.
Development
git clone https://github.com/capskip/capskip-python.git
cd capskip-python
python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux / macOS
source .venv/bin/activate
pip install -e ".[dev]"
pytest
See CONTRIBUTING.md for the full development workflow.
Links
- CapSkip — local captcha solver · download the app
- Captcha demo pages — live reCAPTCHA, Turnstile, GeeTest and image widgets
- Python captcha solver guide
- HTTP API docs
- Other clients: Node.js · PHP · .NET · MCP server for AI agents
- PyPI package · report an issue
License
MIT — see LICENSE.
Metadata
Release files for capskip 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| capskip-1.2.0.tar.gz | 59.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| capskip-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 78.6 kB
Release files / capskip-1.2.0.tar.gz
| Download URL | capskip-1.2.0.tar.gz |
|---|---|
| Size | 59.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ea3ff609e53d67a667dc81bc67bf53ea80b3c4b53de948ab5a8032f1571ae538
|
|
BLAKE2b-256 checksum How to use checksums |
872dc50f67037e9e0c082087f738409a7fa1a6cc77b5a1789f410ade4e85fb27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logRelease files / capskip-1.2.0-py3-none-any.whl
| Download URL | capskip-1.2.0-py3-none-any.whl |
|---|---|
| Size | 19.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df4363b8238a5327805c90bea12f6e5718293152e550257043e9932ec06e6c1c
|
|
BLAKE2b-256 checksum How to use checksums |
b1024b061d5f81b041b0b380f6da434ebbcfd8cb3a26eeb0379fdd362f99a7a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency log