Go's standard library makes HTTP requests a first-class citizen. This guide
shows you how to submit a CAPTCHA task to the CaptchaAI API, poll for the
result, and inject the token — all using only net/http and encoding/json,
with no third-party dependencies required.
Prerequisites: Go 1.21+, a CaptchaAI API key, and the target page's
sitekey.
1. Project layout
captcha-go/
├── main.go
└── captcha/
└── client.go
2. The solver client (captcha/client.go)
package captcha
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)
const (
baseURL = "https://api.captchaai.io/in.php"
resultURL = "https://api.captchaai.io/res.php"
pollEvery = 3 * time.Second
maxWait = 120 * time.Second
)
type Client struct {
APIKey string
HTTPClient *http.Client
}
func New(apiKey string) *Client {
return &Client{
APIKey: apiKey,
HTTPClient: &http.Client{Timeout: 30 * time.Second},
}
}
// SolveRecaptchaV2 submits a reCAPTCHA v2 task and returns the g-recaptcha-response token.
func (c *Client) SolveRecaptchaV2(pageURL, siteKey string) (string, error) {
payload := map[string]any{
"clientKey": c.APIKey,
"task": map[string]any{
"type": "NoCaptchaTaskProxyless",
"websiteURL": pageURL,
"websiteKey": siteKey,
},
}
taskID, err := c.createTask(payload)
if err != nil {
return "", err
}
return c.pollResult(taskID)
}
// SolveHCaptcha submits an hCaptcha task and returns the h-captcha-response token.
func (c *Client) SolveHCaptcha(pageURL, siteKey string) (string, error) {
payload := map[string]any{
"clientKey": c.APIKey,
"task": map[string]any{
"type": "HCaptchaTaskProxyless",
"websiteURL": pageURL,
"websiteKey": siteKey,
},
}
taskID, err := c.createTask(payload)
if err != nil {
return "", err
}
return c.pollResult(taskID)
}
// SolveTurnstile submits a Cloudflare Turnstile task and returns the cf-turnstile-response token.
func (c *Client) SolveTurnstile(pageURL, siteKey string) (string, error) {
payload := map[string]any{
"clientKey": c.APIKey,
"task": map[string]any{
"type": "TurnstileTaskProxyless",
"websiteURL": pageURL,
"websiteKey": siteKey,
},
}
taskID, err := c.createTask(payload)
if err != nil {
return "", err
}
return c.pollResult(taskID)
}
func (c *Client) createTask(payload map[string]any) (string, error) {
body, _ := json.Marshal(payload)
resp, err := c.HTTPClient.Post(baseURL, "application/json", bytes.NewReader(body))
if err != nil {
return "", fmt.Errorf("createTask request: %w", err)
}
defer resp.Body.Close()
var result struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
TaskID string `json:"taskId"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", fmt.Errorf("createTask decode: %w", err)
}
if result.ErrorID != 0 {
return "", fmt.Errorf("createTask API error %d: %s", result.ErrorID, result.ErrorCode)
}
return result.TaskID, nil
}
func (c *Client) pollResult(taskID string) (string, error) {
deadline := time.Now().Add(maxWait)
payload := map[string]any{
"clientKey": c.APIKey,
"taskId": taskID,
}
body, _ := json.Marshal(payload)
for time.Now().Before(deadline) {
time.Sleep(pollEvery)
resp, err := c.HTTPClient.Post(resultURL, "application/json", bytes.NewReader(body))
if err != nil {
continue
}
var result struct {
ErrorID int `json:"errorId"`
Status string `json:"status"`
Solution struct {
GRecaptchaResponse string `json:"gRecaptchaResponse"`
Token string `json:"token"`
} `json:"solution"`
}
_ = json.NewDecoder(resp.Body).Decode(&result)
resp.Body.Close()
if result.ErrorID != 0 {
return "", fmt.Errorf("pollResult error %d", result.ErrorID)
}
if result.Status == "ready" {
token := result.Solution.GRecaptchaResponse
if token == "" {
token = result.Solution.Token
}
return token, nil
}
}
return "", fmt.Errorf("solver timeout after %s", maxWait)
}
3. Using the client (main.go)
package main
import (
"fmt"
"log"
"os"
"captcha-go/captcha"
)
func main() {
apiKey := os.Getenv("CAPTCHAAI_API_KEY")
if apiKey == "" {
log.Fatal("CAPTCHAAI_API_KEY not set")
}
client := captcha.New(apiKey)
// --- reCAPTCHA v2 ---
token, err := client.SolveRecaptchaV2(
"https://example.com/login",
"6Le-wvkSAAAAAPBMRTvw0Q4Muexq1bi0DJwx_mJ-",
)
if err != nil {
log.Fatalf("reCAPTCHA v2: %v", err)
}
fmt.Println("reCAPTCHA token:", token[:20]+"…")
// --- hCaptcha ---
hToken, err := client.SolveHCaptcha(
"https://example.com/signup",
"a5f74b19-9e45-40e0-b45d-47ff91b7a6c2",
)
if err != nil {
log.Fatalf("hCaptcha: %v", err)
}
fmt.Println("hCaptcha token:", hToken[:20]+"…")
// --- Cloudflare Turnstile ---
cfToken, err := client.SolveTurnstile(
"https://example.com/cf-protected",
"0x4AAAAAAAA_some_turnstile_key",
)
if err != nil {
log.Fatalf("Turnstile: %v", err)
}
fmt.Println("Turnstile token:", cfToken[:20]+"…")
}
4. Injecting the token
With net/http (form POST)
import (
"net/http"
"net/url"
"strings"
)
func submitForm(pageURL, token string) error {
form := url.Values{
"g-recaptcha-response": {token},
"username": {"alice"},
"password": {"s3cr3t"},
}
resp, err := http.PostForm(pageURL, form)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
With chromedp (headless browser)
import (
"context"
"github.com/chromedp/chromedp"
)
func injectAndSubmit(token string) error {
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
return chromedp.Run(ctx,
chromedp.Navigate("https://example.com/login"),
chromedp.WaitVisible(`#g-recaptcha-response`, chromedp.ByID),
chromedp.Evaluate(
fmt.Sprintf(`document.getElementById('g-recaptcha-response').value = %q`, token),
nil,
),
chromedp.Click(`#submit-btn`, chromedp.ByID),
chromedp.WaitVisible(`#dashboard`, chromedp.ByID),
)
}
5. Error handling patterns
| Error | Meaning | Fix |
|---|---|---|
errorId: 1 |
API key invalid | Check CAPTCHAAI_API_KEY |
errorId: 10 |
Zero balance | Top up your account |
errorId: 12 |
Wrong CAPTCHA type | Verify type field in task payload |
| timeout after 120s | Solver overloaded | Retry or switch solver |
Next steps
- CAPTCHA Solver API Integration Guide — full API reference
- Best CAPTCHA Solvers — provider comparison
- CAPTCHA Solver for Puppeteer — Node.js headless integration
- CAPTCHA Solver for Python — Python equivalent
Production Readiness Notes
Use CAPTCHA Solving in Go — Quick Start Guide as a decision and implementation aid, not just as a one-time reference. The practical test for captcha solver go golang is whether the same approach behaves reliably when traffic is messy: rotating sessions, expired tokens, changing widget parameters, intermittent solver delays, and target pages that refresh without warning. For Automation developer, the safest rollout is to start with a narrow fixture, record every submitted task, and compare the solver response with the browser state that finally submits the form. That makes failures explainable instead of mysterious, especially when a target alternates between visible challenges, invisible checks, and server-side verification.
Evaluation Criteria
A developer guide should become a reusable integration module with typed configuration, bounded polling, structured errors, and a single place for API credentials. For developer integration work, the most useful scorecard combines technical acceptance with operational cost. A low nominal price is not enough if retries double the real cost per accepted token, and a fast median solve time is not enough if p95 latency stalls the queue. Track these criteria before you standardize the workflow:
- The challenge subtype, sitekey, action, rqdata, blob, captchaId, or page URL used for each task.
- Median and p95 solve time, separated by provider and target domain.
- Accepted-token rate on the target page, not just successful API responses.
- Retry count, timeout count, zero-balance incidents, and invalid-parameter errors.
- The exact browser, proxy region, and user-agent that submitted the solved token.
Rollout Checklist
Before this guidance moves into a production job, build a small acceptance suite around the pages that matter most. Run it with a fixed browser profile, then repeat with the proxy and concurrency settings you expect in production. Keep the first release conservative: bounded polling, clear timeout handling, and a fallback path when the solver cannot return a usable answer. For developer integration, treat the code as a production pattern: timeouts, retries, logging, secret storage, and test fixtures matter as much as the solve request itself. That checklist keeps the article useful after the first copy-paste, because the integration is judged by end-to-end completion rather than by whether a code sample returned a string.
Monitoring Signals
Healthy CAPTCHA automation is observable. Log the task id, provider, challenge type, target host, queue time, solve time, final submit status, and normalized error code for every attempt. Review those logs in daily batches at first, then move to alerts once the baseline is stable. Sudden drops usually come from target-side changes: a new sitekey, a changed action name, a stricter hostname check, an added managed challenge, or a proxy pool that no longer matches the expected geography. When you can see those shifts quickly, provider switching becomes a controlled decision instead of a late-night rewrite.
Maintenance Cadence
Revisit the setup whenever the target UI changes, when the solver provider changes task names or pricing, or when benchmark data shows a sustained latency or solve-rate shift. Keep one known-good fixture for each CAPTCHA subtype and rerun it after dependency upgrades, browser updates, and proxy changes. If the article is used for vendor selection, repeat the same fixture across at least two providers before renewing a balance or migrating the whole pipeline. That habit keeps captcha solver go golang work aligned with the real target behavior rather than with stale assumptions.