Captcha solving service blog | SolveCaptcha How to bypass captcha in Burp Suite with solv

How to bypass captcha in Burp Suite with solver

Burp Suite is widely used for web application security testing, request inspection, and API analysis.

Captchas can interrupt this workflow. A protected login, registration form, or API request may require a valid captcha token before the server processes the request.

With the SolveCaptcha API, you can submit the captcha parameters, retrieve a solution token, and insert it into a request sent through Burp Repeater.

The basic workflow is:

Intercept the protected page
→ identify the captcha type
→ extract the required parameters
→ create a SolveCaptcha task
→ wait for the solution
→ retrieve the token
→ insert it into the Burp request
→ send the request

This guide focuses on reCAPTCHA v2. It also explains the changes required for Invisible reCAPTCHA v2, reCAPTCHA v3, and reCAPTCHA Enterprise.

Use this workflow only on systems you own or are explicitly authorized to test.

What Burp Suite does in this workflow

Burp Suite does not solve the captcha itself.

Burp is used to:

  • inspect the protected page;
  • identify the captcha parameters;
  • capture the final HTTP request;
  • locate the expected captcha field;
  • modify the request;
  • preserve cookies and headers;
  • send the completed request to the server.

SolveCaptcha performs the captcha-solving step and returns the token.

The final request is still sent through Burp Suite.

Recommended integration options

There are two practical ways to connect Burp Suite with SolveCaptcha.

External Python helper

A standalone Python script creates the task and prints the completed token.

You then paste the token into Burp Repeater.

This is the simplest option and is usually easier to debug.

Burp extension

A Burp extension can detect a placeholder in a Repeater request, request a new token, and replace the placeholder before the request is sent.

This reduces manual work but requires additional configuration.

Start with the external helper. Add the extension only after the basic workflow works correctly.

Requirements

You need:

  • Burp Suite Community or Professional;
  • Python 3 for the external helper;
  • Jython when using the optional legacy Burp extension;
  • a SolveCaptcha account;
  • a SolveCaptcha API key;
  • permission to test the target application.

Install Requests for Python:

python -m pip install requests

Store the API key in an environment variable.

Linux or macOS:

export SOLVECAPTCHA_API_KEY="YOUR_API_KEY"

Windows PowerShell:

$env:SOLVECAPTCHA_API_KEY="YOUR_API_KEY"

Do not place the API key in browser JavaScript, public repositories, screenshots, or shared Burp project files.

Step 1: Capture the protected page

Configure your browser to use Burp Proxy.

Open the page containing the captcha and capture its HTML response.

You can inspect the response in:

  • Proxy HTTP history;
  • Repeater;
  • Logger;
  • the browser DevTools.

For reCAPTCHA v2, search the response for:

data-sitekey

Example:

<div
  class="g-recaptcha"
  data-sitekey="6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u">
</div>

The site key is:

6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u

The site key is public. It is not the private reCAPTCHA secret used by the website backend.

Alternative site key locations

The site key may also appear in a reCAPTCHA iframe URL.

Look for:

www.google.com/recaptcha/api2/anchor

Example:

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

The site key is the value of:

k

For reCAPTCHA v3, it may appear in:

<script
  src="https://www.google.com/recaptcha/api.js?render=SITE_KEY">
</script>

It may also appear in:

grecaptcha.execute(
  "SITE_KEY",
  {
    action: "login"
  }
);

Step 2: Record the correct page URL

The SolveCaptcha pageurl parameter must contain the full URL where the captcha is loaded.

Example:

https://example.com/account/login

Do not automatically use only the website origin:

https://example.com

The correct path may matter.

Preserve important query parameters when they are part of the page:

https://example.com/account/login?return=/checkout

The page URL should describe the captcha page, not necessarily the API endpoint that receives the completed form.

Step 3: Find where the token is submitted

Complete the captcha manually once and inspect the resulting request in Burp.

Search the request body for:

g-recaptcha-response

A URL-encoded form may contain:

[email protected]&password=test&g-recaptcha-response=TOKEN

A JSON request may contain:

{
  "username": "[email protected]",
  "password": "test",
  "g-recaptcha-response": "TOKEN"
}

