Se um script PowerShell trava porque o formulário de teste exibe reCAPTCHA, Cloudflare Turnstile ou um CAPTCHA de imagem, a rota mais direta é chamar a API HTTP da CaptchaAI direto do Invoke-RestMethod — sem instalar nenhum módulo do PowerShell Gallery. Funciona tanto no PowerShell 5.1 nativo do Windows quanto no PowerShell 7+ multiplataforma, e cabe em qualquer pipeline que já rode .ps1.
Este guia mostra como montar funções de resolução para reCAPTCHA v2/v3, Cloudflare Turnstile e CAPTCHA de imagem, empacotar tudo em um módulo reutilizável e agendar a automação no Agendador de Tarefas do Windows — com retry, backoff exponencial e paralelismo via Start-Job.
Por que resolver CAPTCHA direto no PowerShell
- Nativo do Windows — o PowerShell 5.1 já vem instalado; nenhuma dependência extra para baixar.
Invoke-RestMethodentende JSON sozinho — a resposta da API já chega como objeto, sem parsing manual.- Agendador de Tarefas — dá para rodar a automação em horário fixo, sem depender de um serviço externo.
- Encaixa no pipeline — o token resolvido segue direto para o próximo passo do script.
- PowerShell 7+ roda fora do Windows — o mesmo código funciona em runners Linux de CI/CD.
Cenário comum em QA de bancos e órgãos públicos que ainda operam Windows Server: o script roda às 6h pelo Agendador de Tarefas, preenche o formulário de homologação em staging.example.com e registra o resultado antes do expediente. Ao gravar logs dessa automação, considere a LGPD — prefira sempre dados fictícios em QA.
O que você precisa antes de começar
- PowerShell 5.1 (Windows) ou PowerShell 7+ (multiplataforma)
- Uma chave de API da CaptchaAI (crie a sua aqui)
- Nenhum módulo adicional — os cmdlets usados já vêm no PowerShell
Funções base: enviar e consultar a tarefa
Enviar a tarefa
A função abaixo empacota a chamada POST para in.php: recebe a chave de API e os parâmetros da tarefa em uma hashtable, e lança uma exceção se o status retornado for diferente de 1.
function Submit-CaptchaTask {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[hashtable]$TaskParams
)
$body = @{
key = $ApiKey
json = 1
} + $TaskParams
$response = Invoke-RestMethod -Uri "https://ocr.captchaai.com/in.php" `
-Method Post `
-Body $body `
-ContentType "application/x-www-form-urlencoded"
if ($response.status -ne 1) {
throw "Submit failed: $($response.request)"
}
return $response.request
}
Consulta do resultado
Depois de enviar a tarefa, é preciso consultar res.php até a CaptchaAI devolver a solução. A função abaixo faz o polling em intervalo configurável e aplica um timeout — enquanto o request vier CAPCHA_NOT_READY, ela continua tentando.
function Get-CaptchaResult {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[string]$TaskId,
[int]$MaxWaitSeconds = 300,
[int]$PollIntervalSeconds = 5
)
$deadline = (Get-Date).AddSeconds($MaxWaitSeconds)
while ((Get-Date) -lt $deadline) {
Start-Sleep -Seconds $PollIntervalSeconds
$response = Invoke-RestMethod -Uri "https://ocr.captchaai.com/res.php" `
-Method Get `
-Body @{
key = $ApiKey
action = "get"
id = $TaskId
json = 1
}
if ($response.request -eq "CAPCHA_NOT_READY") {
Write-Verbose "Waiting for solution..."
continue
}
if ($response.status -ne 1) {
throw "Solve failed: $($response.request)"
}
return $response.request
}
throw "Timeout: CAPTCHA not solved within $MaxWaitSeconds seconds"
}
Resolvendo os principais tipos de CAPTCHA no PowerShell
Com Submit-CaptchaTask e Get-CaptchaResult prontas, cada tipo de CAPTCHA vira só uma combinação diferente de parâmetros na mesma dupla de funções.
reCAPTCHA v2
Monte o method: userrecaptcha com a sitekey (googlekey) e a URL da página (pageurl):
function Solve-RecaptchaV2 {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[string]$SiteUrl,
[Parameter(Mandatory)]
[string]$SiteKey
)
Write-Host "Submitting reCAPTCHA v2 task..."
$taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
method = "userrecaptcha"
googlekey = $SiteKey
pageurl = $SiteUrl
}
Write-Host "Task ID: $taskId"
Write-Host "Polling for solution..."
$token = Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
Write-Host "Solved! Token: $($token.Substring(0, [Math]::Min(50, $token.Length)))..."
return $token
}
# Usage
$apiKey = "YOUR_API_KEY"
$token = Solve-RecaptchaV2 `
-ApiKey $apiKey `
-SiteUrl "https://staging.example.com/qa-login" `
-SiteKey "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
Cloudflare Turnstile
O fluxo segue a mesma estrutura, só trocando o method para turnstile:
function Solve-Turnstile {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[string]$SiteUrl,
[Parameter(Mandatory)]
[string]$SiteKey
)
$taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
method = "turnstile"
key = $SiteKey
pageurl = $SiteUrl
}
return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}
# Usage
$token = Solve-Turnstile `
-ApiKey "YOUR_API_KEY" `
-SiteUrl "https://example.com/form" `
-SiteKey "0x4AAAAAAAB5..."
reCAPTCHA v3 (com score e action)
Informe a version e a action configurada no site — a CaptchaAI usa os dois para calcular o score correto:
function Solve-RecaptchaV3 {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[string]$SiteUrl,
[Parameter(Mandatory)]
[string]$SiteKey,
[string]$Action = "verify",
)
$taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
method = "userrecaptcha"
googlekey = $SiteKey
pageurl = $SiteUrl
version = "v3"
action = $Action
}
return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}
Resolvendo CAPTCHA de imagem
A partir de um arquivo local
Para CAPTCHA de imagem, a CaptchaAI espera o conteúdo em base64 no parâmetro body, com method: base64. A função lê o arquivo, converte e reaproveita o par enviar/consultar já criado:
function Solve-ImageCaptcha {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[string]$ImagePath
)
if (-not (Test-Path $ImagePath)) {
throw "Image file not found: $ImagePath"
}
$imageBytes = [System.IO.File]::ReadAllBytes($ImagePath)
$base64 = [Convert]::ToBase64String($imageBytes)
$taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
method = "base64"
body = $base64
}
return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}
# Usage
$text = Solve-ImageCaptcha -ApiKey "YOUR_API_KEY" -ImagePath "C:\captcha.png"
Write-Host "CAPTCHA text: $text"
A partir de uma URL
Quando a imagem já está hospedada, dá para baixar o conteúdo com Invoke-WebRequest e pular a etapa de gravar em disco:
function Solve-ImageCaptchaFromUrl {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[string]$ImageUrl
)
$imageBytes = (Invoke-WebRequest -Uri $ImageUrl).Content
$base64 = [Convert]::ToBase64String($imageBytes)
$taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
method = "base64"
body = $base64
}
return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}
Um módulo completo para reaproveitar em outros scripts
Para não copiar as mesmas funções em cada script, vale empacotar tudo em uma classe CaptchaAISolver e salvar como CaptchaAI.psm1. Ela cobre reCAPTCHA v2, Turnstile e CAPTCHA de imagem, e ainda expõe GetBalance() para checar o saldo antes de disparar um lote grande de tarefas:
class CaptchaAISolver {
[string]$ApiKey
[string]$BaseUrl = "https://ocr.captchaai.com"
[int]$PollInterval = 5
[int]$MaxWait = 300
CaptchaAISolver([string]$apiKey) {
$this.ApiKey = $apiKey
}
[string] SolveRecaptchaV2([string]$siteUrl, [string]$siteKey) {
return $this.Solve(@{
method = "userrecaptcha"
googlekey = $siteKey
pageurl = $siteUrl
})
}
[string] SolveTurnstile([string]$siteUrl, [string]$siteKey) {
return $this.Solve(@{
method = "turnstile"
key = $siteKey
pageurl = $siteUrl
})
}
[string] SolveImage([string]$imagePath) {
$bytes = [System.IO.File]::ReadAllBytes($imagePath)
$base64 = [Convert]::ToBase64String($bytes)
return $this.Solve(@{
method = "base64"
body = $base64
})
}
[double] GetBalance() {
$response = Invoke-RestMethod -Uri "$($this.BaseUrl)/res.php" `
-Body @{ key = $this.ApiKey; action = "getbalance"; json = 1 }
return [double]$response.request
}
hidden [string] Solve([hashtable]$params) {
$taskId = $this.Submit($params)
return $this.Poll($taskId)
}
hidden [string] Submit([hashtable]$params) {
$body = @{ key = $this.ApiKey; json = 1 } + $params
$response = Invoke-RestMethod -Uri "$($this.BaseUrl)/in.php" `
-Method Post -Body $body
if ($response.status -ne 1) { throw "Submit: $($response.request)" }
return $response.request
}
hidden [string] Poll([string]$taskId) {
$deadline = (Get-Date).AddSeconds($this.MaxWait)
while ((Get-Date) -lt $deadline) {
Start-Sleep -Seconds $this.PollInterval
$response = Invoke-RestMethod -Uri "$($this.BaseUrl)/res.php" `
-Body @{ key = $this.ApiKey; action = "get"; id = $taskId; json = 1 }
if ($response.request -eq "CAPCHA_NOT_READY") { continue }
if ($response.status -ne 1) { throw "Solve: $($response.request)" }
return $response.request
}
throw "Timeout"
}
}
# Export
Export-ModuleMember
Como importar e usar o módulo
Depois de salvo, basta importar com using module e instanciar a classe com a chave de API:
using module .\CaptchaAI.psm1
$solver = [CaptchaAISolver]::new("YOUR_API_KEY")
# Check balance
$balance = $solver.GetBalance()
Write-Host "Balance: `$$balance"
# Solve reCAPTCHA v2
$token = $solver.SolveRecaptchaV2("https://staging.example.com/qa-login", "SITEKEY")
Write-Host "Token: $($token.Substring(0, 50))..."
Colocando a automação em produção
Enviando o formulário com o token resolvido
O token retornado pela CaptchaAI entra no campo g-recaptcha-response do formulário de destino. Para Turnstile, troque o nome do campo pelo esperado no site (cf-turnstile-response):
function Submit-FormWithToken {
param(
[string]$Url,
[string]$Token,
[hashtable]$FormData
)
$body = $FormData + @{
"g-recaptcha-response" = $Token
}
$response = Invoke-WebRequest -Uri $Url `
-Method Post `
-Body $body `
-ContentType "application/x-www-form-urlencoded"
return $response
}
# Usage
$token = Solve-RecaptchaV2 -ApiKey "YOUR_API_KEY" `
-SiteUrl "https://staging.example.com/qa-login" `
-SiteKey "SITEKEY"
$result = Submit-FormWithToken `
-Url "https://staging.example.com/qa-login" `
-Token $token `
-FormData @{
username = "user@example.com"
password = "password"
}
Write-Host "Response: $($result.StatusCode)"
Resolvendo vários CAPTCHAs em paralelo com Start-Job
Start-Job evita que cada Invoke-RestMethod bloqueie o script inteiro enquanto espera o polling. O paralelismo real depende do plano: o BASIC (US$ 15/mês) libera 5 threads simultâneas, o STANDARD (US$ 30/mês) sobe para 15 — o número de Start-Job ativos deve respeitar esse limite.
$apiKey = "YOUR_API_KEY"
$tasks = @(
@{ Url = "https://site-a.com"; Key = "SITEKEY_A" },
@{ Url = "https://site-b.com"; Key = "SITEKEY_B" },
@{ Url = "https://site-c.com"; Key = "SITEKEY_C" }
)
$jobs = $tasks | ForEach-Object {
$task = $_
Start-Job -ScriptBlock {
param($ApiKey, $Url, $SiteKey)
$taskId = (Invoke-RestMethod -Uri "https://ocr.captchaai.com/in.php" -Method Post -Body @{
key = $ApiKey; json = 1; method = "userrecaptcha"
googlekey = $SiteKey; pageurl = $Url
}).request
$deadline = (Get-Date).AddSeconds(300)
while ((Get-Date) -lt $deadline) {
Start-Sleep -Seconds 5
$result = Invoke-RestMethod -Uri "https://ocr.captchaai.com/res.php" -Body @{
key = $ApiKey; action = "get"; id = $taskId; json = 1
}
if ($result.request -ne "CAPCHA_NOT_READY" -and $result.status -eq 1) {
return @{ Url = $Url; Token = $result.request }
}
}
return @{ Url = $Url; Error = "Timeout" }
} -ArgumentList $apiKey, $task.Url, $task.Key
}
# Wait and collect results
$results = $jobs | Wait-Job | Receive-Job
$results | ForEach-Object {
if ($_.Token) {
Write-Host "$($_.Url): $($_.Token.Substring(0, 50))..."
} else {
Write-Host "$($_.Url): $($_.Error)" -ForegroundColor Red
}
}
$jobs | Remove-Job
Retry com backoff exponencial
Nem todo erro deve gerar nova tentativa: a função abaixo só reenvia para ERROR_NO_SLOT_AVAILABLE e ERROR_CAPTCHA_UNSOLVABLE, com backoff exponencial e jitter para não sobrecarregar a API:
function Solve-WithRetry {
param(
[Parameter(Mandatory)]
[string]$ApiKey,
[Parameter(Mandatory)]
[hashtable]$TaskParams,
[int]$MaxRetries = 3
)
$retryableErrors = @(
"ERROR_NO_SLOT_AVAILABLE",
"ERROR_CAPTCHA_UNSOLVABLE"
)
for ($attempt = 0; $attempt -le $MaxRetries; $attempt++) {
if ($attempt -gt 0) {
$delay = [Math]::Pow(2, $attempt) + (Get-Random -Maximum 3)
Write-Host "Retry $attempt/$MaxRetries after $($delay)s..."
Start-Sleep -Seconds $delay
}
try {
$taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams $TaskParams
$result = Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
return $result
}
catch {
$errorMsg = $_.Exception.Message
$isRetryable = $retryableErrors | Where-Object { $errorMsg -like "*$_*" }
if (-not $isRetryable -or $attempt -eq $MaxRetries) {
throw
}
Write-Warning "Retryable error: $errorMsg"
}
}
}
Agendando a automação no Agendador de Tarefas do Windows
Para rodar a automação sem intervenção manual, registre um ScheduledTask que dispara o script todo dia no mesmo horário. Guarde a chave de API fora do script — em uma variável de ambiente da máquina ou em um cofre de segredos — em vez de deixá-la em texto puro dentro do .ps1 que fica no disco:
# Create a scheduled task that runs CAPTCHA automation daily
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-ExecutionPolicy validação autorizada -File C:\Scripts\captcha-automation.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At "08:00"
Register-ScheduledTask `
-TaskName "CaptchaAutomation" `
-Action $action `
-Trigger $trigger `
-Description "Run daily CAPTCHA automation with CaptchaAI"
Erros comuns e como resolver
Erros mais frequentes ao integrar a API no PowerShell:
| Erro | Causa | Correção |
|---|---|---|
ERROR_WRONG_USER_KEY |
Chave de API inválida ou copiada errada | Confira a chave no painel da CaptchaAI |
ERROR_ZERO_BALANCE |
Saldo zerado na conta | Faça a recarga de créditos |
Invoke-RestMethod: SSL/TLS |
Versão de TLS incompatível | Adicione [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 |
The response content cannot be parsed |
A resposta não veio em JSON | Use Invoke-WebRequest e faça o parsing manualmente |
Erro de Execution policy |
Script bloqueado pela política de execução | Execute Set-ExecutionPolicy -Scope CurrentUser RemoteSigned |
Cannot convert to double |
Falha ao converter o saldo retornado | Use [double]::Parse($response.request) |
Perguntas frequentes
Qual plano da CaptchaAI faz sentido para rodar vários scripts em paralelo?
Depende de quantos Start-Job ficam ativos ao mesmo tempo: o BASIC (US$ 15/mês) inclui 5 threads, o STANDARD (US$ 30/mês) sobe para 15 e o ADVANCE (US$ 90/mês) chega a 50 — todos com resoluções ilimitadas por thread.
Preciso instalar algum módulo do PowerShell Gallery para chamar a API?
Não. Invoke-RestMethod e Invoke-WebRequest já vêm no PowerShell 5.1 e no 7+; a API é HTTP puro, sem SDK obrigatório.
O Agendador de Tarefas executa o script, mas o CAPTCHA nunca é resolvido — o que verificar?
Confira se a tarefa usa a mesma versão do PowerShell dos testes manuais e se a Set-ExecutionPolicy permite o script naquele contexto de usuário — sessões agendadas costumam rodar sem perfil carregado, então caminhos relativos podem falhar.
Por que o Invoke-RestMethod retorna erro de SSL/TLS ao chamar a API?
Geralmente porque o Windows ainda usa um protocolo TLS mais antigo. Adicione [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 antes da primeira chamada.
Como evito deixar a chave de API exposta em texto puro no script agendado?
Leia a chave de uma variável de ambiente ($env:CAPTCHAAI_KEY) ou de um cofre como o Windows Credential Manager, em vez de gravá-la direto no .ps1.
Guias relacionados
- Automação de shell com Bash/cURL e a CaptchaAI
- Como resolver CAPTCHA em C#
- Documentação da API da CaptchaAI
Automatize CAPTCHAs a partir da linha de comando do Windows — pegue sua chave de API e comece a automatizar em minutos.