Captcha solving service blog | SolveCaptcha How to bypass reCAPTCHA v2 Invisible

How to bypass reCAPTCHA v2 Invisible with solver

reCAPTCHA v2 Invisible runs in the background and is usually triggered when a visitor clicks a button, submits a form, or performs another protected action.

Unlike the standard reCAPTCHA v2 checkbox, the invisible version does not initially display an “I’m not a robot” widget. However, it can still display an image challenge when Google decides that additional verification is required.

For authorized testing, browser automation, and data collection, the challenge can be processed through the SolveCaptcha API.

The integration follows this workflow:

Detect reCAPTCHA
→ extract the site key
→ identify invisible mode
→ submit the task
→ retrieve the token
→ update the response field
→ call the callback
→ continue the protected action

This guide explains how to implement the complete workflow with Python and Selenium.

Use these methods only on websites you own or are authorized to test and automate.

What is reCAPTCHA v2 Invisible?

reCAPTCHA v2 Invisible is a hidden variation of reCAPTCHA v2.

It is commonly attached to:

  • login buttons;
  • registration forms;
  • payment forms;
  • contact forms;
  • account recovery actions;
  • protected API requests.

A typical implementation looks like this:

<button
  class="g-recaptcha"
  data-sitekey="6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u"
  data-callback="onSubmit"
  data-size="invisible">
  Continue
</button>

The captcha is triggered when the button is activated.

After verification, Google returns a token and the page usually:

  1. writes the token to g-recaptcha-response;
  2. calls the function specified in data-callback;
  3. submits the form or sends an API request.

Standard and Invisible reCAPTCHA v2 differences

Feature Standard reCAPTCHA v2 Invisible reCAPTCHA v2
Visible checkbox Yes No
Activation Checkbox interaction Page load or protected action
Image challenge Possible Possible
Response field g-recaptcha-response g-recaptcha-response
Callback Optional Common
SolveCaptcha parameter invisible=0 invisible=1

Invisible reCAPTCHA still uses the g-recaptcha-response field. It is incorrect to assume that the token is accepted only through a callback.

Some websites read the hidden field, while others wait for the callback or include the token in a custom XHR request.

SolveCaptcha API endpoints

Create a task through:

POST https://api.solvecaptcha.com/in.php

Retrieve the solution through:

GET https://api.solvecaptcha.com/res.php

For reCAPTCHA v2 Invisible, use:

method=userrecaptcha
invisible=1

Do not use unsupported request structures such as:

RecaptchaV2TaskProxyless
RecaptchaV2Task
createTask
getTaskResult
websiteKey
websiteURL
isInvisible

The documented SolveCaptcha parameters are:

method
googlekey
pageurl
invisible

Required parameters

Parameter Required Description
key Yes Your SolveCaptcha API key
method Yes Must be userrecaptcha
googlekey Yes Public reCAPTCHA site key
pageurl Yes Full URL containing the captcha
invisible Yes for this guide Set to 1
json Recommended Set to 1 for JSON responses

Optional parameters include:

Parameter Description
domain Domain used to load reCAPTCHA: google.com or recaptcha.net
userAgent User-Agent used in the active browser session
cookies Cookies associated with the current session
data-s Additional value used by some Google services
proxy Proxy in the supported SolveCaptcha format
proxytype HTTP, HTTPS, SOCKS4, or SOCKS5

For most normal websites, the core parameters are sufficient:

key
method=userrecaptcha
googlekey
pageurl
invisible=1
json=1

How to identify Invisible reCAPTCHA

Invisible mode can be identified through several indicators.

data-size attribute

Look for:

data-size="invisible"

Example:

<div
  class="g-recaptcha"
  data-sitekey="SITE_KEY"
  data-size="invisible"
  data-callback="onCaptchaSolved">
</div>

Iframe URL

The reCAPTCHA iframe may contain:

size=invisible

Example:

https://www.google.com/recaptcha/api2/anchor?k=SITE_KEY&size=invisible

Hidden badge

Invisible reCAPTCHA may display a small reCAPTCHA badge in a page corner rather than a checkbox.

Protected action

