CapSolutio — Documentação da API

API de resolução de CAPTCHA. O fluxo é o padrão: você cria uma tarefa, recebe um taskId, e consulta o resultado até ele ficar pronto. Quem já integra um serviço de resolução encontra aqui o mesmo formato de envelope.

  • URL base: https://capsolutio.kurierservicos.com.br
  • Formato: JSON em todas as requisições e respostas
  • Autenticação: campo clientKey no corpo
  • CAPTCHA suportado: hCaptcha, reCAPTCHA v2 (checkbox e invisível), reCAPTCHA v3 e Cloudflare Turnstile

Índice

  1. Início rápido
  2. Autenticação
  3. Criar tarefa — POST /v1/tasks
  4. Tipos de CAPTCHA
  5. Consultar resultado — POST /v1/tasks/result
  6. Usando o token recebido
  7. Usando seu próprio proxy
  8. Idempotência
  9. Códigos de erro
  10. Limites e cabeçalhos
  11. Exemplos completos
  12. Boas práticas

1. Início rápido

Duas chamadas resolvem um CAPTCHA. O exemplo usa hCaptcha; para os outros tipos, só o objeto task muda (ver seção 4).

Passo 1 — criar a tarefa:

curl -X POST https://capsolutio.kurierservicos.com.br/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "clientKey": "sua_chave_aqui",
    "task": {
      "type": "HCaptchaTaskProxyless",
      "websiteURL": "https://exemplo.com.br/login",
      "websiteKey": "10000000-ffff-ffff-ffff-000000000001"
    }
  }'
{ "errorId": 0, "taskId": "cps_9f3c1a8b7d2e4f60" }

Passo 2 — buscar o resultado (a cada 3–5 s):

curl -X POST https://capsolutio.kurierservicos.com.br/v1/tasks/result \
  -H "Content-Type: application/json" \
  -d '{
    "clientKey": "sua_chave_aqui",
    "taskId": "cps_9f3c1a8b7d2e4f60"
  }'
{
  "errorId": 0,
  "taskId": "cps_9f3c1a8b7d2e4f60",
  "status": "ready",
  "solution": {
    "token": "P1_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
  }
}

Passo 3 — entregar o token ao site. O token substitui o que o site receberia se uma pessoa tivesse resolvido o CAPTCHA. A seção 6 mostra como descobrir onde ele entra.


2. Autenticação

A chave vai no corpo, no campo clientKey, em todas as chamadas:

{ "clientKey": "sua_chave_aqui", "...": "..." }

Chave ausente, desconhecida ou inativa devolve HTTP 401 com ERROR_KEY_DOES_NOT_EXIST.


3. Criar tarefa — POST /v1/tasks

Cria e enfileira uma resolução. Responde 201 para uma tarefa nova.

Corpo

Campo Tipo Obrigatório Descrição
clientKey string sim Sua chave de API.
task objeto sim Descrição do que resolver. Ver abaixo.

Objeto task

Os campos comuns a todos os tipos:

Campo Tipo Obrigatório Descrição
type string sim Ver a tabela de tipos abaixo.
websiteURL string sim URL absoluta da página real onde o widget existe.
websiteKey string sim Sitekey publicada na página.
proxyType string não http, https ou socks5. Ver seção 7.
proxyAddress string não Host ou IP do seu proxy.
proxyPort inteiro não Porta do seu proxy.
proxyLogin string não Usuário do proxy, se houver.
proxyPassword string não Senha do proxy, se houver.

websiteURL e websiteKey são obtidos no site que hospeda o CAPTCHA. A seção 4 mostra onde encontrar a sitekey de cada tipo.

Cada tipo tem ainda campos próprios, descritos na seção 4.

Tipos aceitos. Um tipo que existe mas ainda não é oferecido responde ERROR_UNSUPPORTED_CAPTCHA_TYPE (422) — diferente de ERROR_NO_SUCH_METHOD, que é nome inexistente. A distinção diz se você digitou errado ou pediu algo que ainda não existe.

type estado
HCaptchaTask, HCaptchaTaskProxyless, hcaptcha resolvido
RecaptchaV2Task, RecaptchaV2TaskProxyless resolvido
RecaptchaV3Task, RecaptchaV3TaskProxyless resolvido
TurnstileTask, TurnstileTaskProxyless resolvido
RecaptchaV2EnterpriseTask[Proxyless], RecaptchaV3EnterpriseTask[Proxyless] recusado — ainda não oferecidos
TencentTask, ImageToTextTask recusado — ainda não oferecidos