Some applications use a custom field:

captchaToken
recaptchaToken
verificationToken
captcha_response

Do not assume that every application sends the token under the standard field name.

Compare the request before and after a successful manual captcha completion.

SolveCaptcha API endpoints

Create a task through:

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

Retrieve the result through:

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

For standard reCAPTCHA v2, use:

method=userrecaptcha
googlekey=SITE_KEY
pageurl=PAGE_URL

Do not use unsupported request structures such as:

createTask
getTaskResult
clientKey
websiteKey
websiteURL
NoCaptchaTaskProxyless
RecaptchaV2TaskProxyless

Step 4: Create a reCAPTCHA v2 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=6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u" \
  --data-urlencode "pageurl=https://example.com/account/login" \
  --data-urlencode "json=1"

A successful response contains the task ID:

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

Save the value returned in:

request

If task creation fails, the API returns an error code:

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

Do not start polling unless a valid task ID was returned.

Step 5: Wait for processing

Wait approximately 15–20 seconds before requesting the first result.

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

Duplicate tasks increase costs and make it harder to determine which token belongs to the current request.

Step 6: Retrieve the token

Request the result from:

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

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 complete:

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

The value in request is the reCAPTCHA token.

Complete Python helper

The following script creates a reCAPTCHA v2 task, waits for the result, and prints the completed token.

Create solve_recaptcha.py:

#!/usr/bin/env python3

from __future__ import annotations

import argparse
import os
import sys
import time
from dataclasses import dataclass
from typing import Any

import requests

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 CaptchaSolution:
    task_id: str
    token: str

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

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

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

    return data

def create_task(
    *,
    api_key: str,
    site_key: str,
    page_url: str,
    invisible: bool = False,
    version: str | None = None,
    action: str | None = None,
    min_score: float | None = None,
    enterprise: bool = False,
) -> str:
    payload: dict[str, Any] = {
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1,
    }

    if invisible:
        payload["invisible"] = 1

    if version:
        payload["version"] = version

    if action:
        payload["action"] = action

    if min_score is not None:
        payload["min_score"] = min_score

    if enterprise:
        payload["enterprise"] = 1

    response = requests.post(
        CREATE_TASK_URL,
        data=payload,
        timeout=30,
    )

    data = parse_response(
        response
    )

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

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

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

    return task_id

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

    data = parse_response(
        response
    )

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

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

        return token

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

    if error_code == "CAPCHA_NOT_READY":
        return None

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

def solve(
    *,
    api_key: str,
    site_key: str,
    page_url: str,
    invisible: bool = False,
    version: str | None = None,
    action: str | None = None,
    min_score: float | None = None,
    enterprise: bool = False,
) -> CaptchaSolution:
    task_id = create_task(
        api_key=api_key,
        site_key=site_key,
        page_url=page_url,
        invisible=invisible,
        version=version,
        action=action,
        min_score=min_score,
        enterprise=enterprise,
    )

    time.sleep(
        INITIAL_WAIT_SECONDS
    )

    deadline = (
        time.monotonic()
        + MAX_WAIT_SECONDS
    )

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

        if token is not None:
            return CaptchaSolution(
                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 build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description=(
            "Request a reCAPTCHA token "
            "from SolveCaptcha."
        )
    )

    parser.add_argument(
        "--site-key",
        required=True,
    )

    parser.add_argument(
        "--page-url",
        required=True,
    )

    parser.add_argument(
        "--invisible",
        action="store_true",
    )

    parser.add_argument(
        "--version",
        choices=["v3"],
    )

    parser.add_argument(
        "--action",
    )

    parser.add_argument(
        "--min-score",
        type=float,
    )

    parser.add_argument(
        "--enterprise",
        action="store_true",
    )

    return parser

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

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

    parser = build_parser()
    arguments = parser.parse_args()

    try:
        solution = solve(
            api_key=api_key,
            site_key=arguments.site_key,
            page_url=arguments.page_url,
            invisible=arguments.invisible,
            version=arguments.version,
            action=arguments.action,
            min_score=arguments.min_score,
            enterprise=arguments.enterprise,
        )
    except (
        requests.RequestException,
        SolveCaptchaError,
        TimeoutError,
    ) as error:
        print(
            f"Error: {error}",
            file=sys.stderr,
        )

        raise SystemExit(1) from error

    print(solution.token)

