Call solvethree from a Flask backend
Use separate Flask routes to create and retrieve a solvethree task. The POST below returns without waiting for the whole solve; the caller keeps the task ID and checks the GET route until the task is ready or failed.
On this page
Install dependencies and configure the target
- 1.
Use Python 3.10 or newer
Install Flask with
python -m pip install flask. Install the official Python SDK withpython -m pip install https://solvethree.com/downloads/solvethree_sdk-0.1.0-py3-none-any.whl. - 2.
Set the server environment variables
Set
SOLVETHREE_API_KEYto your workspace API key andINTEGRATION_KEYto the secret your authorized caller will send inX-Integration-Key. SetCAPTCHA_SITEto the target hostname, not a full URL, andSOLVETHREE_PROXYto the HTTP or HTTPS proxy URL used for this task. Keep these values on the server. - 3.
Activate the site profile
Configure an active profile for that hostname in the solvethree console, available to your workspace. Setting
CAPTCHA_SITEdoesn't create a profile. See site profile setup if the target isn't configured yet.
The example fixes the action to login and uses the default reCAPTCHA Enterprise v3 task. Change that action to match the target operation before running it. The caller cannot override the site, action, or proxy through the request body.
Add the create and retrieve routes
# pip install flask and the official solvethree SDK wheel.
import hmac
import os
from flask import Flask, abort, jsonify, request
from solvethree import Solvethree, SolvethreeError
app = Flask(__name__)
def client():
return Solvethree(os.environ["SOLVETHREE_API_KEY"])
@app.before_request
def authorize():
if not hmac.compare_digest(request.headers.get("x-integration-key", ""), os.environ["INTEGRATION_KEY"]):
abort(401)
@app.errorhandler(SolvethreeError)
def task_error(error):
return jsonify(requestId=error.request_id, taskId=error.task_id), 502
@app.errorhandler(ValueError)
def invalid_input(error):
return jsonify(error="Invalid task input or ID"), 400
@app.post("/captcha/tasks")
def create():
task = {"site": os.environ["CAPTCHA_SITE"], "action": "login", "proxy": os.environ["SOLVETHREE_PROXY"]}
if request.headers.get("x-request-id"):
task["requestId"] = request.headers["x-request-id"]
return jsonify(client().create_task(task, wait_ms=0, timeout=20))
@app.get("/captcha/tasks/<task_id>")
def retrieve(task_id):
return jsonify(client().get_task(task_id, wait_ms=15000, timeout=25))Save the example as app.py. With the environment variables set, start it locally with python -m flask --app app run. The routes are POST /captcha/tasks and GET /captcha/tasks/<task_id>. Flask doesn't need a separate URLconf: the decorators register both routes. See the Flask quickstart for local startup details; keep your existing production WSGI configuration.
Creation uses wait_ms=0 with a 20-second network timeout. That skips waiting for the solve, not the HTTP request itself. Retrieval allows up to 15 seconds of waiting for a task update, with a 25-second network timeout. Both views use the synchronous SDK, so each occupies its request worker while the SDK call is running.
Keep the request ID before sending the POST
- 1.
Create one request ID per logical task
Store it before calling
POST /captcha/tasks, then send it asX-Request-IDalongsideX-Integration-Key. Use 1–64 ASCII letters, numbers, underscores, or hyphens. This POST needs no task body because the server constructs the task from its fixed configuration. - 2.
Keep the returned task ID
After a successful HTTP response, save the result's
id. Retrieve it throughGET /captcha/tasks/<task_id>, sendingX-Integration-Keyagain. - 3.
Recover an uncertain creation instead of creating another task
If the POST connection times out, creation may still have succeeded. Retrieve the task if you have its ID. Otherwise, repeat the POST with the same
X-Request-IDand unchanged task inputs to recover the existing task. Reusing the ID after changing the site, action, or proxy conflicts.
The header is optional in the sample, but omitting it removes this recovery option when creation succeeds and its response is lost. Don't generate a new ID merely because the first request timed out.
Handle HTTP errors before task status
Check the Flask response's HTTP status first. The example returns 401 for a missing or incorrect integration key, 400 for a caught ValueError, and 502 for a caught SolvethreeError. That 502 body exposes requestId and taskId for recovery; it isn't a task-status payload and doesn't by itself mean the task failed.
| Task status | Caller action |
|---|---|
| pending | Keep the same task ID and retrieve again. |
| solving | Keep the same task ID and retrieve again. |
| ready | Stop polling and read the solution. |
| failed | Stop polling and inspect errorCode and error. Failed solves are refunded. |
Submit the token with the original proxy and returned identity
Only a ready result contains a usable solution. Send solution.token to the intended target—not the task ID—and use the returned solution.userAgent and solution.secChUa browser identity.
Submit through the same proxy used to create the task. This example uses one fixed SOLVETHREE_PROXY, so retain that original proxy configuration with the task rather than assuming the current environment value will still match later. The Flask routes return the SDK result; they don't perform the target submission or configure the caller's proxy.
Keep the token, proxy, browser identity, and intended target together. A ready task means solvethree generated a solution; acceptance is determined by the target's response to the subsequent application request.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.