Respostas

HTTP Significado
201 Tarefa criada e enfileirada.
200 Repetição de uma Idempotency-Key já usada com o mesmo corpo — devolve a tarefa original, sem criar nem cobrar de novo.
400 ERROR_MISSING_PARAMETER, ERROR_INVALID_WEBSITE_URL, ERROR_INVALID_SITEKEY, ERROR_INVALID_PROXY.
401 ERROR_KEY_DOES_NOT_EXIST.
402 ERROR_NO_QUOTA_AVAILABLE — cota total esgotada.
409 ERROR_IDEMPOTENCY_CONFLICT.
422 ERROR_NO_SUCH_METHOD (tipo inexistente) ou ERROR_UNSUPPORTED_CAPTCHA_TYPE (tipo conhecido, ainda não resolvido).
429 ERROR_RATE_LIMIT_EXCEEDED ou ERROR_NO_DAILY_QUOTA_AVAILABLE.
503 ERROR_NO_SLOT_AVAILABLE ou ERROR_TEMPORARILY_UNAVAILABLE.
{ "errorId": 0, "taskId": "cps_9f3c1a8b7d2e4f60" }

4. Tipos de CAPTCHA

Para cada tipo abaixo: como reconhecê-lo na página, onde achar a sitekey, os campos próprios, um exemplo de task e o que volta em solution.

Como olhar a página. Abra o site no Chrome ou no Firefox, aperte F12 e use a aba Elements (Elementos) com Ctrl+F para buscar os termos indicados em cada tipo. A aba Network (Rede) mostra os scripts e iframes que o widget carrega.

Resumo

Tipo Como reconhecer Formato típico da sitekey Campo que recebe o token Validade do token
hCaptcha checkbox "Sou humano"; script de hcaptcha.com UUID (10000000-ffff-...) h-captcha-response ~2 min
reCAPTCHA v2 checkbox checkbox "Não sou um robô" 6L... (40 caracteres) g-recaptcha-response 2 min
reCAPTCHA v2 invisível sem checkbox, só o selo no canto; desafio só aparece se o Google desconfiar 6L... g-recaptcha-response ou callback 2 min
reCAPTCHA v3 sem checkbox e nunca mostra desafio; só o selo 6L... varia por site 2 min
Turnstile caixa da Cloudflare "Verifique que você é humano" 0x4AAAA... cf-turnstile-response 5 min

4.1 hCaptcha

Como reconhecer: um <div class="h-captcha" data-sitekey="..."> no HTML, ou um script de js.hcaptcha.com/1/api.js. A sitekey é o data-sitekey; ela também aparece no parâmetro sitekey= do iframe do widget.

Exemplo:

{
  "clientKey": "sua_chave_aqui",
  "task": {
    "type": "HCaptchaTask",
    "websiteURL": "https://exemplo.com.br/login",
    "websiteKey": "10000000-ffff-ffff-ffff-000000000001"
  }
}

Solução: solution.token e solution.userAgent. O token vai no campo h-captcha-response. Alguns sites leem também g-recaptcha-response (o hCaptcha preenche os dois por compatibilidade): quando o formulário tem os dois, preencha os dois com o mesmo token.

4.2 reCAPTCHA v2 (checkbox e invisível)

O reCAPTCHA v2 tem duas formas, que usam o mesmo tipo de tarefa (RecaptchaV2Task). O que muda é o campo isInvisible.

Onde achar a sitekey: no data-sitekey de um <div class="g-recaptcha">, no primeiro argumento de grecaptcha.render(...), ou no parâmetro k= do iframe .../recaptcha/api2/anchor?...&k=6L....

Como saber se é o checkbox ou o invisível:

Sinal Checkbox Invisível
O que aparece na página caixa "Não sou um robô" nada no formulário; só o selo "protegido por reCAPTCHA" no canto da tela
Atributo no HTML sem data-size ou data-size="normal"/"compact" data-size="invisible"
iframe anchor (aba Network) ...&size=normal... ...&size=invisible...
Como o desafio começa o usuário clica na caixa o próprio site chama grecaptcha.execute(), geralmente ao clicar em "Enviar"

