Solver de captcha em Go com net/http: exemplo funcional (2026)
Um solver de captcha em Go não precisa de pacotes de terceiros. O net/http e o encoding/json da biblioteca padrão cobrem todo o fluxo: um POST para a API da Scarnox, uma resposta JSON e o envio do formulário ao site. Este guia constrói esse fluxo para o PopularCaptcha, que a Scarnox resolve hoje de forma proxyless.
Comece com crédito de teste grátis, sem cartão e rode o programa em uma página que você controla antes de usá-lo em outro lugar.
Requisitos
- Go 1.21 ou mais recente.
- Uma chave de API da Scarnox, lida de uma variável de ambiente.
- O sitekey do widget PopularCaptcha da página. Se precisar de ajuda para encontrá-lo, veja como encontrar o sitekey de um captcha.
O programa completo
Este arquivo único resolve o captcha, refaz as falhas transitórias e envia o token ao formulário. Salve-o como main.go:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"time"
)
const apiURL = "https://api.scarnox.com/api/tasks/create"
// A chamada de resolução fica bloqueada até o token estar pronto, por isso o timeout é longo.
var apiClient = &http.Client{Timeout: 185 * time.Second}
type createRequest struct {
CaptchaType string `json:"captcha_type"`
SiteKey string `json:"site_key"`
SiteURL string `json:"site_url"`
IsInvisible bool `json:"is_invisible,omitempty"`
}
type createResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
Solution struct {
Token string `json:"token"`
} `json:"solution"`
}
func solveCaptcha(apiKey, sitekey, pageURL string, invisible bool) (string, error) {
body, err := json.Marshal(createRequest{
CaptchaType: "PopularCaptchaTaskProxyless",
SiteKey: sitekey,
SiteURL: pageURL,
IsInvisible: invisible,
})
if err != nil {
return "", err
}
req, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewReader(body))
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := apiClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
raw, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var out createResponse
if err := json.Unmarshal(raw, &out); err != nil {
return "", fmt.Errorf("HTTP %d, response was not JSON: %s", resp.StatusCode, raw)
}
if out.ErrorID != 0 {
return "", fmt.Errorf("%s: %s", out.ErrorCode, out.ErrorDescription)
}
return out.Solution.Token, nil
}
// solveWithRetry refaz falhas transitórias. Resoluções com falha não são cobradas.
func solveWithRetry(apiKey, sitekey, pageURL string, attempts int) (string, error) {
var lastErr error
for i := 1; i <= attempts; i++ {
token, err := solveCaptcha(apiKey, sitekey, pageURL, false)
if err == nil {
return token, nil
}
lastErr = err
log.Printf("attempt %d failed: %v", i, err)
time.Sleep(time.Duration(i) * time.Second)
}
return "", lastErr
}
func main() {
apiKey := os.Getenv("SCARNOX_API_KEY")
if apiKey == "" {
log.Fatal("set SCARNOX_API_KEY")
}
const pageURL = "https://example.com/signup"
const sitekey = "YOUR-SITEKEY-UUID"
token, err := solveWithRetry(apiKey, sitekey, pageURL, 3)
if err != nil {
log.Fatal(err)
}
// Um cookie jar mantém a sessão à qual o site vincula o captcha.
jar, err := cookiejar.New(nil)
if err != nil {
log.Fatal(err)
}
site := &http.Client{Jar: jar, Timeout: 60 * time.Second}
form := url.Values{}
form.Set("email", "you@example.com")
form.Set("h-captcha-response", token)
form.Set("g-recaptcha-response", token)
resp, err := site.PostForm(pageURL, form)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Println("Status:", resp.Status)
}
Execute:
export SCARNOX_API_KEY=scarnox_YOURAPIKEY
go run main.go
Como as peças se encaixam
A requisição. solveCaptcha converte uma struct no corpo JSON que a API espera e define o cabeçalho Authorization. O prefixo Bearer é obrigatório.
O timeout. A API fica bloqueada até o token estar pronto, então o timeout do cliente é de 185 segundos. Não defina um valor menor, senão você cancelará resoluções que estavam prestes a terminar.
A verificação de erro. A API retorna errorId com 0 em caso de sucesso. Qualquer outro valor vem com errorCode e errorDescription, e a função devolve esses dados como erro. O token fica em solution.token.
O envio. O token vai para o campo h-captcha-response, e g-recaptcha-response recebe o mesmo valor. A maioria dos sites lê um ou outro.
Use um cookie jar
Muitas páginas de cadastro e busca vinculam o captcha a uma sessão. O cookiejar no cliente do site mantém esses cookies entre o GET que mostra o formulário e o POST que o envia. Se você pular essa etapa, o site pode rejeitar um token que, de outra forma, seria válido.
Tempo
O token expira em cerca de dois minutos, então resolva logo antes de enviar. Não resolva no início de um job longo e depois espere. Expiração de token de captcha explica como dimensionar essa janela.
Widgets invisíveis
Se o widget da página for invisível, defina o argumento invisible como true. A requisição então inclui is_invisible: true. Algumas páginas também precisam de uma função de callback chamada com o token. Verifique o script da página para encontrá-la. O guia captcha invisível lista os padrões.
Concorrência
Em Go é fácil resolver muitos captchas ao mesmo tempo com goroutines. Mantenha o número de resoluções simultâneas dentro do que a sua conta e o site alvo conseguem suportar. Um único domínio não deve receber uma rajada de requisições, mesmo que o seu solver consiga acompanhar. Respeite o robots.txt e os termos do site, e limite o scraping a dados públicos que você tem permissão para coletar.
Preços
A Scarnox cobra por resolução bem-sucedida. Resoluções com falha são gratuitas. Veja os preços atuais para as tarifas. Para testar a API, comece com crédito de teste grátis, sem cartão.