How to Solve reCAPTCHA v2 in Python With the CapSkip SDK

reCAPTCHA v2 has three variants, and in Python they are one function with different keyword arguments. Checkbox is the bare call, Invisible and Enterprise are flags, and both can be set at once. Python is also the only CapSkip SDK with a genuinely separate async client, which matters as soon as you are solving more than one at a time.
Setup
# Python 3.10 or newer. pip install capskip
CapSkip solves locally, so start the desktop app first, then point the client at the port from its settings:
from capskip import CapSkip
solver = CapSkip(
apiKey="capskip", # any string when key validation is off
host="127.0.0.1",
port=8080,
recaptchaTimeout=300, # seconds
)In production, read those from the environment instead:
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")),
)The three variants
| Variant | Keyword to add |
|---|---|
| Checkbox | none |
| Invisible | invisible=1 |
| Enterprise | enterprise=1 |
| Invisible Enterprise | both |
# Checkbox: the baseline call.
result = solver.recaptcha(
sitekey="6Lc...YOUR_SITEKEY",
url="https://example.com/login",
)
print(result["code"]) # g-recaptcha-response token
# Invisible.
result = solver.recaptcha(sitekey=sitekey, url=page_url, invisible=1)
# Enterprise, and both together.
result = solver.recaptcha(sitekey=sitekey, url=page_url, enterprise=1)
result = solver.recaptcha(sitekey=sitekey, url=page_url, enterprise=1, invisible=1)The return value is a plain dict, so result["code"] is the token. There is also result["captchaId"] if you want to log which internal solve produced it.
Getting the sitekey and URL right
The sitekey is the data-sitekey attribute on the widget container, or the first argument to grecaptcha.render when there is no container. It always begins with 6L and is public.
The URL has to be the page the widget renders on. Passing your form handler or a post-login redirect is the most common cause of a token that solves fine and then fails validation.
Submitting the token
import requests
response = requests.post(
"https://example.com/login",
data={
"g-recaptcha-response": result["code"],
"username": "...",
"password": "...",
},
)Tokens last around two minutes and are single use, so solve as late in the flow as you can. If the site hands the token to a JavaScript callback instead of a form field, the solve is identical but submission differs, which our reCAPTCHA v2 callback solver page covers.
Solving several at once
This is where Python differs from the other CapSkip SDKs. AsyncCapSkip is a real async implementation, not an alias, so it genuinely overlaps solves:
import asyncio
from capskip import AsyncCapSkip
async def main():
solver = AsyncCapSkip()
r1, r2 = await asyncio.gather(
solver.recaptcha(sitekey=key_a, url="https://a.example.com"),
solver.recaptcha(sitekey=key_b, url="https://b.example.com"),
)
print(r1["code"], r2["code"])
asyncio.run(main())In the Node.js and .NET SDKs AsyncCapSkip is only an alias, and in PHP it is there purely so ported code compiles. Python is the one where switching to it actually changes behaviour.
Errors
from capskip import (
CapSkip, ValidationException, NetworkException,
ApiException, TimeoutException,
)
try:
result = solver.recaptcha(sitekey=sitekey, url=page_url)
except ValidationException:
pass # missing or malformed arguments
except NetworkException:
pass # CapSkip is not running
except ApiException:
pass # bad sitekey or pageurl
except TimeoutException:
pass # exceeded recaptchaTimeoutAll four derive from a common base, so except CapSkipError catches everything if you would rather handle failures in one place.
Using a proxy
result = solver.recaptcha(
sitekey=sitekey,
url=page_url,
proxy={"type": "HTTPS", "uri": "user:[email protected]:3128"},
)Proxies work for reCAPTCHA, Turnstile and GeeTest. They are not supported for image CAPTCHAs, which are solved from the image bytes and never reach the target site.
Frequently asked questions
Do I need to poll for the result?
No. The SDK polls internally and returns the finished token, so CAPCHA_NOT_READY never reaches your code. It starts checking after 250ms and backs off, which usually beats a hand-written loop.
Is AsyncCapSkip worth using for a single solve?
Not really. It matters when several solves overlap, or when you are already inside an event loop and a blocking call would stall it. For one solve in a script the sync client is simpler.
Which Python versions are supported?
3.10 and newer. The package has no heavy dependencies, so it drops into an existing scraping or automation project without pulling a tree of extras.
Summary
One function, three variants, selected with invisible and enterprise. Read result["code"], submit it as g-recaptcha-response, and reach for AsyncCapSkip when you are solving in bulk.
The wider Python surface is on the Python CAPTCHA solver page, other languages on the reCAPTCHA v2 solver page, and there is a live v2 demo to test against. CapSkip is a captcha solver that runs on your own machine.