The captcha may be triggered only after:

  • clicking Submit;
  • clicking Login;
  • opening checkout;
  • requesting a verification code;
  • sending a form.

The captcha may not exist in the DOM until the protected action begins.

How to find the site key

The site key is a public identifier used by the website’s reCAPTCHA integration.

data-sitekey

The simplest location is:

data-sitekey="6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u"

Iframe k parameter

The site key may appear in:

k=SITE_KEY

Example:

https://www.google.com/recaptcha/api2/anchor?k=6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u

JavaScript configuration

The page may render the widget through JavaScript:

grecaptcha.render("captcha-container", {
  sitekey:
    "6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u",
  size: "invisible",
  callback: onSubmit
});

Extract the parameters in the browser console

The following script checks the most common HTML and iframe locations:

function findInvisibleRecaptcha() {
  const result = {
    siteKey: null,
    callbackName: null,
    invisible: false,
    domain: "google.com"
  };

  const element = document.querySelector(
    ".g-recaptcha[data-sitekey],"
    + "[data-sitekey]"
  );

  if (element) {
    result.siteKey =
      element.getAttribute(
        "data-sitekey"
      );

    result.callbackName =
      element.getAttribute(
        "data-callback"
      );

    result.invisible =
      element.getAttribute(
        "data-size"
      ) === "invisible";
  }

  const iframe = document.querySelector(
    'iframe[src*="/recaptcha/api2/anchor"]'
  );

  if (iframe) {
    try {
      const url = new URL(iframe.src);

      result.siteKey =
        result.siteKey
        || url.searchParams.get("k");

      result.invisible =
        result.invisible
        || url.searchParams.get("size")
          === "invisible";

      if (
        url.hostname.includes(
          "recaptcha.net"
        )
      ) {
        result.domain =
          "recaptcha.net";
      }
    } catch {
      // Ignore malformed iframe URLs.
    }
  }

  return result;
}

console.log(
  findInvisibleRecaptcha()
);

Example result:

{
  "siteKey": "6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u",
  "callbackName": "onSubmit",
  "invisible": true,
  "domain": "google.com"
}

How to find the callback

The callback is often declared through:

data-callback="onSubmit"

The expected call is then:

onSubmit(token);

It can also be defined during JavaScript rendering:

grecaptcha.render(
  "captcha-container",
  {
    sitekey: "SITE_KEY",
    size: "invisible",
    callback: onSubmit
  }
);

The callback may:

  • submit a form;
  • enable a button;
  • send an XHR request;
  • update application state;
  • redirect the browser.

Do not call the callback before updating the response field unless the website’s implementation explicitly requires a different order.

Step 1: Submit the task

Example cURL request:

curl --request POST \
  --url https://api.solvecaptcha.com/in.php \
  --data-urlencode "key=YOUR_API_KEY" \
  --data-urlencode "method=userrecaptcha" \
  --data-urlencode "googlekey=6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u" \
  --data-urlencode "pageurl=https://example.com/login" \
  --data-urlencode "invisible=1" \
  --data-urlencode "domain=google.com" \
  --data-urlencode "json=1"

A successful response contains the captcha task ID:

{
  "status": 1,
  "request": "2122988149"
}

Save the request value.

Step 2: Wait for processing

Wait approximately:

15–20 seconds

before requesting the first result.

Do not create another task while the first one is still being processed.

Step 3: Retrieve the token

Send:

key=YOUR_API_KEY
action=get
id=TASK_ID
json=1

Example:

curl "https://api.solvecaptcha.com/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

If the task is still processing:

{
  "status": 0,
  "request": "CAPCHA_NOT_READY"
}

Wait five seconds and repeat the request with the same task ID.

When the task is ready:

{
  "status": 1,
  "request": "03AFcWeA5dCJ..."
}

The request value is the reCAPTCHA token.

Step 4: Insert the token

Update every g-recaptcha-response field on the page:

function applyRecaptchaToken(token) {
  const fields = [
    ...document.querySelectorAll(
      '[name="g-recaptcha-response"]'
    )
  ];

  for (const field of fields) {
    field.value = token;
    field.innerHTML = token;

    field.dispatchEvent(
      new Event("input", {
        bubbles: true
      })
    );

    field.dispatchEvent(
      new Event("change", {
        bubbles: true
      })
    );
  }

  return fields.length;
}

