Skip to content

Repository files navigation

CaptchaAI Go SDK

Go client for the CaptchaAI captcha-solving API.

Call one method per captcha type; the SDK handles HTTP, polling, retries, and error mapping. Every solve returns a single SolveResult.


Table of Contents


Requirements

  • Go 1.22+
  • Zero third-party dependencies (net/http and the standard library only)

Installation

go get github.com/CaptchaAI/captchaai-go

Quick start

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/CaptchaAI/captchaai-go"
)

func main() {
	ctx := context.Background()

	client, err := captchaai.New(ctx, "YOUR_32_CHAR_API_KEY")
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	result, err := client.RecaptchaV2(ctx,
		"6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
		"https://google.com/recaptcha/api2/demo",
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.String()) // g-recaptcha-response token
}

New validates the API key and reads the account thread cap via one threadsinfo call. A bad key fails immediately with ErrInvalidKey.

Every method accepts context.Context first for cancellation and deadlines. There is no separate async client type — use goroutines and context when you need concurrent solves (see Other methods).


Configuration

Create a client with your API key:

client, err := captchaai.New(ctx, "YOUR_32_CHAR_API_KEY",
	captchaai.WithProxy("user:pass@host:port", "HTTP"),
	captchaai.WithAutoRetry(true),
	captchaai.WithMaxRetries(5),
	captchaai.WithBaseURL("https://ocr.captchaai.com"),
	captchaai.WithThreadBusyTimeout(120),
	captchaai.WithMaxPollSeconds(180),
)
Option Default Description
apiKey (constructor arg) (required) 32-character CaptchaAI API key
WithProxy(proxy, proxytype) none "user:pass@host:port" applied to every solve
WithAutoRetry(bool) true Retry transient errors with exponential backoff
WithMaxRetries(n) 5 0 disables retries; n > 0 enables up to n submit attempts (overrides WithAutoRetry when set last)
WithBaseURL(url) https://ocr.captchaai.com API host for in.php / res.php
WithThreadBusyTimeout(seconds) 120 Max seconds retrying when all account threads are busy
WithMaxPollSeconds(seconds) 180 Poll deadline before ErrTimeout
WithHTTPClient(*http.Client) internal Custom HTTP client for tests or custom transport

Call client.Close() when finished to release idle HTTP connections.

Retry behaviour

Setting Behaviour
WithAutoRetry(true) (default) Retry transient submit/proxy errors and thread-busy conditions with backoff
WithAutoRetry(false) Raise immediately on transient errors; also fast-fails when the local in-flight count hits the thread cap
WithMaxRetries(N) (N > 0) Like WithAutoRetry(true), capped at N submit attempts
WithMaxRetries(0) Like WithAutoRetry(false)

Fatal errors (ErrInvalidKey, ErrValidation, ErrUnsolvable, ErrNoThreads, and similar) always return immediately.


Solve result

Every solve method returns a SolveResult:

result.TaskID    // string — task ID from submit (always present)
result.Solution  // any — string token, []any cell indices, or map[string]any structured answer
result.UserAgent // string — set for Enterprise / Cloudflare Challenge / CaptchaFox when the API returns it
result.Raw       // map[string]any — full API response (debugging)
result.RawText    // string — unparsed solution string before JSON parsing (structured types)

result.String()  // token string when Solution is a string; otherwise fmt.Sprintf("%v", Solution)

Reuse result.UserAgent on the target site when it is present (reCAPTCHA Enterprise, Cloudflare Challenge, CaptchaFox).

Captcha type Typical Solution type UserAgent
Normal, reCAPTCHA v2/v3 (standard/invisible), Turnstile, Friendly Captcha string empty
reCAPTCHA v2/v3 Enterprise, Cloudflare Challenge, CaptchaFox string set when API returns it
Grid, BLS []any (parsed JSON) empty
GeeTest, Lemin map[string]any (parsed JSON) empty

Normal captcha

Solve a text/image captcha. image may be a file path, URL, data-URI, raw base64 string, or []byte.

result, err := client.Normal(ctx, "captcha.png",
	captchaai.WithNumeric(1),       // 0=any, 1=digits, 2=letters, 3=digits+letters, 4=no digits
	captchaai.WithMinLen(4),
	captchaai.WithMaxLen(6),
	captchaai.WithPhrase(0),        // 0=single word, 1=multi-word
	captchaai.WithCaseSensitive(0), // 0=insensitive, 1=case-sensitive
	captchaai.WithLang("en"),
	captchaai.WithInstructions("Type the characters you see"),
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution)