Invisível ou v3? Os dois mostram só o selo. O v3 carrega o script com a sitekey no endereço (api.js?render=6L...) e chama grecaptcha.execute('6L...', {action: '...'}) com a sitekey como argumento. O v2 invisível carrega api.js sem render= (ou com render=explicit). Se em algum momento aparecer uma grade de imagens para resolver, é v2.

Campos próprios:

Campo Tipo Obrigatório Descrição
isInvisible booleano não true quando o widget é o invisível. Padrão false.
recaptchaDataSValue string não O token s que alguns widgets passam no render (data-s). Ausente na maioria dos sites.
apiDomain string não google.com (padrão) ou recaptcha.net. Use recaptcha.net quando o script do site vem de www.recaptcha.net. Só estes dois valores — qualquer outro é 400.
callback string não Nome da função de callback que o site registra no widget. Não muda a resolução; volta em solution.callback. Ver seção 6.

Exemplo — checkbox:

{
  "clientKey": "cpsk_...",
  "task": {
    "type": "RecaptchaV2Task",
    "websiteURL": "https://alvo.example/formulario",
    "websiteKey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
  }
}

Exemplo — invisível:

{
  "clientKey": "cpsk_...",
  "task": {
    "type": "RecaptchaV2Task",
    "websiteURL": "https://alvo.example/consulta",
    "websiteKey": "6LcSgwQs...",
    "isInvisible": true
  }
}

Solução: solution.token, solution.userAgent e, se você mandou callback, solution.callback com o mesmo nome. O token vai no campo g-recaptcha-response, ou é passado à função de callback do site — ver seção 6.

O token do reCAPTCHA vale 2 minutos e é verificável uma vez. Ele é contado da resolução, não do seu polling: consuma-o assim que o status virar ready.

4.3 reCAPTCHA v3

O v3 não tem desafio. O Google devolve um token para qualquer visitante, e o site recebe junto um score de 0,0 (provável robô) a 1,0 (provável humano) e decide o que fazer com ele.

Como reconhecer: script https://www.google.com/recaptcha/api.js?render=6L... (a sitekey vai no endereço do script) e uma chamada grecaptcha.execute('6L...', {action: 'login'}) no JavaScript da página. Na tela aparece só o selo.

Onde achar a sitekey e o action: a sitekey é o render= do script ou o primeiro argumento de grecaptcha.execute. O action é o segundo argumento. Busque grecaptcha.execute com Ctrl+Shift+F na aba Sources (Fontes) para achar a chamada nos arquivos JavaScript do site.

Campos próprios:

Campo Tipo Obrigatório Descrição
pageAction string não O action que o site passa em grecaptcha.execute(key, {action}). Copie exatamente o valor da página.
apiDomain string não google.com (padrão) ou recaptcha.net. Só estes dois valores.
enterprisePayload objeto não Campos extras que alguns widgets passam no render. Só a chave s é lida.

Exemplo:

{
  "clientKey": "cpsk_...",
  "task": {
    "type": "RecaptchaV3Task",
    "websiteURL": "https://alvo.example/login",
    "websiteKey": "6LcR_okUAAAAAPYrPe-HK_0RULO1aZM15ENyM-Mf",
    "pageAction": "login"
  }
}

No v3 o pageAction é o campo que mais importa. Se a página chama grecaptcha.execute sem action, não mande o campo.

Solução: solution.token e solution.userAgent. Use o mesmo userAgentno envio: o token é avaliado junto com o ambiente que o gerou, e mandar outro User-Agent é um descasamento que a verificação do site enxerga. O nome do campo que recebe o token varia de site para site — ver seção 6.

4.4 Cloudflare Turnstile

Como reconhecer: uma caixa da Cloudflare ("Verifique que você é humano", às vezes só um ícone girando), o script https://challenges.cloudflare.com/turnstile/v0/api.js e um <div class="cf-turnstile" data-sitekey="0x4AAAA...">. A sitekey começa com 0x.

Este tipo resolve o widget, e não a tela de bloqueio da Cloudflare. A página inteira "Verificando se você é humano…" que aparece antes do site carregar (o desafio que gera o cookie cf_clearance) é outro produto e não é atendida. Se não há um widget dentro de um formulário do site, não é este tipo.

Exemplo:

{
  "clientKey": "cpsk_...",
  "task": {
    "type": "TurnstileTask",
    "websiteURL": "https://alvo.example/cadastro",
    "websiteKey": "0x4AAAAAAABkMYinukE8nzY"
  }
}