Call it with:

applyRecaptchaToken(
  "TOKEN_FROM_SOLVECAPTCHA"
);

A page can contain more than one response field, so update all matching elements.

Step 5: Call the callback

For a globally available callback:

onSubmit(
  "TOKEN_FROM_SOLVECAPTCHA"
);

For a nested callback such as:

application.forms.onCaptchaSolved

use:

function resolveFunction(path) {
  const value = path
    .split(".")
    .reduce(
      (object, key) =>
        object?.[key],
      window
    );

  return typeof value === "function"
    ? value
    : null;
}

const callback = resolveFunction(
  "application.forms.onCaptchaSolved"
);

if (callback) {
  callback(
    "TOKEN_FROM_SOLVECAPTCHA"
  );
}

If the callback is not globally accessible, inspect the website’s normal workflow. The page may expect the token in a form field or network request instead.

Complete Python and Selenium example

Install dependencies:

python -m pip install requests selenium

Set the API key:

export SOLVECAPTCHA_API_KEY="YOUR_API_KEY"

Create solve_invisible_recaptcha.py:

#!/usr/bin/env python3

from __future__ import annotations

import os
import time
from dataclasses import dataclass
from typing import Any
from urllib.parse import parse_qs, urlparse

import requests
from selenium import webdriver
from selenium.common.exceptions import (
    NoSuchElementException,
)
from selenium.webdriver.common.by import By

CREATE_TASK_URL = (
    "https://api.solvecaptcha.com/in.php"
)

GET_RESULT_URL = (
    "https://api.solvecaptcha.com/res.php"
)

INITIAL_WAIT_SECONDS = 20
POLL_INTERVAL_SECONDS = 5
MAX_WAIT_SECONDS = 180

class SolveCaptchaError(RuntimeError):
    """Raised when SolveCaptcha returns an error."""

@dataclass(frozen=True)
class RecaptchaParameters:
    site_key: str
    callback_name: str | None
    invisible: bool
    domain: str

@dataclass(frozen=True)
class RecaptchaSolution:
    task_id: str
    token: str

def parse_api_response(
    response: requests.Response,
) -> dict[str, Any]:
    response.raise_for_status()

    try:
        result = response.json()
    except ValueError as exc:
        raise SolveCaptchaError(
            "SolveCaptcha returned invalid JSON: "
            f"{response.text[:500]}"
        ) from exc

    if not isinstance(result, dict):
        raise SolveCaptchaError(
            f"Unexpected API response: {result!r}"
        )

    return result

def extract_recaptcha_parameters(
    driver: webdriver.Chrome,
) -> RecaptchaParameters:
    elements = driver.find_elements(
        By.CSS_SELECTOR,
        ".g-recaptcha[data-sitekey],"
        "[data-sitekey]",
    )

    for element in elements:
        site_key = (
            element.get_attribute(
                "data-sitekey"
            )
            or ""
        ).strip()

        if not site_key:
            continue

        callback_name = (
            element.get_attribute(
                "data-callback"
            )
            or ""
        ).strip()

        invisible = (
            element.get_attribute(
                "data-size"
            )
            == "invisible"
        )

        return RecaptchaParameters(
            site_key=site_key,
            callback_name=(
                callback_name or None
            ),
            invisible=invisible,
            domain="google.com",
        )

    frames = driver.find_elements(
        By.CSS_SELECTOR,
        'iframe[src*="/recaptcha/api2/anchor"]',
    )

    for frame in frames:
        src = (
            frame.get_attribute("src")
            or ""
        )

        if not src:
            continue

        parsed = urlparse(src)
        query = parse_qs(parsed.query)

        site_key = (
            query.get("k", [""])[0]
        ).strip()

        if not site_key:
            continue

        invisible = (
            query.get(
                "size",
                [""],
            )[0]
            == "invisible"
        )

        domain = (
            "recaptcha.net"
            if "recaptcha.net"
            in parsed.hostname or ""
            else "google.com"
        )

        return RecaptchaParameters(
            site_key=site_key,
            callback_name=None,
            invisible=invisible,
            domain=domain,
        )

    raise RuntimeError(
        "reCAPTCHA site key was not found"
    )