if __name__ == "__main__":
    main()

Run it for standard reCAPTCHA v2:

python solve_recaptcha.py \
  --site-key "6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u" \
  --page-url "https://example.com/account/login"

The script prints only the token:

03AFcWeA5dCJ...

This makes it easy to copy the value into Burp Repeater.

Step 7: Insert the token in Burp Repeater

Send the protected request to Repeater.

Locate the captcha field and replace its current value with the SolveCaptcha token.

URL-encoded form

Before:

POST /account/login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

username=test%40example.com&password=test&g-recaptcha-response=

After:

POST /account/login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

username=test%40example.com&password=test&g-recaptcha-response=03AFcWeA5dCJ...

Burp recalculates Content-Length when the request is sent.

JSON body

Before:

{
  "username": "[email protected]",
  "password": "test",
  "captchaToken": ""
}

After:

{
  "username": "[email protected]",
  "password": "test",
  "captchaToken": "03AFcWeA5dCJ..."
}

Multipart form

A multipart request may contain:

Content-Disposition: form-data; name="g-recaptcha-response"

03AFcWeA5dCJ...

Preserve the existing multipart boundary and field formatting.

Do not inject the token into HTML in Repeater

When using Burp Repeater, you normally do not need to modify the HTML response or execute the page callback.

Burp sends the final HTTP request directly to the server.

The important step is to place the token where the backend expects it:

  • form field;
  • JSON property;
  • multipart field;
  • custom header;
  • another application-specific parameter.

Browser-side callbacks matter when automating the actual page. They are usually unnecessary when reproducing the final request directly in Repeater.

Preserve the original session

A captcha token may depend on the session that generated the challenge.

Keep the relevant request context unchanged:

  • cookies;
  • CSRF token;
  • User-Agent;
  • page URL;
  • account state;
  • proxy or source IP;
  • request order.

Do not generate the token for one session and submit it from an unrelated Burp project or browser profile.

If the application issues a new CSRF token or captcha challenge, capture fresh parameters and request a new solution.

Invisible reCAPTCHA v2

Invisible reCAPTCHA v2 uses the same base method.

Add:

invisible=1

Run the helper with:

python solve_recaptcha.py \
  --site-key "SITE_KEY" \
  --page-url "https://example.com/account/login" \
  --invisible

The returned token is still commonly submitted through:

g-recaptcha-response

When editing the final request in Burp, the browser callback normally does not need to be executed.

reCAPTCHA v3

For reCAPTCHA v3, add:

version=v3
action=ACTION
min_score=SCORE

Example:

python solve_recaptcha.py \
  --site-key "SITE_KEY" \
  --page-url "https://example.com/account/login" \
  --version v3 \
  --action login \
  --min-score 0.3

The action must match the value used by the website.

Look for:

grecaptcha.execute(
  "SITE_KEY",
  {
    action: "login"
  }
);

Do not guess between similar values such as:

login
signin
authenticate
verify

The server may reject a valid token when its action does not match.

reCAPTCHA Enterprise

For reCAPTCHA Enterprise, add:

enterprise=1

Example:

python solve_recaptcha.py \
  --site-key "SITE_KEY" \
  --page-url "https://example.com/account/login" \
  --enterprise

For Enterprise v3:

python solve_recaptcha.py \
  --site-key "SITE_KEY" \
  --page-url "https://example.com/account/login" \
  --version v3 \
  --action login \
  --min-score 0.3 \
  --enterprise

Enterprise integrations can require additional values. Inspect the page and the successful browser request before reproducing the workflow in Burp.

Optional Burp Repeater extension

The following Jython extension watches requests sent from Repeater.

When the request body contains:

__SOLVECAPTCHA_TOKEN__

the extension:

  1. creates a reCAPTCHA v2 task;
  2. waits for the token;
  3. replaces the placeholder;
  4. sends the modified request.

The extension processes only Repeater requests and only the configured hostname.

This example is intended for low-volume authorized testing. It blocks the Repeater request while the captcha is being solved.