Solução: solution.token e solution.userAgent. O token vai no campo cf-turnstile-response (o site pode ter renomeado o campo com data-response-field-name). Ele vale 5 minutos e é verificável uma vez.


5. Consultar resultado — POST /v1/tasks/result

Corpo

Campo Tipo Obrigatório Descrição
clientKey string sim Sua chave de API.
taskId string sim O taskId devolvido na criação.

Estados

status Final? O que fazer
queued não Na fila. Consulte de novo em 3–5 s.
running não Está sendo resolvido. Consulte de novo em 3–5 s.
ready sim Sucesso. solution.token está preenchido.
failed sim Não resolvido. Veja errorCode e errorDescription.

Resposta pronta

{
  "errorId": 0,
  "taskId": "cps_9f3c1a8b7d2e4f60",
  "status": "ready",
  "solution": {
    "token": "P1_eyJ0eXAiOiJKV1Qi...",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
  }
}

Campos de solution

Campo Tipos Descrição
token todos O token a entregar ao site.
userAgent todos User-Agent do browser que gerou o token. Use o mesmo no envio.
callback reCAPTCHA v2 O nome que você mandou em callback, devolvido para ficar junto do token. Só aparece se você o mandou.

Resposta com falha

{
  "errorId": 1,
  "taskId": "cps_9f3c1a8b7d2e4f60",
  "status": "failed",
  "errorCode": "ERROR_SOLVE_TIMEOUT",
  "errorDescription": "o solve não completou dentro do prazo"
}

Ritmo e prazo do polling

  • Consulte a cada 3–5 s. Um solve típico leva dezenas de segundos; consultar mais rápido gasta ficha do seu limite de requisições sem antecipar nada.

404 com ERROR_TASK_NOT_FOUND significa taskId inexistente. Use na consulta o mesmo clientKey que criou a tarefa: uma tarefa de outra chave não é encontrada.


6. Usando o token recebido

O token é a prova de que o CAPTCHA foi resolvido. Quando uma pessoa resolve o CAPTCHA no site, o widget coloca esse token num campo escondido do formulário (ou entrega a uma função do site), e o site o envia ao próprio servidor junto com os dados do formulário. Você faz o mesmo, trocando o token do widget pelo que a API devolveu.

Cada site integra de um jeito, mas o caminho para descobrir o seu é sempre o mesmo.

Passo 1 — Descubra qual requisição leva o token

  1. Abra a página no Chrome ou Firefox e aperte F12. Vá à aba Network (Rede) e marque Preserve log (Manter registro).
  2. Preencha o formulário e resolva o CAPTCHA manualmente, uma vez, como um usuário comum. Envie.
  3. Na aba Network, procure a requisição que saiu nesse momento (geralmente um POST). Use o campo de busca da aba (Ctrl+F dentro da Network) com o nome do campo do tipo: h-captcha-response, g-recaptcha-response ou cf-turnstile-response. Se nada aparecer, busque pelo começo do token (costuma ser P1_ no hCaptcha, 03A no reCAPTCHA e 0. no Turnstile).
  4. Clique na requisição e anote: a URL, o método, os cabeçalhos (principalmente Content-Type, Cookie e Referer) e o corpo (aba Payload). O token é um dos campos do corpo.

Essa requisição é o que você vai reproduzir, com o token da API no lugar do token do widget.

Passo 2 — Escolha como enviar

A. Direto por HTTP (o mais comum). Refaça a requisição do passo 1 com a sua biblioteca HTTP, trocando o valor do token e usando o userAgent da solução:

import requests

sessao = requests.Session()
sessao.headers["User-Agent"] = solucao["userAgent"]

# 1) abra a página antes, para receber os cookies de sessão que o site exige
sessao.get("https://exemplo.com.br/login")

# 2) envie o formulário com o token no campo que você achou no passo 1
resposta = sessao.post(
    "https://exemplo.com.br/login",
    data={
        "usuario": "...",
        "senha": "...",
        "g-recaptcha-response": solucao["token"],   # nome do campo do seu site
    },
    headers={"Referer": "https://exemplo.com.br/login"},
)

Se o corpo da requisição do passo 1 era JSON, envie JSON (json= no lugar de data=), com o token no mesmo campo em que ele estava.