def format_cookies(
    driver: webdriver.Chrome,
) -> str:
    cookies = driver.get_cookies()

    return "".join(
        f"{cookie['name']}:"
        f"{cookie['value']};"
        for cookie in cookies
        if cookie.get("name")
    )

class SolveCaptchaClient:
    def __init__(
        self,
        api_key: str,
        timeout: float = 30,
    ) -> None:
        if not api_key:
            raise ValueError(
                "SolveCaptcha API key is required"
            )

        self.api_key = api_key
        self.timeout = timeout
        self.session = requests.Session()

    def create_task(
        self,
        *,
        site_key: str,
        page_url: str,
        domain: str = "google.com",
        user_agent: str | None = None,
        cookies: str | None = None,
        data_s: str | None = None,
    ) -> str:
        payload: dict[str, Any] = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "invisible": 1,
            "domain": domain,
            "json": 1,
        }

        if user_agent:
            payload["userAgent"] = (
                user_agent
            )

        if cookies:
            payload["cookies"] = cookies

        if data_s:
            payload["data-s"] = data_s

        response = self.session.post(
            CREATE_TASK_URL,
            data=payload,
            timeout=self.timeout,
        )

        result = parse_api_response(
            response
        )

        if result.get("status") != 1:
            raise SolveCaptchaError(
                "Task creation failed: "
                f"{result.get('request')}"
            )

        task_id = str(
            result.get("request", "")
        ).strip()

        if not task_id:
            raise SolveCaptchaError(
                "SolveCaptcha did not return "
                "a task ID"
            )

        return task_id

    def get_result(
        self,
        task_id: str,
    ) -> str | None:
        response = self.session.get(
            GET_RESULT_URL,
            params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1,
            },
            timeout=self.timeout,
        )

        result = parse_api_response(
            response
        )

        if result.get("status") == 1:
            token = str(
                result.get("request", "")
            ).strip()

            if not token:
                raise SolveCaptchaError(
                    "SolveCaptcha returned "
                    "an empty token"
                )

            return token

        error_code = str(
            result.get(
                "request",
                "UNKNOWN_ERROR",
            )
        )

        if error_code == "CAPCHA_NOT_READY":
            return None

        raise SolveCaptchaError(
            f"Task {task_id} failed: "
            f"{error_code}"
        )

    def solve(
        self,
        *,
        site_key: str,
        page_url: str,
        domain: str = "google.com",
        user_agent: str | None = None,
        cookies: str | None = None,
        data_s: str | None = None,
    ) -> RecaptchaSolution:
        task_id = self.create_task(
            site_key=site_key,
            page_url=page_url,
            domain=domain,
            user_agent=user_agent,
            cookies=cookies,
            data_s=data_s,
        )

        time.sleep(
            INITIAL_WAIT_SECONDS
        )

        deadline = (
            time.monotonic()
            + MAX_WAIT_SECONDS
        )

        while time.monotonic() < deadline:
            token = self.get_result(
                task_id
            )

            if token is not None:
                return RecaptchaSolution(
                    task_id=task_id,
                    token=token,
                )

            time.sleep(
                POLL_INTERVAL_SECONDS
            )

        raise TimeoutError(
            f"Task {task_id} was not completed "
            f"within {MAX_WAIT_SECONDS} seconds"
        )

def apply_token(
    driver: webdriver.Chrome,
    token: str,
) -> int:
    return int(
        driver.execute_script(
            """
            const token = arguments[0];

            const fields = [
              ...document.querySelectorAll(
                '[name="g-recaptcha-response"]'
              )
            ];

            for (const field of fields) {
              field.value = token;
              field.innerHTML = token;

              field.dispatchEvent(
                new Event("input", {
                  bubbles: true
                })
              );

              field.dispatchEvent(
                new Event("change", {
                  bubbles: true
                })
              );
            }

            return fields.length;
            """,
            token,
        )
    )