# -*- coding: utf-8 -*-

from burp import IBurpExtender
from burp import IHttpListener

import json
import os
import threading
import time
import urllib
import urllib2

API_KEY = os.environ.get(
    "SOLVECAPTCHA_API_KEY",
    "",
).strip()

ALLOWED_HOST = "example.com"

SITE_KEY = (
    "6Lc_aXkUAAAAAJs_"
    "eEHvoOl75_83eXSqpPSRFJ_u"
)

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

PLACEHOLDER = (
    "__SOLVECAPTCHA_TOKEN__"
)

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

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

class BurpExtender(
    IBurpExtender,
    IHttpListener,
):
    def registerExtenderCallbacks(
        self,
        callbacks,
    ):
        self.callbacks = callbacks
        self.helpers = (
            callbacks.getHelpers()
        )

        self.lock = threading.Lock()

        callbacks.setExtensionName(
            "SolveCaptcha Repeater Injector"
        )

        callbacks.registerHttpListener(
            self
        )

        callbacks.printOutput(
            "SolveCaptcha Repeater Injector loaded"
        )

    def processHttpMessage(
        self,
        toolFlag,
        messageIsRequest,
        messageInfo,
    ):
        if not messageIsRequest:
            return

        if (
            toolFlag
            != self.callbacks.TOOL_REPEATER
        ):
            return

        request = messageInfo.getRequest()

        request_info = (
            self.helpers.analyzeRequest(
                messageInfo
            )
        )

        url = request_info.getUrl()

        if url.getHost() != ALLOWED_HOST:
            return

        body_offset = (
            request_info.getBodyOffset()
        )

        request_string = (
            self.helpers.bytesToString(
                request
            )
        )

        body = request_string[
            body_offset:
        ]

        if PLACEHOLDER not in body:
            return

        if not API_KEY:
            self.callbacks.printError(
                "SOLVECAPTCHA_API_KEY is not set"
            )

            return

        with self.lock:
            try:
                token = (
                    self.solve_recaptcha()
                )

                headers = list(
                    request_info.getHeaders()
                )

                content_type = (
                    self.find_header(
                        headers,
                        "Content-Type",
                    )
                    or ""
                ).lower()

                if (
                    "application/"
                    "x-www-form-urlencoded"
                    in content_type
                ):
                    replacement = (
                        urllib.quote_plus(
                            token
                        )
                    )
                else:
                    replacement = token

                new_body = body.replace(
                    PLACEHOLDER,
                    replacement,
                )

                new_request = (
                    self.helpers.buildHttpMessage(
                        headers,
                        self.helpers.stringToBytes(
                            new_body
                        ),
                    )
                )

                messageInfo.setRequest(
                    new_request
                )

                self.callbacks.printOutput(
                    "Captcha token inserted "
                    "into Repeater request"
                )

            except Exception as error:
                self.callbacks.printError(
                    "SolveCaptcha error: "
                    + str(error)
                )

    def find_header(
        self,
        headers,
        name,
    ):
        prefix = name.lower() + ":"

        for header in headers:
            if (
                header.lower()
                .startswith(prefix)
            ):
                return header.split(
                    ":",
                    1,
                )[1].strip()

        return None

    def post_form(
        self,
        url,
        parameters,
    ):
        encoded = urllib.urlencode(
            parameters
        )

        request = urllib2.Request(
            url,
            encoded,
        )

        response = urllib2.urlopen(
            request,
            timeout=30,
        )

        return json.loads(
            response.read()
        )

    def get_json(
        self,
        url,
        parameters,
    ):
        request_url = (
            url
            + "?"
            + urllib.urlencode(
                parameters
            )
        )

        response = urllib2.urlopen(
            request_url,
            timeout=30,
        )

        return json.loads(
            response.read()
        )

    def solve_recaptcha(self):
        create_result = self.post_form(
            CREATE_TASK_URL,
            {
                "key": API_KEY,
                "method": (
                    "userrecaptcha"
                ),
                "googlekey": SITE_KEY,
                "pageurl": PAGE_URL,
                "json": 1,
            },
        )

        if (
            create_result.get("status")
            != 1
        ):
            raise RuntimeError(
                "Task creation failed: "
                + str(
                    create_result.get(
                        "request"
                    )
                )
            )

        task_id = str(
            create_result.get(
                "request"
            )
        )

        time.sleep(20)

        deadline = (
            time.time() + 180
        )

        while time.time() < deadline:
            result = self.get_json(
                GET_RESULT_URL,
                {
                    "key": API_KEY,
                    "action": "get",
                    "id": task_id,
                    "json": 1,
                },
            )

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

                if not token:
                    raise RuntimeError(
                        "Empty token returned"
                    )

                return token

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

            if (
                error_code
                != "CAPCHA_NOT_READY"
            ):
                raise RuntimeError(
                    "Task failed: "
                    + error_code
                )

            time.sleep(5)

        raise RuntimeError(
            "Captcha solving timed out"
        )