B. Dentro de um browser automatizado (Playwright, Selenium, Puppeteer). Preencha o campo escondido e envie o formulário como o site faria:

// hCaptcha: preencha os dois campos
document.querySelector('[name="h-captcha-response"]').value = token;
document.querySelector('[name="g-recaptcha-response"]').value = token;

// reCAPTCHA v2 (checkbox ou invisível) com botão de envio
document.querySelector('[name="g-recaptcha-response"]').value = token;
document.querySelector('form').submit();

// Turnstile
document.querySelector('[name="cf-turnstile-response"]').value = token;

No Playwright isso vai dentro de page.evaluate(...); no Selenium, de driver.execute_script(...).

Quando o site usa callback (comum no reCAPTCHA invisível)

Alguns sites não têm botão de envio "normal": o widget é criado com uma função (data-callback="onSubmit" no HTML, ou grecaptcha.render(elemento, {callback: onSubmit})), e é essa função que faz a página avançar quando recebe o token. Nesses sites, preencher o campo não faz nada — o token é válido, mas a página não anda, e não aparece erro nenhum. É preciso chamar a função:

window.onSubmit(token);   // e NÃO apenas preencher o campo
  • Como achar o nome: busque data-callback no HTML do widget (aba Elements) ou grecaptcha.render / turnstile.render no JavaScript (aba Sources, Ctrl+Shift+F).
  • Mande o nome em callback na criação da tarefa (só reCAPTCHA v2) e ele volta em solution.callback, junto do token.
  • Não conseguimos descobri-lo por você: a resolução acontece fora da página do site, e há sites em que o callback é uma função anônima, sem nome. Nesses, o caminho é o A (reproduzir a requisição por HTTP).

No reCAPTCHA v3

No v3 o site costuma chamar grecaptcha.execute(...) ao clicar em "Enviar" e colocar o token num campo escondido (o nome varia: g-recaptcha-response, token, recaptcha_token...) ou num campo de um JSON enviado por fetch. O passo 1 mostra qual é. Com o nome em mãos, use o caminho A ou B. Se a página gera o token de novo a cada envio, gere uma tarefa por envio.

Se o site recusar o token

Sintoma Causa provável
Site diz que o CAPTCHA é inválido ou expirou Token com mais de 2 min (5 min no Turnstile), ou já usado uma vez. Crie outra tarefa e use o token logo.
Site recusa, mas o token é novo websiteURL ou websiteKey errados (confira que são da página onde o widget está), ou User-Agent diferente de solution.userAgent.
Nada acontece, sem erro Site da variante callback: chame a função em vez de só preencher o campo.
v3: login/ação negada sem mensagem de CAPTCHA pageAction diferente do usado pela página, e o score caiu.
Página pede o CAPTCHA de novo Faltou o cookie de sessão: abra a página antes (passo A, item 1) na mesma sessão HTTP.

7. Usando seu próprio proxy

Preencha os cinco campos proxy* dentro de task. proxyAddress e proxyPort andam juntos; proxyLogin e proxyPassword só quando o proxy exige autenticação.

{
  "clientKey": "cpsk_sua_chave_aqui",
  "task": {
    "type": "HCaptchaTask",
    "websiteURL": "https://exemplo.com.br/login",
    "websiteKey": "10000000-ffff-ffff-ffff-000000000001",
    "proxyType": "http",
    "proxyAddress": "203.0.113.10",
    "proxyPort": 8080,
    "proxyLogin": "usuario",
    "proxyPassword": "senha"
  }
}

Um proxy inválido ou inalcançável devolve ERROR_INVALID_PROXY (400) na criação, ou ERROR_PROXY_AUTH_FAILED / ERROR_PROXY_NETWORK no resultado.


8. Idempotência

Envie o cabeçalho Idempotency-Key na criação para tornar o retry seguro:

curl -X POST https://capsolutio.kurierservicos.com.br/v1/tasks \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f14e45f-ceea-467a-9f2c-1b2c3d4e5f60" \
  -d '{ "clientKey": "...", "task": { "...": "..." } }'
  • Mesma chave, mesmo corpo → HTTP 200 com a tarefa original. Nada é criado e nada é cobrado de novo.
  • Mesma chave, corpo diferente → HTTP 409, ERROR_IDEMPOTENCY_CONFLICT.
  • A janela é de 1 hora.

9. Códigos de erro

Do pedido — corrija e reenvie