def call_callback(
    driver: webdriver.Chrome,
    callback_name: str | None,
    token: str,
) -> bool:
    if not callback_name:
        return False

    return bool(
        driver.execute_script(
            """
            const path = arguments[0];
            const token = arguments[1];

            const callback = path
              .split(".")
              .reduce(
                (object, key) =>
                  object?.[key],
                window
              );

            if (
              typeof callback
              !== "function"
            ) {
              return false;
            }

            callback(token);
            return true;
            """,
            callback_name,
            token,
        )
    )

def report_result(
    *,
    api_key: str,
    task_id: str,
    accepted: bool,
) -> None:
    action = (
        "reportgood"
        if accepted
        else "reportbad"
    )

    response = requests.get(
        GET_RESULT_URL,
        params={
            "key": api_key,
            "action": action,
            "id": task_id,
            "json": 1,
        },
        timeout=30,
    )

    parse_api_response(response)

def main() -> None:
    api_key = os.environ.get(
        "SOLVECAPTCHA_API_KEY",
        "",
    ).strip()

    target_url = os.environ.get(
        "TARGET_URL",
        "https://example.com/login",
    )

    if not api_key:
        raise SystemExit(
            "Set SOLVECAPTCHA_API_KEY"
        )

    options = webdriver.ChromeOptions()

    driver = webdriver.Chrome(
        options=options
    )

    client = SolveCaptchaClient(
        api_key
    )

    try:
        driver.get(target_url)

        parameters = (
            extract_recaptcha_parameters(
                driver
            )
        )

        if not parameters.invisible:
            raise RuntimeError(
                "The detected widget is not "
                "configured as invisible"
            )

        user_agent = driver.execute_script(
            "return navigator.userAgent"
        )

        cookies = format_cookies(
            driver
        )

        solution = client.solve(
            site_key=parameters.site_key,
            page_url=driver.current_url,
            domain=parameters.domain,
            user_agent=user_agent,
            cookies=cookies or None,
        )

        fields_updated = apply_token(
            driver,
            solution.token,
        )

        callback_called = call_callback(
            driver,
            parameters.callback_name,
            solution.token,
        )

        print(
            "Task ID:",
            solution.task_id,
        )

        print(
            "Response fields updated:",
            fields_updated,
        )

        print(
            "Callback called:",
            callback_called,
        )

        /*
         * Continue the authorized page
         * workflow here.
         *
         * For example, click the original
         * submit button or call
         * form.requestSubmit().
         */

        time.sleep(10)

    finally:
        driver.quit()

if __name__ == "__main__":
    main()

The comment inside the Python example must use Python syntax. Replace this section:

/*
 * Continue the authorized page
 * workflow here.
 */

with:

# Continue the authorized page workflow here.
# For example, click the original submit button
# or call the page's expected submission logic.

The corrected ending of main() is:

        print(
            "Callback called:",
            callback_called,
        )

        # Continue the authorized page workflow.
        # Do not submit an irreversible action
        # without verifying the target form.

        time.sleep(10)

    finally:
        driver.quit()

Shorter Python API example

When you already know the site key and callback, the API integration can be reduced to:

import os
import time

import requests

API_KEY = os.environ[
    "SOLVECAPTCHA_API_KEY"
]

SITE_KEY = (
    "6LfD3PIbAAAAAJs_"
    "eEHvoOl75_83eXSqpPSRFJ_u"
)

PAGE_URL = (
    "https://example.com/login"
)

create_response = requests.post(
    "https://api.solvecaptcha.com/in.php",
    data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": SITE_KEY,
        "pageurl": PAGE_URL,
        "invisible": 1,
        "json": 1,
    },
    timeout=30,
).json()

if create_response.get("status") != 1:
    raise RuntimeError(
        create_response.get(
            "request",
            "Task creation failed",
        )
    )

task_id = str(
    create_response["request"]
)

time.sleep(20)