Call options: WithNumeric, WithMinLen, WithMaxLen, WithPhrase, WithCaseSensitive, WithLang, WithInstructions, WithCallProxy.


Grid captcha

Solve a tile-selection captcha. gridSize must be "3x3" or "4x4". Solution is a list of cell indices.

result, err := client.Grid(ctx,
	"grid.png",
	"select all traffic lights",
	"3x3",
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution)

Call options: WithCallProxy.


BLS captcha

Solve a BLS multi-image captcha. Pass exactly 9 images (path, URL, base64, or []byte each). Solution is a list of cell indices.

images := make([]any, 9)
for i := range images {
	images[i] = fmt.Sprintf("img%d.png", i)
}

result, err := client.BLS(ctx, images, "YOUR_INSTRUCTIONS")
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution)

Call options: WithCallProxy.


reCAPTCHA v2

Solve reCAPTCHA v2 (standard, invisible, or enterprise). Pass sitekey and page url. Enterprise results may include UserAgent.

// Standard
result, err := client.RecaptchaV2(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.String())

// Invisible
result, err = client.RecaptchaV2(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
	captchaai.WithInvisible(),
)

// Enterprise (optional action; result may include UserAgent)
result, err = client.RecaptchaV2(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
	captchaai.WithEnterprise(),
	captchaai.WithAction("login"),
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution, result.UserAgent)

Call options: WithInvisible, WithEnterprise, WithAction, WithCookies, WithUserAgent, WithCallProxy.


reCAPTCHA v3

Solve reCAPTCHA v3 (standard or enterprise). WithAction is required — the API registry rejects submits without an action.

result, err := client.RecaptchaV3(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
	captchaai.WithAction("login"),
	captchaai.WithMinScore(0.3),
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.String())

// Enterprise
result, err = client.RecaptchaV3(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
	captchaai.WithAction("login"),
	captchaai.WithEnterprise(),
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution, result.UserAgent)

Call options: WithEnterprise, WithAction, WithMinScore, WithCookies, WithUserAgent, WithCallProxy.


Cloudflare Turnstile

Solve a Cloudflare Turnstile widget.

result, err := client.Turnstile(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.String())

Call options: WithCookies, WithUserAgent, WithCallProxy.


Cloudflare Challenge

Solve a Cloudflare interstitial challenge page. A proxy is mandatory (client-level or per-call). The result includes UserAgent to reuse on the target site.

client, err := captchaai.New(ctx, "YOUR_32_CHAR_API_KEY",
	captchaai.WithProxy("user:pass@host:port", "HTTP"),
)
if err != nil {
	log.Fatal(err)
}
defer client.Close()

result, err := client.CloudflareChallenge(ctx, "https://example.com")
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution, result.UserAgent)

Call options: WithCookies, WithUserAgent, WithCallProxy.


GeeTest

Solve GeeTest v3. Solution is a map[string]any with challenge, validate, and seccode.

result, err := client.Geetest(ctx,
	"YOUR_GT",
	"YOUR_CHALLENGE",
	"https://example.com",
)
if err != nil {
	log.Fatal(err)
}

m := result.Solution.(map[string]any)
fmt.Println(m["challenge"])
fmt.Println(m["validate"])
fmt.Println(m["seccode"])

Call options: WithCookies, WithUserAgent, WithCallProxy.


CaptchaFox

Solve a CaptchaFox slider challenge. A proxy is mandatory. Reuse result.UserAgent when submitting the token.

client, err := captchaai.New(ctx, "YOUR_32_CHAR_API_KEY",
	captchaai.WithProxy("user:pass@host:port", "HTTP"),
)
if err != nil {
	log.Fatal(err)
}
defer client.Close()

result, err := client.Captchafox(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution, result.UserAgent)

Call options: WithCookies, WithUserAgent, WithCallProxy.


Friendly Captcha

Solve a Friendly Captcha proof-of-work challenge. No proxy is required.

result, err := client.FriendlyCaptcha(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
	captchaai.WithVersion("v1"), // optional
)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.String())

Call options: WithVersion, WithCallProxy.


Lemin

Solve a Lemin puzzle. Solution is a map[string]any with answer and challenge_uuid.

result, err := client.Lemin(ctx,
	"YOUR_CAPTCHA_ID",
	"lemin-cropped-captcha",
	"https://example.com",
	captchaai.WithAPIServer("api.leminnow.com"), // optional
)
if err != nil {
	log.Fatal(err)
}

m := result.Solution.(map[string]any)
fmt.Println(m["answer"])
fmt.Println(m["challenge_uuid"])

Call options: WithAPIServer, WithCallProxy.


Other methods

