O token voltou válido da CaptchaAI e o formulário recusou o envio mesmo assim. Em integrações com gateway de proxy a causa costuma ser uma só: o IP mudou entre o carregamento da página e o POST. Quem resolve o desafio e quem envia a requisição precisam sair pelo mesmo endereço. Este guia amarra as duas pontas — o Smartproxy cuidando do egress de rede autorizado e a CaptchaAI resolvendo reCAPTCHA v2 e Turnstile — em Python, Selenium e Node.js.
Como os dois serviços se dividem no fluxo
Dois serviços, dois papéis — confundi-los é o erro mais comum:
- Smartproxy define de onde sai o tráfego: carrega a página, sustenta a sessão e envia o formulário.
- A CaptchaAI recebe só os dados do desafio (
pageurlegooglekeyousitekey) e devolve o token, sem precisar do seu gateway.
O roteiro não muda: carregue a página, extraia a sitekey, envie a tarefa e devolva o token ao formulário pela mesma sessão de saída. O reCAPTCHA v2 fica pronto em menos de 60 s e o Turnstile em menos de 10 s; a sessão precisa cobrir essa janela.
Qual pool do Smartproxy usar em cada cenário
| Tipo | Pool | Indicado para | Frequência de CAPTCHA |
|---|---|---|---|
| Residencial | mais de 55 milhões de IPs | coleta autorizada em geral | baixa |
| Datacenter | mais de 100 mil IPs | volume alto, latência baixa | média a alta |
| Mobile | mais de 10 milhões de IPs | páginas servidas a celulares | muito baixa |
| ISP | estáticos de nível residencial | fluxos longos com sessão | baixa |
Para QA e monitoramento autorizado, o pool residencial cobre quase tudo. O datacenter custa menos por GB, mas é onde o desafio aparece mais — e cada desafio é uma tarefa a mais.
Passo 1: gateway e chave de API em Python
import requests
import time
SMARTPROXY_USER = "spuser"
SMARTPROXY_PASS = "sppassword"
SMARTPROXY_HOST = "gate.smartproxy.com"
SMARTPROXY_PORT = 10001
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
proxies = {
"http": f"http://{SMARTPROXY_USER}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}",
"https": f"http://{SMARTPROXY_USER}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}",
}
def fetch_page(url):
return requests.get(url, proxies=proxies, timeout=30)
def solve_captcha(site_url, sitekey, captcha_type="recaptcha_v2"):
submit_data = {
"key": CAPTCHAAI_KEY,
"pageurl": site_url,
"json": 1,
}
if captcha_type == "turnstile":
submit_data["method"] = "turnstile"
submit_data["sitekey"] = sitekey
else:
submit_data["method"] = "userrecaptcha"
submit_data["googlekey"] = sitekey
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=submit_data)
data = resp.json()
if data["status"] != 1:
raise Exception(f"Submit failed: {data['request']}")
task_id = data["request"]
for _ in range(60):
time.sleep(5)
resp = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY,
"action": "get",
"id": task_id,
"json": 1,
})
data = resp.json()
if data["request"] == "CAPCHA_NOT_READY":
continue
if data["status"] == 1:
return data["request"]
raise Exception(f"Solve: {data['request']}")
raise TimeoutError("Timeout")
Duas coisas economizam depuração: mantenha SMARTPROXY_USER, SMARTPROXY_PASS e CAPTCHAAI_KEY em variáveis de ambiente e trate CAPCHA_NOT_READY como estado normal do polling, não como erro.
O parâmetro method decide o tipo: userrecaptcha com googlekey para reCAPTCHA v2, turnstile com sitekey para o Turnstile. O valor devolvido é o token que o formulário espera em g-recaptcha-response (ou em cf-turnstile-response).
Passo 2: prender o IP durante todo o fluxo
import random
import string
def get_sticky_proxy(session_duration_minutes=10):
"""Create a sticky session proxy (same IP for duration)."""
session_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
proxy_url = (
f"http://{SMARTPROXY_USER}"
f"-session-{session_id}"
f"-sessionduration-{session_duration_minutes}"
f":{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
)
return {"http": proxy_url, "https": proxy_url}
# Use same IP for entire CAPTCHA workflow
sticky = get_sticky_proxy(session_duration_minutes=10)
# Page load
resp = requests.get("https://staging.example.com/qa-form", proxies=sticky)
# Solve CAPTCHA
token = solve_captcha("https://staging.example.com/qa-form", "SITEKEY_HERE")
# Submit with same IP
resp = requests.post(
"https://target.com/submit",
data={"g-recaptcha-response": token},
proxies=sticky,
)
O sufixo -session-ID amarra a sessão a um IP; -sessionduration-N define por quantos minutos ele fica reservado. Dez minutos cobrem o ciclo carregar → resolver → enviar. Em fluxos de várias etapas, suba para 30 minutos: trocar de IP no meio do caminho invalida o token que você acabou de pagar.
Passo 3: escolher o país de saída
# Smartproxy country targeting via username
def get_country_proxy(country_code):
proxy_url = (
f"http://{SMARTPROXY_USER}"
f"-country-{country_code}"
f":{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
)
return {"http": proxy_url, "https": proxy_url}
# US proxy
us_proxy = get_country_proxy("us")
# UK proxy
uk_proxy = get_country_proxy("gb")
# Germany proxy
de_proxy = get_country_proxy("de")
A segmentação por país também entra pelo nome de usuário: -country-us, -country-gb e assim por diante. Isso pesa quando a aplicação responde diferente por região.
Caso comum em times brasileiros: os workers rodam em sa-east-1 (São Paulo) e a suíte valida a mesma página com saída no Brasil e nos Estados Unidos. Fixe o país por execução e registre o código ao lado do task_id — assim dá para comparar tempo de resolução por região.
Selenium: o mesmo endereço dentro do navegador
from selenium import webdriver
from selenium.webdriver.common.by import By
def create_smartproxy_driver(country=None, sticky_session=None):
proxy_user = SMARTPROXY_USER
if country:
proxy_user += f"-country-{country}"
if sticky_session:
proxy_user += f"-session-{sticky_session}"
proxy_url = f"{proxy_user}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
options = webdriver.ChromeOptions()
options.add_argument(f"--proxy-server=http://{SMARTPROXY_HOST}:{SMARTPROXY_PORT}")
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("--window-size=1920,1080")
# For authenticated proxies, use seleniumwire or extension
return webdriver.Chrome(options=options)
def scrape_with_captcha(url, country="us"):
session_id = "".join(random.choices(string.ascii_lowercase, k=8))
driver = create_smartproxy_driver(country=country, sticky_session=session_id)
try:
driver.get(url)
time.sleep(3)
sitekey = driver.execute_script(
"return document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')"
)
if sitekey:
token = solve_captcha(url, sitekey)
driver.execute_script(f"""
document.querySelector('#g-recaptcha-response').value = '{token}';
""")
driver.find_element(By.CSS_SELECTOR, "form").submit()
time.sleep(3)
return driver.page_source
finally:
driver.quit()
Uma ressalva: o --proxy-server do Chrome não aceita usuário e senha na URL. Para gateways autenticados, use o Selenium Wire ou uma extensão que injete as credenciais. Sem isso, o navegador devolve 407. Como o driver nasce amarrado a uma sessão, o envio sai do mesmo IP.
Node.js: a mesma lógica com axios
const axios = require("axios");
const HttpsProxyAgent = require("https-proxy-agent");
const CAPTCHAAI_KEY = "YOUR_API_KEY";
function getSmartproxyAgent(options = {}) {
let user = "spuser";
if (options.country) user += `-country-${options.country}`;
if (options.session) user += `-session-${options.session}`;
return new HttpsProxyAgent(
`http://${user}:[email protected]:10001`
);
}
async function scrapeWithCaptcha(url, sitekey) {
const agent = getSmartproxyAgent({
country: "us",
session: `sess-${Date.now()}`,
});
// Fetch page through proxy
const pageResp = await axios.get(url, { httpsAgent: agent });
// Solve CAPTCHA via CaptchaAI (no proxy needed)
const submitResp = await axios.post(
"https://ocr.captchaai.com/in.php",
null,
{
params: {
key: CAPTCHAAI_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: url,
json: 1,
},
}
);
const taskId = submitResp.data.request;
// Poll for result
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: {
key: CAPTCHAAI_KEY,
action: "get",
id: taskId,
json: 1,
},
});
if (result.data.request === "CAPCHA_NOT_READY") continue;
if (result.data.status === 1) return result.data.request;
}
throw new Error("Timeout");
}
Aqui o HttpsProxyAgent carrega o gateway e a chamada à CaptchaAI vai sem agente. O session derivado de Date.now() serve para testes; em produção, prefira um identificador ligado ao job, para correlacionar sessão, task_id e resultado no log.
Execução paralela e o limite de threads
from concurrent.futures import ThreadPoolExecutor, as_completed
def process_url(url):
session_id = "".join(random.choices(string.ascii_lowercase, k=8))
proxy = get_sticky_proxy(10)
try:
resp = requests.get(url, proxies=proxy, timeout=30)
# Check if CAPTCHA is present (simplified detection)
if "data-sitekey" in resp.text:
import re
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if match:
sitekey = match.group(1)
token = solve_captcha(url, sitekey)
return {"url": url, "status": "solved", "token": token[:30]}
return {"url": url, "status": "no_captcha"}
except Exception as e:
return {"url": url, "status": "error", "error": str(e)}
urls = [
"https://site1.com/page",
"https://site2.com/page",
"https://site3.com/page",
]
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(process_url, u): u for u in urls}
for future in as_completed(futures):
result = future.result()
print(f"[{result['status']}] {result['url']}")
O max_workers do exemplo precisa conversar com o seu plano. A CaptchaAI cobra por thread simultânea, não por resolução: cada plano inclui resoluções ilimitadas dentro das threads contratadas. Cinco workers casam com o BASIC (US$ 15/mês, 5 threads); para cinquenta em paralelo, o plano correspondente é o ADVANCE (US$ 90/mês, 50 threads).
Abrir mais workers do que threads não gera erro — só empilha fila e alonga o tempo total.
Checklist antes de culpar o solver
- Carregamento e envio usam a mesma sessão fixa.
- A sessão dura mais que o tempo de resolução observado.
- O
pageurlenviado é idêntico à URL carregada. - A sitekey veio do DOM renderizado, não de um HTML em cache.
- O token foi usado uma única vez.
Solução de problemas
| Sintoma | Causa provável | Correção |
|---|---|---|
407 Proxy Authentication Required |
credenciais fora do formato | confira o painel do Smartproxy |
| IP muda no meio da sessão | sessão fixa não configurada | acrescente -session-ID ao usuário |
| Desafio em quase toda requisição | saída pelo endpoint de datacenter | use o gateway residencial |
| Token recusado após a resolução | IP mudou entre carregar e enviar | aumente a duração da sessão |
ERROR_ZERO_BALANCE em in.php |
saldo esgotado | recarregue antes de reiniciar a fila |
Escopo autorizado e LGPD
O fluxo serve a ambientes que você tem permissão para acessar: staging próprio, endpoints internos, monitoramento contratado. Havendo dados pessoais, considere as obrigações da LGPD (RGPD, em Portugal): guarde só o necessário e defina retenção. Nos exemplos de QA, use dados fictícios e URLs de staging.
Perguntas frequentes
Por que o token é recusado se a resolução deu certo?
Quase sempre porque o IP de saída mudou entre carregar e enviar. Prenda a sessão com -session-ID e confirme que o pageurl enviado é o da página carregada.
Quantas threads eu preciso para rodar 20 workers em paralelo?
Pelo menos 20 — cada tarefa em voo ocupa uma thread. O STANDARD (US$ 30/mês, 15 threads) fica curto nessa carga; o degrau seguinte é o ADVANCE (US$ 90/mês, 50 threads). Confira a fila nos logs antes de trocar de plano.
Em quanto tempo o desafio costuma ser resolvido?
Nos tipos suportados, o reCAPTCHA v2 fica pronto em menos de 60 s e o Turnstile em menos de 10 s, com alta taxa de sucesso. Dimensione a sessão por esses tetos, não pelo caso médio.
E o hCaptcha, entra nesse fluxo?
Não. O hCaptcha e o FunCaptcha não estão entre os tipos atendidos pela CaptchaAI. A lista suportada é reCAPTCHA v2 e v3, Cloudflare Turnstile e Cloudflare Challenge, GeeTest v3 e CAPTCHAs de imagem, grade e BLS.
A chamada da CaptchaAI precisa passar pelo gateway?
Não. A API funciona de qualquer origem; o que precisa ser consistente é o IP do cliente que carrega a página e envia o formulário.
Guias relacionados
- Bright Data + CaptchaAI pelo gateway
- Rotação de IPs de saída na resolução de CAPTCHA
- Qualidade do proxy e taxa de resolução
Ligue o gateway do Smartproxy à API da CaptchaAI — crie sua chave de API e resolva o primeiro reCAPTCHA v2 pelo mesmo IP de saída.