while True:
    result = requests.get(
        "https://api.solvecaptcha.com/res.php",
        params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1,
        },
        timeout=30,
    ).json()

    if result.get("status") == 1:
        token = result["request"]
        break

    if (
        result.get("request")
        != "CAPCHA_NOT_READY"
    ):
        raise RuntimeError(
            result.get("request")
        )

    time.sleep(5)

print(token)

Submitting the form after applying the token

Do not immediately use:

form.submit();

The native submit() method skips the normal submit event and may bypass client-side validation.

Prefer:

form.requestSubmit();

Example:

driver.execute_script(
    """
    const form =
      document.querySelector(
        "#login-form"
      );

    if (!form) {
      throw new Error(
        "Form was not found"
      );
    }

    form.requestSubmit();
    """
)

Some pages do not use a traditional form. They may send the token through Fetch or XMLHttpRequest.

Inspect the normal request in DevTools to identify:

  • the endpoint;
  • HTTP method;
  • request body;
  • response-token field;
  • callback behavior;
  • additional required form values.

Working with dynamically loaded captcha

Invisible reCAPTCHA may not be present when the page first loads.

Wait for one of the following:

[data-sitekey]
iframe[src*="/recaptcha/api2/anchor"]

Selenium example:

from selenium.webdriver.support.ui import (
    WebDriverWait,
)
from selenium.webdriver.support import (
    expected_conditions as EC,
)
from selenium.webdriver.common.by import By

WebDriverWait(
    driver,
    20,
).until(
    EC.presence_of_element_located(
        (
            By.CSS_SELECTOR,
            "[data-sitekey],"
            'iframe[src*="/recaptcha/api2/anchor"]',
        )
    )
)

When the widget appears only after clicking a button, perform the action first and then extract the parameters.

Working with data-s

Some Google services add a data-s value to the reCAPTCHA request.

It may appear in:

data-s="VALUE"

or inside the reCAPTCHA iframe URL.

When present, send it as:

data-s=VALUE

Example:

payload["data-s"] = data_s_value

Do not invent or reuse an old data-s value. Extract it from the current page or challenge.

Working with recaptcha.net

Some websites load reCAPTCHA from:

www.recaptcha.net

instead of:

www.google.com

In that case, send:

domain=recaptcha.net

For a standard Google integration:

domain=google.com

The parameter should match the domain used by the active page.

Working with cookies and User-Agent

The optional userAgent and cookies parameters can be passed when the captcha depends on the active browser session.

Get the browser User-Agent:

user_agent = driver.execute_script(
    "return navigator.userAgent"
)

Format cookies for SolveCaptcha:

cookies = "".join(
    f"{cookie['name']}:"
    f"{cookie['value']};"
    for cookie in driver.get_cookies()
)

Example task parameters:

payload = {
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": SITE_KEY,
    "pageurl": PAGE_URL,
    "invisible": 1,
    "userAgent": user_agent,
    "cookies": cookies,
    "json": 1,
}

Do not include authentication cookies unless they are genuinely required for an authorized workflow.

Reporting accepted and rejected solutions

After using the token, report whether it was accepted.

Accepted token:

https://api.solvecaptcha.com/res.php?key=YOUR_API_KEY&action=reportgood&id=TASK_ID

Rejected token:

https://api.solvecaptcha.com/res.php?key=YOUR_API_KEY&action=reportbad&id=TASK_ID

Use reportgood when:

  • the website accepts the captcha;
  • the form or protected request succeeds;
  • no captcha validation error is returned.

Use reportbad only when the returned captcha solution itself is rejected.

Do not send reportbad when the failure was caused by:

  • an incorrect site key;
  • an incorrect page URL;
  • a missing callback;
  • an expired page;
  • a refreshed widget;
  • another invalid form field;
  • an incorrect selector;
  • application validation unrelated to captcha.

Common API errors

ERROR_WRONG_USER_KEY

The API key is invalid.

Copy the key again from the SolveCaptcha account settings.

ERROR_ZERO_BALANCE

The account balance is insufficient.

Add funds before submitting another task.

ERROR_CAPTCHA_UNSOLVABLE

The captcha could not be solved reliably.

Check:

  • googlekey;
  • pageurl;
  • invisible=1;
  • the loading domain;
  • whether data-s is required;
  • whether the page generated a new challenge.