Thread usage

CaptchaAI is thread-based. Check current usage:

usage, err := client.ThreadsInfo(ctx)
if err != nil {
	log.Fatal(err)
}
// usage.Threads, usage.WorkingThreads

Manual submit / fetch

Submit now and poll later (params use API names, e.g. googlekey, pageurl):

taskID, err := client.Send(ctx, "recaptcha_v2", map[string]any{
	"googlekey": "YOUR_SITEKEY",
	"pageurl":   "https://example.com",
})
if err != nil {
	log.Fatal(err)
}

result, err := client.GetResult(ctx, "recaptcha_v2", taskID)
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Solution)

Close

client.Close()

Concurrency

This SDK has no separate async client (unlike the Python AsyncCaptchaAI). All methods are blocking calls that respect context.Context for cancellation and deadlines.

For concurrent solves, launch goroutines and pass a shared or derived context:

var wg sync.WaitGroup
for _, job := range jobs {
	wg.Add(1)
	go func(sitekey, pageURL string) {
		defer wg.Done()
		result, err := client.Turnstile(ctx, sitekey, pageURL)
		// handle result / err
	}(job.Sitekey, job.URL)
}
wg.Wait()

When WithAutoRetry(false) or WithMaxRetries(0), the client tracks in-flight solves locally and returns ErrThreadLimit when the account thread cap is reached. With auto-retry enabled (default), the SDK retries thread-busy conditions server-side up to WithThreadBusyTimeout.


Error handling

All SDK errors are *captchaai.Error with a Kind field. Use errors.As to inspect the kind, or errors.Is against sentinel values:

result, err := client.RecaptchaV2(ctx, sitekey, pageURL)
if err != nil {
	var sdkErr *captchaai.Error
	if errors.As(err, &sdkErr) {
		switch sdkErr.Kind {
		case captchaai.KindInvalidKey:
			// wrong-length key, or API rejected the key
		case captchaai.KindValidation:
			// missing/invalid params, bad image input, missing required proxy, etc.
		case captchaai.KindProxy:
			// bad proxy or proxy connection failed
		case captchaai.KindThreadLimit:
			// all account threads busy (and retries skipped or exhausted)
		case captchaai.KindNoThreads:
			// account expired or has no active plan
		case captchaai.KindUnsolvable:
			// service could not solve the captcha
		case captchaai.KindNetwork:
			// could not reach the API
		case captchaai.KindTimeout:
			// poll deadline exceeded before a result was ready
		case captchaai.KindAPI:
			// unexpected / malformed API response
		}
	}

	// Or use sentinels with errors.Is:
	if errors.Is(err, captchaai.ErrThreadLimit) {
		// all threads busy
	}
}
Sentinel When raised
ErrInvalidKey API returns ERROR_WRONG_USER_KEY or ERROR_KEY_DOES_NOT_EXIST
ErrValidation Invalid key length at New; missing/invalid parameters; bad image input; proxy required but missing; unknown captcha type
ErrProxy Bad proxy (ERROR_BAD_PROXY) or proxy connection failed
ErrThreadLimit All threads busy and retries skipped or exhausted; local in-flight cap reached when auto-retry is disabled
ErrNoThreads Account expired or has no active plan (ERROR_ZERO_BALANCE with spare thread capacity)
ErrUnsolvable Captcha could not be solved (ERROR_CAPTCHA_UNSOLVABLE)
ErrAPI Unexpected or malformed API response
ErrNetwork HTTP transport failure contacting the API
ErrTimeout Poll deadline exceeded (WithMaxPollSeconds) before a result was ready

Context cancellation and deadlines return the standard library error from context (context.Canceled, context.DeadlineExceeded) — not wrapped as *captchaai.Error.


Proxies

Proxies are supported at client level and per call. Format: "user:pass@host:port". proxytype is required whenever a proxy is set (HTTP, HTTPS, SOCKS4, or SOCKS5).

// Client-level (every solve)
client, err := captchaai.New(ctx, "YOUR_32_CHAR_API_KEY",
	captchaai.WithProxy("user:pass@host:port", "HTTP"),
)

// Per-call override (does not mutate client config)
result, err := client.RecaptchaV2(ctx,
	"YOUR_SITEKEY",
	"https://example.com",
	captchaai.WithCallProxy("user:pass@other-host:port", "SOCKS5"),
)

Proxy is mandatory (enforced at submit time) for:

  • CloudflareChallenge
  • Captchafox

License

This project is licensed under the MIT License.

Copyright (c) 2026 Dev@Captchaai

About

Official Go SDK for the CaptchaAI captcha-solving service.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages