Scarnox · Blog

Captcha-solver in Go: werkend voorbeeld met net/http (2026)

Een captcha-solver in Go heeft geen pakketten van derden nodig. De net/http en encoding/json uit de standaardbibliotheek dekken de hele flow: één POST naar de Scarnox-API, één JSON-respons, en één formulierverzending naar de site. Deze gids bouwt die flow voor PopularCaptcha, dat Scarnox vandaag zonder proxy oplost.

Begin met gratis proefcredit, zonder creditcard en draai het programma tegen een pagina die je beheert voordat je het ergens anders gebruikt.

Vereisten

Het volledige programma

Dit ene bestand lost de captcha op, probeert tijdelijke fouten opnieuw en dient het token in bij het formulier. Sla het op als 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"

// De oplossingsaanroep blokkeert totdat het token klaar is, dus de time-out is lang.
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 probeert tijdelijke fouten opnieuw. Mislukte oplossingen worden niet in rekening gebracht.
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)
	}

	// Een cookiejar houdt de sessie vast waaraan de site de captcha koppelt.
	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)
}

Uitvoeren:

export SCARNOX_API_KEY=scarnox_YOURAPIKEY
go run main.go

Hoe de onderdelen samenwerken

Het verzoek. solveCaptcha zet een struct om in de JSON-body die de API verwacht en zet de header Authorization. Het voorvoegsel Bearer is verplicht.

De time-out. De API blokkeert totdat het token klaar is, dus de client-time-out is 185 seconden. Zet hem niet lager, anders annuleer je oplossingen die bijna klaar waren.

De foutcontrole. De API geeft errorId als 0 bij succes. Elke andere waarde komt met errorCode en errorDescription, en de functie geeft ze terug als fout. Het token staat in solution.token.

Het verzenden. Het token gaat in het veld h-captcha-response, en g-recaptcha-response krijgt dezelfde waarde. De meeste sites lezen het ene of het andere.

Gebruik een cookiejar

Veel aanmeld- en zoekpagina's koppelen de captcha aan een sessie. De cookiejar op de site-client houdt die cookies vast tussen de GET die het formulier toont en de POST die het verzendt. Als je hem overslaat, kan de site een token weigeren dat anders geldig was.

Timing

Het token verloopt na ongeveer twee minuten, dus los op vlak voordat je verzendt. Los niet op aan het begin van een lange job en wacht daarna. Verlopen captchatokens legt uit hoe je het tijdvenster inschat.

Onzichtbare widgets

Als de widget van de pagina onzichtbaar is, zet het argument invisible dan op true. Het verzoek bevat dan is_invisible: true. Sommige pagina's hebben ook een callbackfunctie nodig die met het token wordt aangeroepen. Controleer het script van de pagina daarvoor. De gids over onzichtbare captcha somt de patronen op.

Gelijktijdigheid

Go maakt het makkelijk om met goroutines veel captchas tegelijk op te lossen. Houd het aantal gelijktijdige oplossingen binnen wat je account en de doelsite aankunnen. Eén domein mag geen uitbarsting van verzoeken krijgen, zelfs als je solver ze kan bijhouden. Respecteer robots.txt en de voorwaarden van de site, en houd het scrapen beperkt tot publieke data die je mag verzamelen.

Prijzen

Scarnox rekent per geslaagde oplossing. Mislukte oplossingen zijn gratis. Zie actuele prijzen voor de tarieven. Om de API uit te proberen, begin je met gratis proefcredit, zonder creditcard.

Volgende stappen