Configure the extension

Set these constants before loading the extension:

ALLOWED_HOST = "example.com"
SITE_KEY = "SITE_KEY"
PAGE_URL = "https://example.com/account/login"

Set the SolveCaptcha API key before starting Burp:

export SOLVECAPTCHA_API_KEY="YOUR_API_KEY"

Load the Jython file through Burp Extender.

Then place the placeholder in the Repeater request:

g-recaptcha-response=__SOLVECAPTCHA_TOKEN__

When you click Send, the extension requests a fresh token and replaces the placeholder.

Extension limitations

The example extension is intentionally narrow.

It:

  • works only in Repeater;
  • processes one configured hostname;
  • solves standard reCAPTCHA v2;
  • replaces placeholders only in the request body;
  • waits synchronously for the solution;
  • does not detect the site key automatically;
  • does not process Intruder requests;
  • does not support high-volume traffic.

For Invisible v2, v3, or Enterprise, update the parameters inside solve_recaptcha().

Do not remove the hostname and Repeater restrictions unless you have a clear authorized testing requirement.

Common errors

ERROR_WRONG_USER_KEY

The API key is invalid.

Copy the key again from the SolveCaptcha account settings.

Check for spaces, missing characters, or an unset environment variable.

ERROR_ZERO_BALANCE

The account balance is insufficient.

Add funds before creating another task.

ERROR_CAPTCHA_UNSOLVABLE

The task could not be completed.

Check:

  • the site key;
  • the page URL;
  • the captcha version;
  • Invisible or Enterprise parameters;
  • the v3 action;
  • the requested v3 score.

Capture fresh parameters before retrying.

CAPCHA_NOT_READY

The task is still processing.

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

Do not create another task.

Server rejects the token

Possible causes include:

  • incorrect site key;
  • incorrect page URL;
  • expired token;
  • token generated for another session;
  • missing CSRF token;
  • incorrect reCAPTCHA action;
  • wrong request field;
  • changed cookies;
  • changed source IP;
  • another server-side validation error.

Compare the modified request with a successful request generated through the browser.

Repeater returns HTTP 403

A valid captcha token does not bypass every security control.

The request may still fail because of:

  • missing cookies;
  • invalid CSRF state;
  • expired session;
  • blocked IP address;
  • incorrect headers;
  • account restrictions;
  • WAF rules;
  • rate limits.

Inspect the response instead of assuming the captcha token was incorrect.

Token works in the browser but not in Repeater

The token may be tied to the browser session.

Copy the complete browser request to Repeater and preserve:

  • cookies;
  • User-Agent;
  • page URL;
  • CSRF value;
  • request body;
  • proxy path.

Request a fresh token immediately before sending the Repeater request.

No site key is visible in the HTML

The captcha may be rendered dynamically.

Search:

  • loaded JavaScript;
  • iframe URLs;
  • grecaptcha.render;
  • grecaptcha.execute;
  • the Network history;
  • the browser DOM after JavaScript executes.

Common implementation mistakes

Using createTask and getTaskResult

Incorrect:

createTask
getTaskResult
clientKey
taskId

Correct:

in.php
res.php
key
request

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

Injecting the token into the HTML response

When using Repeater, modify the final outgoing request.

There is usually no reason to edit the page HTML.

Reusing a token

