This guide walks through automating the 2Captcha service to obtain a solution token for Google's reCAPTCHA v2 ("I'm not a robot"). Over the years Google has steadily made its behaviour-based reCAPTCHA harder for automated clients to clear on their own. 2Captcha works around this by handing the challenge off to a solving back end and returning a usable token, which your script then submits to the target page exactly as a browser would.
Everything below can be done with plain HTTP requests — no headless browser or browser automation is required — using nothing more than Python's requests library. We'll cover both the low-level HTTP approach (so you understand what's actually happening on the wire) and the cleaner, recommended approach using the official 2captcha-python client.
A note on responsible use. CAPTCHA-solving services exist for legitimate purposes such as QA automation, accessibility tooling, and gathering publicly available data at scale. Always respect a site's Terms of Service, its
robots.txt, applicable laws, and rate limits. Don't use any of this to abuse a service or to defeat protections you have no right to bypass.
How the service works
The end-to-end flow is short and always the same, regardless of language:
- Collect the page's reCAPTCHA parameters. You need the site's public reCAPTCHA key (the
data-sitekeyattribute) and the full URL of the page where the challenge appears. A proxy IP is optional. - Submit those parameters to 2Captcha. The service queues the task and returns a task/CAPTCHA ID.
- Poll for the result. After roughly 10–30 seconds, the back end has solved the challenge and Google has issued a valid token. You request that token by ID.
- Use the token. You place the returned
g-recaptcha-responsetoken into the target page's form (or pass it to its callback) and submit, just as a real user's browser would.
The official Python and API documentation for reCAPTCHA v2 lives at 2captcha.com/api-docs/recaptcha-v2.
Step 1 — Get your API key
Sign in to your 2Captcha account and open the account settings page. Your API key is a 32-character string that authenticates every request you send, for example 1abc234de56fab7c89012d34e56fa7b8. Keep it secret — treat it like a password and never commit it to source control. A good practice is to read it from an environment variable rather than hard-coding it:
import os
service_key = os.environ["TWOCAPTCHA_API_KEY"]
Step 2 — Find the site key (data-sitekey)
The site key is a public value that Google issues to each website. It's stable for a given site, so you only need to find it once.
Open the target page in your browser and launch developer tools (F12, or right-click → Inspect). Search the page's HTML for the reCAPTCHA container, which usually looks like one of these:
<div class="g-recaptcha" data-sitekey="6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u"></div>
If the page uses the JavaScript-rendered widget, you can instead look in the Network tab for a request to a URL beginning with www.google.com/recaptcha/api2/anchor and read the k parameter from its query string — that value is the same site key.
Copy the value of data-sitekey (or of k). That string is the googlekey parameter you'll pass to 2Captcha.
The other parameter you'll need is simply the full address of the page hosting the CAPTCHA, e.g. https://example.com/page-with-recaptcha.
Approach A — The official Python client (recommended)
The simplest, most maintainable way to integrate is the official library, which handles submission, polling, timeouts, and error handling for you.
Install it from PyPI:
pip3 install 2captcha-python
(Source and full examples: github.com/2captcha/2captcha-python.)
A complete reCAPTCHA v2 solve then looks like this:
import os
from twocaptcha import TwoCaptcha
solver = TwoCaptcha(os.environ["TWOCAPTCHA_API_KEY"])
try:
result = solver.recaptcha(
sitekey="6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u",
url="https://example.com/page-with-recaptcha",
)
except Exception as error:
print("Could not solve the captcha:", error)
else:
token = result["code"]
print("Token:", token)
result["code"] is the g-recaptcha-response token you submit to the target page (see Step 5). The library polls for you, so there's no manual wait loop to write.
Approach B — Raw HTTP requests
If you'd rather call the service directly — to understand the protocol, or to avoid an extra dependency — you can talk to the legacy in.php / res.php endpoints with requests. Make sure requests is installed:
pip3 install requests
Step 3 — Submit the CAPTCHA for solving
Send a GET (or POST) request to https://2captcha.com/in.php with these parameters:
| Parameter | Value |
|---|---|
key |
your 2Captcha API key |
method |
userrecaptcha |
googlekey |
the data-sitekey value you found in Step 2 |
pageurl |
the full URL of the page that contains the reCAPTCHA |
json |
1 (recommended — returns a clean JSON response) |
import requests
service_key = "YOUR_API_KEY"
google_site_key = "6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u"
page_url = "https://example.com/page-with-recaptcha"
submit_response = requests.get(
"https://2captcha.com/in.php",
params={
"key": service_key,
"method": "userrecaptcha",
"googlekey": google_site_key,
"pageurl": page_url,
"json": 1,
},
).json()
if submit_response.get("status") != 1:
raise SystemExit(f"Submission failed: {submit_response.get('request')}")
captcha_id = submit_response["request"]
print("Captcha ID:", captcha_id)
With json=1, a successful response is {"status": 1, "request": "<CAPTCHA_ID>"}, where CAPTCHA_ID identifies the task inside the service. If status is 0, the request field holds an error code you can look up in the error-codes reference.
Step 4 — Poll for the token
A worker doesn't solve the challenge instantly, so you poll https://2captcha.com/res.php until the token is ready. Wait a few seconds between attempts to avoid hammering the API:
from time import sleep
token = None
for _ in range(20):
sleep(5) # give the service time to solve
poll_response = requests.get(
"https://2captcha.com/res.php",
params={
"key": service_key,
"action": "get",
"id": captcha_id,
"json": 1,
},
).json()
if poll_response.get("status") == 1:
token = poll_response["request"]
break
if poll_response.get("request") != "CAPCHA_NOT_READY":
raise SystemExit(f"Solving error: {poll_response.get('request')}")
if token is None:
raise SystemExit("Timed out waiting for the captcha token.")
print("g-recaptcha-response token:", token)
While the task is still in progress the service returns CAPCHA_NOT_READY; once it's done you get {"status": 1, "request": "<TOKEN>"}.
Step 5 — Submit the token to the target form
Once you have the token, you use it like a browser would: place it in the form field named g-recaptcha-response and send the form's request to the server.
submit_url = "https://example.com/page-with-recaptcha"
headers = {
"user-agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
}
payload = {
"submit": "submit",
"g-recaptcha-response": token,
# add any other fields the form requires
}
final_response = requests.post(submit_url, headers=headers, data=payload)
print(final_response.text)
The exact field names and method (GET vs POST) depend on the target form, so inspect the request the page sends in your browser's Network tab and mirror it. Some pages don't have a visible submit button and instead run a JavaScript callback when the challenge is solved; in that case, find the callback (often named in a data-callback attribute or passed to grecaptcha.render) and trigger the same request it would have made.
How the token is verified (server side)
It helps to understand what happens after you submit the token, because it explains why a freshly issued token matters. On the receiving end, the target server passes the token to Google's verification endpoint, https://www.google.com/recaptcha/api/siteverify, together with its own secret key. Google replies with JSON indicating whether the token is genuine, recent, and tied to that site. Conceptually, server-side verification looks like this:
<?php
header('Content-type: application/json');
$secret = getenv('RECAPTCHA_SECRET'); // the site's private key
$response = $_POST['g-recaptcha-response'] ?? '';
$result = file_get_contents(
'https://www.google.com/recaptcha/api/siteverify'
. '?secret=' . urlencode($secret)
. '&response=' . urlencode($response)
);
echo $result;
A successful verification returns something like:
{
"success": true,
"challenge_ts": "2024-09-29T09:25:55Z",
"hostname": "example.com"
}
If success is false, the token was missing, malformed, already used, expired, or didn't match the site. Full documentation for the verification response is in Google's reCAPTCHA verification guide.
A complete working example (raw HTTP)
Putting the raw-HTTP pieces together — submit, poll, then send the token:
import os
import requests
from time import sleep, time
service_key = os.environ["TWOCAPTCHA_API_KEY"]
google_site_key = "6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u"
page_url = "https://example.com/page-with-recaptcha"
start = time()
# 1. Submit the task and receive a captcha ID.
submit = requests.get(
"https://2captcha.com/in.php",
params={
"key": service_key,
"method": "userrecaptcha",
"googlekey": google_site_key,
"pageurl": page_url,
"json": 1,
},
).json()
if submit.get("status") != 1:
raise SystemExit(f"Submission failed: {submit.get('request')}")
captcha_id = submit["request"]
# 2. Poll until the g-recaptcha-response token is ready.
token = None
for _ in range(20):
sleep(5)
poll = requests.get(
"https://2captcha.com/res.php",
params={"key": service_key, "action": "get", "id": captcha_id, "json": 1},
).json()
if poll.get("status") == 1:
token = poll["request"]
break
if poll.get("request") != "CAPCHA_NOT_READY":
raise SystemExit(f"Solving error: {poll.get('request')}")
if token is None:
raise SystemExit("Timed out waiting for the token.")
print(f"Solved in {time() - start:.1f}s")
# 3. Submit the token to the target form.
headers = {"user-agent": "Mozilla/5.0 Chrome/120.0.0.0 Safari/537.36"}
payload = {"submit": "submit", "g-recaptcha-response": token}
result = requests.post(page_url, headers=headers, data=payload)
print(result.text)
The modern JSON API
The in.php / res.php endpoints shown above are the long-standing legacy interface and still work. 2Captcha also offers a newer task-based JSON API at https://api.2captcha.com/createTask and https://api.2captcha.com/getTaskResult, using a RecaptchaV2TaskProxyless (or proxy-enabled RecaptchaV2Task) task object. The official Python client wraps whichever interface it needs, which is one more reason to prefer Approach A for new projects. Full details are in the API documentation.
Limitations
The most important constraint to plan around is the token's lifetime: a g-recaptcha-response token is valid for roughly 120 seconds (2 minutes) after it's issued. You're responsible for submitting it to the target form within that window — if you wait too long, the target site's server-side verification will reject it and you'll have to request a fresh one.
To stay within the window reliably, keep the gap between receiving the token and submitting it as short as possible, and avoid doing slow work (large downloads, long sleeps) in between.
Other languages and further reading
2Captcha publishes official clients and examples for many languages, so the same workflow translates easily:
- Python — 2captcha-python · Python guide
- PHP — 2captcha-php · PHP guide
- Other SDKs and demos — the code examples hub links to Java, C#, Ruby, Go, and Selenium-based examples.
Background reading on reCAPTCHA itself is in Google's official reCAPTCHA documentation.
If you need to clear many CAPTCHAs in bulk, the next thing worth optimising is throughput: average solve times hover around 25 seconds, so submitting tasks concurrently — rather than one at a time — dramatically reduces the total time for batch form submissions.