Solve and submit reCAPTCHA with aiohttp
Use aiohttp directly against the REST API: create one task, poll until it’s ready, then submit solution.token to the target. Keep the API and target requests in separate sessions so your workspace Bearer token never reaches the target.
The example requires Python 3.11+ and puts creation, polling and target submission inside one 180-second deadline. That’s an application timeout, not a promised solve time.
On this page
Set the site, proxy and target request
- Set SOLVETHREE_API_KEY to your workspace API key. Keep it on the server.
- Set SOLVETHREE_PROXY to the proxy URL the service should use for solving. The target submission uses that same proxy.
- Replace accounts.example.com and the login action with the hostname and action used by your integration. The site needs an active profile available to your workspace; set up the profile before creating tasks.
- Replace the target login URL and form data with the actual application request. The example sends only g-recaptcha-response; add the other fields your target requires and use its expected token field.
The task sends site, action and a single proxy. It defaults to reCAPTCHA Enterprise v3; don’t add type or url fields. If the target flow needs cookies from an earlier request, make those requests through the target session—the example starts with a fresh session.
Create once, poll, then submit
# pip install aiohttp
import asyncio
import os
import uuid
from urllib.parse import urlsplit, unquote
import aiohttp
async def main():
api = "https://api.solvethree.com/v1"
proxy = os.environ["SOLVETHREE_PROXY"]
task = {"site": "accounts.example.com", "action": "login", "proxy": proxy, "requestId": str(uuid.uuid4())}
task_id = None
try:
async with asyncio.timeout(180):
async with aiohttp.ClientSession(headers={"Authorization": "Bearer " + os.environ["SOLVETHREE_API_KEY"]}) as client:
async with client.post(api + "/tasks", params={"waitMs": 0}, json=task, allow_redirects=False) as response:
response.raise_for_status()
if response.status >= 300: raise RuntimeError("Unexpected API redirect")
result = await response.json()
task_id = result["id"]
while result["status"] in ("pending", "solving"):
async with client.get(api + "/tasks/" + task_id, params={"waitMs": 15000}, allow_redirects=False) as response:
response.raise_for_status()
if response.status >= 300: raise RuntimeError("Unexpected API redirect")
result = await response.json()
if result["status"] in ("pending", "solving"): await asyncio.sleep(1)
if result["status"] != "ready":
raise RuntimeError("Task failed: " + str(result.get("errorCode", "unknown")))
solution = result["solution"]
# A separate session prevents the API Bearer header reaching the target.
proxy_url = urlsplit(proxy)
netloc = proxy_url.netloc.rsplit("@", 1)[-1]
server = f"{proxy_url.scheme}://{netloc}"
auth = aiohttp.BasicAuth(unquote(proxy_url.username), unquote(proxy_url.password or "")) if proxy_url.username else None
async with aiohttp.ClientSession() as target:
async with target.post("https://accounts.example.com/login", proxy=server, proxy_auth=auth,
headers={"User-Agent": solution["userAgent"], "sec-ch-ua": solution["secChUa"]},
data={"g-recaptcha-response": solution["token"]}, allow_redirects=False) as response:
print("Application HTTP status:", response.status)
except TimeoutError:
print("Timed out; recover requestId", task["requestId"], "taskId", task_id)
raise
asyncio.run(main())This is direct async REST, not an async method on the official Python SDK. It makes one creation POST to /v1/tasks, saves the returned id, and uses GET requests to poll that task. pending and solving continue the loop; ready permits submission; a failed task stops the loop.
The proxy in the task body tells solvethree which proxy to use. The aiohttp proxy and proxy_auth arguments appear only on the target HTTP call—not on the API calls. The example separates proxy credentials from the proxy server URL and submits the token with the returned userAgent and secChUa.
The sessions are deliberately separate: only the API session has the Authorization header. Redirects are disabled, and API redirects are treated as errors rather than followed.
- An API request returns an HTTP error.
The example checks HTTP status before reading a success payload.
Try this: Handle the HTTP error separately from task failure. Correct invalid inputs or authorization problems before retrying.
- The task reaches failed.
Inspect errorCode and error in the task response. The example raises with errorCode only.
Try this: Stop polling that task and address the reported cause. Fix the reported cause before trying again.
- The task is ready, but the application rejects the request.
Inspect the outgoing token field, target URL, returned browser identity and proxy, plus any required form fields or cookies.
Try this: Submit solution.token with the returned identity through the same proxy. Read the application response using that target’s success criteria—the printed HTTP status alone doesn’t establish acceptance.
Recover a timeout without buying another solve
A creation timeout doesn’t prove that creation failed. The example keeps the original inputs in task and prints its requestId plus task_id when the deadline expires. Those variables survive only while the process is running; the printed IDs aren’t a complete recovery record.
- 1.
Save the original request before creation
For recovery across restarts, securely retain the complete task body, including its requestId and proxy. The example generates a fresh UUID on each run, so simply rerunning it creates a new logical request.
- 2.
Resume the existing task
If you have task_id, retrieve that task. If creation timed out before you received an ID, repeat creation with the saved, unchanged task body and requestId. The API returns the existing task; changing the inputs while reusing the ID conflicts.
- 3.
Check whether submission already happened
The deadline also covers the target POST. If that request timed out, inspect the application outcome before submitting again: it may have received the request even though your client didn’t receive a response. Recovering the solvethree task doesn’t establish what happened at the target.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.