Código HTTP Significado
ERROR_MISSING_PARAMETER 400 Campo obrigatório ausente.
ERROR_INVALID_WEBSITE_URL 400 websiteURL não é uma URL http/https absoluta.
ERROR_INVALID_SITEKEY 400 websiteKey malformada.
ERROR_INVALID_DOMAIN 400 Domínio não aceito.
ERROR_INVALID_PROXY 400 Campos proxy* incompletos ou inválidos.
ERROR_NO_SUCH_METHOD 422 type não existe.
ERROR_UNSUPPORTED_CAPTCHA_TYPE 422 type existe, mas ainda não é resolvido.
ERROR_IDEMPOTENCY_CONFLICT 409 Idempotency-Key reutilizada com outro corpo.
ERROR_TASK_NOT_FOUND 404 taskId inexistente ou de outra conta.

Da conta — não adianta repetir

Código HTTP Significado
ERROR_KEY_DOES_NOT_EXIST 401 Chave ausente, desconhecida ou inativa.
ERROR_ACCOUNT_SUSPENDED 401 Conta suspensa.
ERROR_ZERO_BALANCE 402 Saldo zerado.
ERROR_NO_QUOTA_AVAILABLE 402 Cota total esgotada. Único 4xx sem Retry-After — esperar não resolve.

De capacidade — repita depois do Retry-After

Código HTTP Significado
ERROR_RATE_LIMIT_EXCEEDED 429 Requisições demais por segundo.
ERROR_NO_DAILY_QUOTA_AVAILABLE 429 Cota diária esgotada.
ERROR_NO_SLOT_AVAILABLE 503 Sobrecarga momentânea do servidor.
ERROR_TEMPORARILY_UNAVAILABLE 503 Indisponibilidade momentânea.

Do solve — aparecem em status: failed

Estes chegam no resultado, não na criação. Repetir a tarefa costuma resolver.

Código Significado
ERROR_CHALLENGE_TIMEOUT O desafio não completou no prazo.
ERROR_UNSUPPORTED_CHALLENGE Tipo de desafio ainda não suportado.
ERROR_CAPTCHA_UNSOLVABLE Não resolvido depois de todas as tentativas.
ERROR_PROXY_UNAVAILABLE Nenhum IP de saída disponível.
ERROR_PROXY_AUTH_FAILED O proxy recusou as credenciais.
ERROR_PROXY_NETWORK Falha de rede no caminho de saída.
ERROR_SOLVE_TIMEOUT O solve não completou no prazo.
ERROR_INTERNAL Falha interna do servidor.

10. Limites e cabeçalhos

Idempotency-Key

Opcional na criação. Ver seção 8.


11. Exemplos completos

Os exemplos recebem o objeto task inteiro, então servem para qualquer tipo da seção 4.

Python

import time
import requests

BASE = "https://capsolutio.kurierservicos.com.br"
CHAVE = "sua_chave_aqui"


def resolver(task: dict, timeout: int = 300) -> dict:
    """Cria a tarefa e faz polling até um estado final. Devolve a solução."""
    criada = requests.post(
        f"{BASE}/v1/tasks",
        json={"clientKey": CHAVE, "task": task},
        timeout=30,
    )
    criada.raise_for_status()
    task_id = criada.json()["taskId"]

    limite = time.monotonic() + timeout
    while time.monotonic() < limite:
        time.sleep(4)  # 3-5 s: consultar mais rápido não antecipa nada
        r = requests.post(
            f"{BASE}/v1/tasks/result",
            json={"clientKey": CHAVE, "taskId": task_id},
            timeout=30,
        )
        r.raise_for_status()
        dados = r.json()

        if dados["status"] == "ready":
            return dados["solution"]
        if dados["status"] == "failed":
            raise RuntimeError(f"{dados['errorCode']}: {dados.get('errorDescription')}")

    raise TimeoutError(f"tarefa {task_id} não concluiu em {timeout}s")


# hCaptcha
solucao = resolver({
    "type": "HCaptchaTask",
    "websiteURL": "https://exemplo.com.br/login",
    "websiteKey": "10000000-ffff-ffff-ffff-000000000001",
})