Treat each token as short-lived and single-use.

Sending the token to the wrong endpoint

Generate the token for the page where the captcha appears, then submit it through the request expected by the application.

Ignoring URL encoding

URL-encode the token when inserting it into an application/x-www-form-urlencoded body.

Do not URL-encode it inside a normal JSON string.

Losing the original cookies

Repeater does not automatically reproduce every browser state change.

Confirm that the request contains the active session and CSRF cookies.

Automating unlimited retries

Set a fixed polling timeout and stop when it is reached.

Do not create tasks indefinitely.

Recommended Burp workflow

A reliable manual workflow is:

  1. Open the target page through Burp Proxy.
  2. Record the request and response sequence.
  3. Find the captcha site key.
  4. Record the exact page URL.
  5. Complete the captcha manually once.
  6. Identify the token field in the final request.
  7. Send that request to Repeater.
  8. Replace the old token with an empty value or placeholder.
  9. Request a fresh token through SolveCaptcha.
  10. Insert the token into the correct request field.
  11. Preserve cookies, CSRF values, and headers.
  12. Send the request before the token expires.
  13. Review the complete server response.

This approach is easier to debug than attempting full automation immediately.

Pre-launch checklist

Before using the workflow, confirm:

  • [ ] The target application is within the authorized test scope.
  • [ ] The captcha type has been identified correctly.
  • [ ] The current site key was extracted.
  • [ ] The full captcha page URL is correct.
  • [ ] The final token field was identified.
  • [ ] Active cookies are present in Repeater.
  • [ ] The CSRF token is still valid.
  • [ ] method=userrecaptcha is used.
  • [ ] The site key is sent as googlekey.
  • [ ] The page URL is sent as pageurl.
  • [ ] Invisible, v3, or Enterprise parameters are included when required.
  • [ ] The first result request waits approximately 15–20 seconds.
  • [ ] Later result requests use five-second intervals.
  • [ ] The completed token is read from request.
  • [ ] URL encoding matches the request content type.
  • [ ] A fresh token is used for each test attempt.

Frequently asked questions

Can Burp Suite solve captchas by itself?

No. Burp captures and modifies HTTP traffic. SolveCaptcha performs the solving step.

Which SolveCaptcha method is used for reCAPTCHA v2?

Use:

method=userrecaptcha

Where do I send the site key?

Use:

googlekey=SITE_KEY

Which page URL should I send?

Use the complete URL where the captcha is loaded.

Where is the token returned?

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

request

Where should I insert the token?

Insert it into the same request field used by the browser after a successful manual solve.

The standard field is often:

g-recaptcha-response

Do I need to invoke the JavaScript callback?

Not when you are reproducing the final HTTP request directly in Repeater.

The callback is part of the browser workflow. The server normally receives only the resulting token.

Can I use this with Invisible reCAPTCHA?

Yes.

Add:

invisible=1

Can I use this with reCAPTCHA v3?

Yes.

Add:

version=v3
action=ACTION
min_score=SCORE

Can I use it with reCAPTCHA Enterprise?

Yes.

Add:

enterprise=1

Can I reuse a token?

No. Request a fresh token for each new challenge or test attempt.

Should I use this with Intruder?

The example is designed for controlled Repeater testing.

High-volume automated credential attacks or requests outside the authorized scope are not appropriate uses of this workflow.

Conclusion

Bypassing captcha in Burp Suite requires coordination between Burp, the active application session, and the SolveCaptcha API.

The correct workflow is:

  1. Capture the protected page through Burp Proxy.
  2. Identify the captcha type.
  3. Extract the public site key.
  4. Record the complete page URL.
  5. Inspect a successful browser request.
  6. Find the field containing the captcha token.
  7. Submit the captcha parameters to in.php.
  8. Save the task ID returned in request.
  9. Wait approximately 15–20 seconds.
  10. Poll res.php every five seconds.
  11. Read the completed token from request.
  12. Insert the token into the Repeater request.
  13. Preserve cookies, CSRF values, headers, and session state.
  14. Send the request before the token expires.
  15. Review the complete response for other validation failures.

Use the SolveCaptcha API documentation to verify the current parameters for each supported captcha type.