Call the CAPTCHA API from PowerShell
Use PowerShell 7.5+ and Invoke-RestMethod to call solvethree directly. The script below creates one reCAPTCHA Enterprise task and polls the same task ID until it finishes or the local waiting budget runs out.
On this page
Set the credentials and target inputs
- 1.
Make the site available to your workspace
Configure an active profile for the target hostname in the console. Sending a hostname and action doesn't create one. See site profile setup if this hasn't been done.
- 2.
Supply the environment variables
Set
SOLVETHREE_API_KEYandSOLVETHREE_PROXYin the PowerShell process's environment using your normal secret-handling mechanism. Check that both have values before running. The API key becomes a Bearer header; it doesn't belong in the task JSON. - 3.
Replace the example hostname and action
Change
accounts.example.comandloginto the hostname and action required by your integration. Use a hostname, not a full URL. The request defaults to Enterprise v3, so it needs neither atypenor aurlfield.
Create once, then poll the task ID
#requires -Version 7.5
$ErrorActionPreference = 'Stop'
$apiBase = 'https://api.solvethree.com/v1'
$headers = @{ Authorization = "Bearer $env:SOLVETHREE_API_KEY"; Accept = 'application/json' }
$taskInput = @{ site = 'accounts.example.com'; action = 'login'; proxy = $env:SOLVETHREE_PROXY; requestId = [guid]::NewGuid().ToString() }
$requestClock = [Diagnostics.Stopwatch]::StartNew()
function Call-TaskApi($Method, $Path, $Payload = $null) {
$remaining = 180 - $requestClock.Elapsed.TotalSeconds
if ($remaining -le 0) { throw 'Local deadline reached' }
$seconds = [int][Math]::Ceiling([Math]::Min(25, $remaining))
$options = @{ Method = $Method; Uri = "$apiBase$Path"; Headers = $headers;
ConnectionTimeoutSeconds = $seconds; OperationTimeoutSeconds = $seconds;
MaximumRetryCount = 0; MaximumRedirection = 0 }
if ($null -ne $Payload) { $options.Body = $Payload | ConvertTo-Json -Depth 6; $options.ContentType = 'application/json' }
Invoke-RestMethod @options
}
$taskId = $null
try {
$result = Call-TaskApi 'POST' '/tasks?waitMs=0' $taskInput
$taskId = $result.id
while ($result.status -in @('pending', 'solving')) {
$escapedId = [uri]::EscapeDataString($taskId)
$result = Call-TaskApi 'GET' "/tasks/$escapedId`?waitMs=15000"
if ($result.status -in @('pending', 'solving')) { Start-Sleep -Seconds 1 }
}
if ($result.status -ne 'ready') { throw "Task failed: $($result.errorCode)" }
Write-Host "Ready task: $taskId"
# Submit $result.solution.token with userAgent/secChUa and the original proxy.
} catch {
Write-Host "Recovery requestId=$($taskInput.requestId) taskId=$taskId"
throw
}The script sends one POST to /v1/tasks?waitMs=0. Subsequent requests are GETs for the returned ID, using waitMs=15000. pending and solving keep the loop running; another status ends it.
ConvertTo-Json serializes the task hashtable rather than relying on hand-quoted JSON or a shell command. The task ID is URL-escaped before polling. Keep these parts when adapting the example.
The Stopwatch checks a 180-second local budget before each API call. Each call gets connection and operation timeouts of at most 25 seconds, reduced as the budget runs down. After a nonterminal poll, the script sleeps for one second. The budget prevents endless polling; it isn't an exact wall-clock cutoff.
MaximumRetryCount = 0 disables automatic HTTP retries, and MaximumRedirection = 0 refuses redirects. The timeout parameters used here require PowerShell 7.5+, not just any PowerShell 7 installation. Microsoft's Invoke-RestMethod reference documents these parameters.
Use the token, not the task ID
On ready, the reCAPTCHA value to submit is $result.solution.token. $taskId only identifies the solvethree task. The example stops before making the application request to your target.
Submit the token with the returned solution's userAgent and secChUa, using the original proxy. Don't substitute an unrelated browser identity or send it to a different target. ready means solvethree generated a solution; it doesn't establish that the target accepted your application request.
- Invoke-RestMethod throws an HTTP or connection exception
The API request failed before the script could process a successful response. The catch block prints recovery IDs and rethrows the original exception.
Try this: Inspect the HTTP status and response details, when available. Correct invalid inputs rather than retrying them unchanged. If task creation had an uncertain outcome, recover it as described below.
- The task returns failed
Inspect both
$result.errorCodeand$result.error. The script's terminal-task exception includes only the error code.Try this: Stop polling that task and address the reported cause. For example,
site-unreachableis a task error, not proof that the API request itself failed. See CAPTCHA API errors.
Recover an uncertain request without creating another task
A timeout doesn't prove the create request failed. The catch block prints requestId and, if it was received, taskId. Keep those values.
- 1.
If you have a task ID, retrieve that task
Continue with GET
/v1/tasks/:idrather than creating another task. Reaching the script's local deadline doesn't establish that the remote task failed. - 2.
If creation returned no task ID, reuse the original request
Resubmit the same task inputs with the same
requestIdto recover the existing task. Changing the inputs while reusing that ID causes a conflict. - 3.
Don't restart this script as your recovery procedure
A fresh run generates a new GUID and therefore a new logical request. For unattended use, save the original requestId and task inputs securely before the POST, then save the task ID when it arrives. The example prints recovery IDs on failure but doesn't persist them across process loss.
Keep the saved inputs out of ordinary logs if the proxy URL contains credentials. You need the original inputs for recovery, not another copy of the API key.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.