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
clientKeyno corpo - CAPTCHA suportado: hCaptcha, reCAPTCHA v2 (checkbox e invisível), reCAPTCHA v3 e Cloudflare Turnstile
Índice
- Início rápido
- Autenticação
- Criar tarefa —
POST /v1/tasks - Tipos de CAPTCHA
- Consultar resultado —
POST /v1/tasks/result - Usando o token recebido
- Usando seu próprio proxy
- Idempotência
- Códigos de erro
- Limites e cabeçalhos
- Exemplos completos
- 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. |
websiteURLewebsiteKeysã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 chamagrecaptcha.execute('6L...', {action: '...'})com a sitekey como argumento. O v2 invisível carregaapi.jssemrender=(ou comrender=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
statusvirarready.
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 chamagrecaptcha.executesemaction, 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
- Abra a página no Chrome ou Firefox e aperte F12. Vá à aba Network (Rede) e marque Preserve log (Manter registro).
- Preencha o formulário e resolva o CAPTCHA manualmente, uma vez, como um usuário comum. Envie.
- 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-responseoucf-turnstile-response. Se nada aparecer, busque pelo começo do token (costuma serP1_no hCaptcha,03Ano reCAPTCHA e0.no Turnstile). - Clique na requisição e anote: a URL, o método, os cabeçalhos
(principalmente
Content-Type,CookieeReferer) 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-callbackno HTML do widget (aba Elements) ougrecaptcha.render/turnstile.renderno JavaScript (aba Sources, Ctrl+Shift+F). - Mande o nome em
callbackna criação da tarefa (só reCAPTCHA v2) e ele volta emsolution.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.