CAPCHA_NOT_READY

The task is still processing.

Wait five seconds and request the result again using the same task ID.

Token rejected

Possible causes include:

  • incorrect site key;
  • incorrect page URL;
  • token applied after the widget reset;
  • missing callback;
  • token inserted into the wrong field;
  • changed browser session;
  • missing data-s;
  • application-specific verification requirements.

Common implementation mistakes

Using isInvisible instead of invisible

Incorrect:

isInvisible=true

Correct:

invisible=1

Using websiteKey instead of googlekey

Incorrect:

websiteKey=SITE_KEY

Correct:

googlekey=SITE_KEY

Using websiteURL instead of pageurl

Incorrect:

websiteURL=PAGE_URL

Correct:

pageurl=PAGE_URL

Using createTask and getTaskResult

Use:

https://api.solvecaptcha.com/in.php

and:

https://api.solvecaptcha.com/res.php

Treating Invisible reCAPTCHA as reCAPTCHA v3

Invisible reCAPTCHA v2 and reCAPTCHA v3 are different systems.

For Invisible reCAPTCHA v2:

method=userrecaptcha
invisible=1

For reCAPTCHA v3:

method=userrecaptcha
version=v3
action=ACTION

Updating only the response field

Some pages also require the configured callback.

Calling only the callback

Some pages read the hidden response field during form submission.

Update the field first and then call the callback.

Reloading the page while waiting

Reloading can generate a new challenge and invalidate the pending result.

Reusing a token

Treat each token as challenge-specific and single-use.

Automatically submitting the wrong form

A page may contain multiple forms. Use a specific selector rather than:

document.querySelector("form")

Testing applications you control

For routine automated tests, avoid solving a live production captcha on every test run.

Use:

  • Google test keys;
  • mocked verification responses;
  • staging-only captcha configuration;
  • dedicated test accounts;
  • approved allowlists;
  • a limited number of production smoke tests.

This makes tests faster, more predictable, and less dependent on external services.

Use the real SolveCaptcha integration only when the complete captcha flow must be tested.

Frequently asked questions

Which method is used for reCAPTCHA v2 Invisible?

Use:

method=userrecaptcha
invisible=1

Which parameters are required?

Use:

key
method
googlekey
pageurl
invisible

Where can I find the site key?

Look for:

data-sitekey

or the k parameter in the reCAPTCHA iframe URL.

How do I confirm invisible mode?

Look for:

data-size="invisible"

or:

size=invisible

in the iframe URL.

Is the callback required?

Not always, but it is common. Inspect data-callback and the page’s normal submission process.

Is g-recaptcha-response still used?

Yes. Invisible reCAPTCHA commonly writes the token to g-recaptcha-response.

How long should I wait?

Wait approximately 15–20 seconds before the first result request. Poll every five seconds while the API returns CAPCHA_NOT_READY.

Where is the token returned?

With json=1, the completed token is returned in:

request

Should I pass cookies?

Only when they are required by the authorized browser workflow.

Should I pass the User-Agent?

It is optional. When used, it should match the active browser session.

Can the token be reused?

No. Treat it as specific to the current challenge and workflow.

Conclusion

Bypassing reCAPTCHA v2 Invisible with SolveCaptcha requires more than inserting a token into one hidden field.

The correct workflow is:

  1. Confirm that the page uses reCAPTCHA v2 Invisible.
  2. Extract the site key from data-sitekey or the iframe URL.
  3. Find the callback name when one is configured.
  4. Submit method=userrecaptcha.
  5. Pass the site key as googlekey.
  6. Pass the complete page URL as pageurl.
  7. Set invisible=1.
  8. Include domain, userAgent, cookies, or data-s only when required.
  9. Wait approximately 15–20 seconds.
  10. Poll res.php every five seconds.
  11. Update all g-recaptcha-response fields.
  12. Dispatch input and change events.
  13. Call the configured callback.
  14. Continue the page’s expected submission workflow.
  15. Report whether the solution was accepted.

Use the SolveCaptcha reCAPTCHA v2 Invisible documentation to verify the current parameters before deploying the integration.