# reCAPTCHA v2 invisível
# solucao = resolver({"type": "RecaptchaV2Task", "websiteURL": "...", "websiteKey": "6L...", "isInvisible": True})
# reCAPTCHA v3
# solucao = resolver({"type": "RecaptchaV3Task", "websiteURL": "...", "websiteKey": "6L...", "pageAction": "login"})
# Turnstile
# solucao = resolver({"type": "TurnstileTask", "websiteURL": "...", "websiteKey": "0x4AAAA..."})

# Use o MESMO User-Agent na requisição final ao site (ver seção 6).
requests.post(
    "https://exemplo.com.br/login",
    headers={"User-Agent": solucao["userAgent"]},
    data={"h-captcha-response": solucao["token"], "usuario": "...", "senha": "..."},
)

Node.js

const BASE = "https://capsolutio.kurierservicos.com.br";
const CHAVE = "cpsk_sua_chave_aqui";

const dormir = (ms) => new Promise((r) => setTimeout(r, ms));

async function resolver(task, timeoutMs = 300_000) {
  const criada = await fetch(`${BASE}/v1/tasks`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ clientKey: CHAVE, task }),
  });
  const { taskId } = await criada.json();

  const limite = Date.now() + timeoutMs;
  while (Date.now() < limite) {
    await dormir(4000);
    const r = await fetch(`${BASE}/v1/tasks/result`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ clientKey: CHAVE, taskId }),
    });
    const dados = await r.json();

    if (dados.status === "ready") return dados.solution;
    if (dados.status === "failed") {
      throw new Error(`${dados.errorCode}: ${dados.errorDescription ?? ""}`);
    }
  }
  throw new Error(`tarefa ${taskId} não concluiu no prazo`);
}

const solucao = await resolver({
  type: "RecaptchaV2Task",
  websiteURL: "https://exemplo.com.br/login",
  websiteKey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
});
console.log(solucao.token);

PHP

<?php
const BASE  = 'https://capsolutio.kurierservicos.com.br';
const CHAVE = 'cpsk_sua_chave_aqui';

function chamar(string $rota, array $corpo): array {
    $ch = curl_init(BASE . $rota);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS     => json_encode($corpo),
        CURLOPT_TIMEOUT        => 30,
    ]);
    $resposta = curl_exec($ch);
    curl_close($ch);
    return json_decode($resposta, true);
}

$criada = chamar('/v1/tasks', [
    'clientKey' => CHAVE,
    'task' => [
        'type'       => 'TurnstileTask',
        'websiteURL' => 'https://exemplo.com.br/cadastro',
        'websiteKey' => '0x4AAAAAAABkMYinukE8nzY',
    ],
]);
$taskId = $criada['taskId'];

$limite = time() + 300;
while (time() < $limite) {
    sleep(4);
    $dados = chamar('/v1/tasks/result', ['clientKey' => CHAVE, 'taskId' => $taskId]);

    if ($dados['status'] === 'ready') {
        echo $dados['solution']['token'], PHP_EOL;
        exit(0);
    }
    if ($dados['status'] === 'failed') {
        fwrite(STDERR, $dados['errorCode'] . PHP_EOL);
        exit(1);
    }
}
fwrite(STDERR, "tarefa {$taskId} não concluiu no prazo" . PHP_EOL);
exit(1);

12. Boas práticas

Envie o userAgent de volta. O token é emitido para a sessão que o gerou. Enviar o token com outro User-Agent é a causa mais comum de "o token veio válido e o site recusou" — e no reCAPTCHA v3 derruba o score.

Use a URL real da página. websiteURL define o origin e o Referer que o widget observa. A raiz do domínio no lugar da página de login muda o que ele vê.

Mande os campos do tipo que o site declara. isInvisible certo no reCAPTCHA v2; pageAction certo no v3 evita score baixo;

Faça polling a cada 3–5 s. Mais rápido não antecipa nada e consome o seu limite de requisições.

Respeite o Retry-After. Ele vem do estado real do serviço e é melhor que qualquer backoff que você calcule.

Trate failed como retentável, com teto. Códigos de solve costumam passar na tentativa seguinte. Recrie a tarefa, duas ou três vezes, não indefinidamente.

Não faça polling eterno. Toda tarefa tem um tempo limite. Defina um ponto de parada no seu polling.

Use o token logo. Ele vale cerca de 2 minutos no hCaptcha e no reCAPTCHA e 5 minutos no Turnstile, contados da resolução, e só pode ser usado uma vez. Não o guarde em fila; crie a tarefa quando for usar o token.