Captcha solving service blog | SolveCaptcha How to bypass Friendly Captcha with solver

How to bypass Friendly Captcha with solver

Friendly Captcha is a privacy-focused bot-protection system that normally works without image grids, checkboxes, or other interactive puzzles.

Instead of asking the visitor to recognize objects, the widget performs a computational challenge in the background. When that work is complete, it generates a token that the website verifies on its server.

With the SolveCaptcha API, you can submit the public challenge parameters, retrieve a ready token, and pass it to the protected form or callback.

The complete workflow is:

Identify the Friendly Captcha version
→ extract the site key
→ record the widget script URLs
→ prevent the native widget from initializing
→ create a SolveCaptcha task
→ retrieve the solution token
→ update the expected form field
→ call the callback when required
→ continue the normal form workflow

This guide covers:

  • Friendly Captcha v1 and v2;
  • version detection;
  • site key extraction;
  • module and nomodule script detection;
  • SolveCaptcha request parameters;
  • optional proxy configuration;
  • task polling;
  • token insertion;
  • callback handling;
  • native widget blocking;
  • common integration errors.

Use this method only on websites you own or are authorized to test and automate.

What is Friendly Captcha?

Friendly Captcha is a bot-protection system based primarily on computational Proof-of-Work.

The widget asks the browser to complete a mathematical challenge. The result is converted into a token and submitted with the protected form.

A normal visitor usually sees only a small status indicator while the challenge runs in the background.

The website may use Friendly Captcha to protect:

  • login forms;
  • registration pages;
  • contact forms;
  • password recovery;
  • checkout flows;
  • newsletter subscriptions;
  • account actions;
  • public API forms.

Unlike a traditional image captcha, Friendly Captcha does not normally ask the visitor to select traffic lights, enter distorted text, or move a slider.

For automation, the important result is the final solution token.

How Friendly Captcha works

A typical Friendly Captcha workflow looks like this:

The page loads the Friendly Captcha widget
→ the widget requests a challenge
→ the browser performs computational work
→ the widget generates a solution token
→ the token is written to a hidden field
→ the form submits the token to the website
→ the website verifies the solution

The token is usually placed into a field named:

frc-captcha-solution

The field name can be changed through:

data-solution-field-name

The page may also define a callback that receives the token after the challenge is complete.

Friendly Captcha v1 and v2

SolveCaptcha supports both Friendly Captcha versions.

The API parameter is:

version

Accepted values are:

v1
v2

When version is omitted, SolveCaptcha uses:

v1

Always identify and send the correct version. A v2 integration submitted as v1 may produce an unusable token or an unsolvable task.

Main differences between v1 and v2

Feature Friendly Captcha v1 Friendly Captcha v2
API value version=v1 version=v2
Common package name friendly-challenge Friendly Captcha v2
Common script path Package version inside the URL Usually contains /v2/
Default in SolveCaptcha Yes No
Recommended handling Send version explicitly Send version explicitly

Script hostnames and file paths can be customized by the website.

Do not identify the version only from the hostname. Inspect the complete script URL and the page configuration.

How to identify the version

Open DevTools and inspect the scripts loaded by the page.

Common v1 pattern

A v1 page may load scripts similar to:

<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/[email protected]/widget.module.min.js">
</script>

<script
  nomodule
  src="https://cdn.jsdelivr.net/npm/[email protected]/widget.js">
</script>

Common v1 indicators include:

friendly-challenge
widget.module.min.js
cdn.jsdelivr.net/npm/friendly-challenge

Common v2 pattern

A v2 page may load scripts similar to:

<script
  type="module"
  src="https://cdn.friendlycaptcha.com/v2/widget.module.min.js">
</script>

<script
  nomodule
  src="https://cdn.friendlycaptcha.com/v2/widget.js">
</script>

Common v2 indicators include:

friendlycaptcha.com/v2/
widget.module.min.js
/v2/

A website may host the scripts on its own CDN. In that case, inspect the package name, path, and widget configuration instead of relying only on the domain.

Find Friendly Captcha scripts in the console

Run the following code in the browser console:

const friendlyCaptchaScripts = [
  ...document.scripts
]
  .filter((script) => {
    const source = script.src || "";

    return (
      /friendly-challenge/i.test(source)
      || /friendlycaptcha/i.test(source)
      || /widget\.module(?:\.min)?\.js/i.test(source)
    );
  })
  .map((script) => ({
    src: script.src,
    type: script.type || null,
    noModule: script.noModule
  }));

console.table(
  friendlyCaptchaScripts
);

Example output:

src: https://cdn.friendlycaptcha.com/v2/widget.module.min.js
type: module
noModule: false

This gives you the script URL and helps identify the version.

How to find the site key

The Friendly Captcha site key is usually stored in:

data-apikey

Example:

<div
  class="frc-captcha"
  data-apikey="7KXMD4TQ9PL8RW">
</div>

The site key is:

7KXMD4TQ9PL8RW

Send this value to SolveCaptcha as:

sitekey

Do not send it as:

websiteKey
publickey
googlekey
apiKey

The page attribute is named data-apikey, but the SolveCaptcha request parameter is named sitekey.

Extract the site key in the console

Run:

const widget = document.querySelector(
  ".frc-captcha[data-apikey],"
  + "[data-apikey]"
);

if (!widget) {
  console.log(
    "Friendly Captcha widget was not found."
  );
} else {
  console.log({
    sitekey:
      widget.getAttribute(
        "data-apikey"
      ),
    callback:
      widget.getAttribute(
        "data-callback"
      ),
    solutionFieldName:
      widget.getAttribute(
        "data-solution-field-name"
      )
      || "frc-captcha-solution"
  });
}

Example result:

{
  "sitekey": "7KXMD4TQ9PL8RW",
  "callback": "doneCallback",
  "solutionFieldName": "frc-captcha-solution"
}

Complete parameter detector

The following script extracts the most important integration parameters:

function detectFriendlyCaptcha() {
  const widget = document.querySelector(
    ".frc-captcha[data-apikey],"
    + "[data-apikey]"
  );

  const scripts = [
    ...document.scripts
  ];

  const moduleScript = scripts.find(
    (script) => {
      const source =
        script.src || "";

      return (
        script.type === "module"
        && (
          /friendly-challenge/i.test(source)
          || /friendlycaptcha/i.test(source)
        )
      );
    }
  );

  const nomoduleScript = scripts.find(
    (script) => {
      const source =
        script.src || "";

      return (
        script.noModule
        && (
          /friendly-challenge/i.test(source)
          || /friendlycaptcha/i.test(source)
        )
      );
    }
  );

  const scriptSources = scripts
    .map((script) => script.src)
    .filter(Boolean)
    .filter((source) =>
      /friendly-challenge|friendlycaptcha/i.test(
        source
      )
    );

  const allSources = [
    moduleScript?.src,
    nomoduleScript?.src,
    ...scriptSources
  ].filter(Boolean);

  const version = allSources.some(
    (source) =>
      /\/v2(?:\/|$)/i.test(source)
  )
    ? "v2"
    : "v1";

  return {
    sitekey:
      widget?.getAttribute(
        "data-apikey"
      )
      || null,
    version,
    moduleScript:
      moduleScript?.src
      || null,
    nomoduleScript:
      nomoduleScript?.src
      || null,
    callback:
      widget?.getAttribute(
        "data-callback"
      )
      || null,
    solutionFieldName:
      widget?.getAttribute(
        "data-solution-field-name"
      )
      || "frc-captcha-solution",
    pageurl:
      window.location.href
  };
}

console.log(
  detectFriendlyCaptcha()
);

Review the detected version manually before creating the API task.

Custom script hosting can prevent automatic version detection from being completely reliable.

SolveCaptcha API endpoints

Create a Friendly Captcha task through:

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

Retrieve the result through:

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

The required method is:

method=friendly_captcha

Do not use unsupported request structures such as:

FriendlyCaptchaTask
FriendlyCaptchaTaskProxyless
createTask
getTaskResult
clientKey
websiteURL
websiteKey
moduleScript
nomoduleScript

The documented SolveCaptcha parameter names are:

key
method
sitekey
pageurl
version
module_script
nomodule_script
proxy
proxytype
json

Required parameters

Parameter Required Description
key Yes Your SolveCaptcha API key
method Yes Must be friendly_captcha
sitekey Yes Value of the widget’s data-apikey attribute
pageurl Yes Full URL of the page containing the captcha
version No v1 or v2; default is v1
module_script No URL of the script with type="module"
nomodule_script No URL of the script with the nomodule attribute
proxy No Proxy in login:password@host:port format
proxytype No HTTP, HTTPS, SOCKS4, or SOCKS5
json Recommended Set to 1 for JSON responses

Although version, module_script, and nomodule_script are optional, include them whenever you can identify them reliably.

Explicit parameters reduce ambiguity and make the integration easier to debug.

Get your SolveCaptcha API key

Copy the API key from your SolveCaptcha account settings.

Store it in an environment variable instead of placing it directly in source code.

Linux or macOS:

export SOLVECAPTCHA_API_KEY="YOUR_API_KEY"

Windows PowerShell:

$env:SOLVECAPTCHA_API_KEY="YOUR_API_KEY"

Do not include the key in:

  • public repositories;
  • browser JavaScript;
  • screenshots;
  • application logs;
  • support messages.

Create a Friendly Captcha v1 task

Example request:

curl --request POST \
  --url https://api.solvecaptcha.com/in.php \
  --data-urlencode "key=YOUR_API_KEY" \
  --data-urlencode "method=friendly_captcha" \
  --data-urlencode "sitekey=7KXMD4TQ9PL8RW" \
  --data-urlencode "pageurl=https://example.com/login" \
  --data-urlencode "version=v1" \
  --data-urlencode "module_script=https://cdn.example.com/friendly-challenge/widget.module.min.js" \
  --data-urlencode "nomodule_script=https://cdn.example.com/friendly-challenge/widget.js" \
  --data-urlencode "json=1"

A successful response contains the task ID:

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

Save the value from:

request

Create a Friendly Captcha v2 task

For v2, set:

version=v2

Example:

curl --request POST \
  --url https://api.solvecaptcha.com/in.php \
  --data-urlencode "key=YOUR_API_KEY" \
  --data-urlencode "method=friendly_captcha" \
  --data-urlencode "sitekey=7KXMD4TQ9PL8RW" \
  --data-urlencode "pageurl=https://example.com/checkout" \
  --data-urlencode "version=v2" \
  --data-urlencode "module_script=https://cdn.example.com/v2/widget.module.min.js" \
  --data-urlencode "nomodule_script=https://cdn.example.com/v2/widget.js" \
  --data-urlencode "json=1"

The response format is the same as for v1:

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

Use an optional proxy

SolveCaptcha does not require separate proxyless and proxy task types for Friendly Captcha.

To use your own proxy, add:

proxy=login:password@host:port
proxytype=HTTP

Example:

curl --request POST \
  --url https://api.solvecaptcha.com/in.php \
  --data-urlencode "key=YOUR_API_KEY" \
  --data-urlencode "method=friendly_captcha" \
  --data-urlencode "sitekey=7KXMD4TQ9PL8RW" \
  --data-urlencode "pageurl=https://example.com/login" \
  --data-urlencode "version=v2" \
  --data-urlencode "module_script=https://cdn.example.com/v2/widget.module.min.js" \
  --data-urlencode "nomodule_script=https://cdn.example.com/v2/widget.js" \
  --data-urlencode "proxy=user:[email protected]:8000" \
  --data-urlencode "proxytype=HTTP" \
  --data-urlencode "json=1"

Supported proxy types are:

HTTP
HTTPS
SOCKS4
SOCKS5

Use a proxy only when the authorized workflow requires a specific network location or consistent source address.

Retrieve the solution

Request the result from:

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

Required parameters are:

key
action=get
id=TASK_ID
json=1

Example:

curl "https://api.solvecaptcha.com/res.php?key=YOUR_API_KEY&action=get&id=2112373961&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.

Do not create another task while the current one is still processing.

A completed task returns:

{
  "status": 1,
  "request": "c3e9a7d5f1b2486fae7c93d2ab41e8f0.Q7K2L...BBBBBB.BhEF"
}

The value in request is the Friendly Captcha solution token.

Complete Python example

Install Requests:

python -m pip install requests

Set the API key:

export SOLVECAPTCHA_API_KEY="YOUR_API_KEY"

Create solve_friendly_captcha.py:

#!/usr/bin/env python3

from __future__ import annotations

import os
import time
from dataclasses import dataclass
from typing import Any, Literal

import requests

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

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

POLL_INTERVAL_SECONDS = 5
MAX_WAIT_SECONDS = 180

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

@dataclass(frozen=True)
class FriendlyCaptchaSolution:
    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 create_task(
    *,
    api_key: str,
    site_key: str,
    page_url: str,
    version: Literal["v1", "v2"],
    module_script: str | None = None,
    nomodule_script: str | None = None,
    proxy: str | None = None,
    proxy_type: str | None = None,
) -> str:
    payload: dict[str, Any] = {
        "key": api_key,
        "method": "friendly_captcha",
        "sitekey": site_key,
        "pageurl": page_url,
        "version": version,
        "json": 1,
    }

    if module_script:
        payload["module_script"] = (
            module_script
        )

    if nomodule_script:
        payload["nomodule_script"] = (
            nomodule_script
        )

    if proxy:
        payload["proxy"] = proxy

    if proxy_type:
        payload["proxytype"] = (
            proxy_type
        )

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

    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(
    *,
    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,
    )

    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 failed: {error_code}"
    )

def solve_friendly_captcha(
    *,
    api_key: str,
    site_key: str,
    page_url: str,
    version: Literal["v1", "v2"],
    module_script: str | None = None,
    nomodule_script: str | None = None,
    proxy: str | None = None,
    proxy_type: str | None = None,
) -> FriendlyCaptchaSolution:
    task_id = create_task(
        api_key=api_key,
        site_key=site_key,
        page_url=page_url,
        version=version,
        module_script=module_script,
        nomodule_script=nomodule_script,
        proxy=proxy,
        proxy_type=proxy_type,
    )

    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 FriendlyCaptchaSolution(
                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 main() -> None:
    api_key = os.environ.get(
        "SOLVECAPTCHA_API_KEY",
        "",
    ).strip()

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

    solution = solve_friendly_captcha(
        api_key=api_key,
        site_key="7KXMD4TQ9PL8RW",
        page_url=(
            "https://example.com/login"
        ),
        version="v2",
        module_script=(
            "https://cdn.example.com/"
            "v2/widget.module.min.js"
        ),
        nomodule_script=(
            "https://cdn.example.com/"
            "v2/widget.js"
        ),
    )

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

    print(
        "Token:",
        solution.token,
    )

if __name__ == "__main__":
    main()

Run the script:

python solve_friendly_captcha.py

Example output:

Task ID: 2112373961
Token: c3e9a7d5f1b2486fae7c93d2ab41e8f0.Q7K2L...BBBBBB.BhEF

Why the native widget must be blocked

The SolveCaptcha documentation warns that the Friendly Captcha widget should not initialize before the external token is applied.

When the native widget loads, it can:

  • request its own challenge;
  • create or replace the solution field;
  • overwrite the injected token;
  • reset its internal state;
  • submit a different solution;
  • reject a token created outside its current state.

The recommended sequence is:

Extract the integration parameters
→ configure script blocking
→ load the page without initializing the widget
→ request the SolveCaptcha token
→ apply the token
→ continue the form workflow

Do not block the scripts before you know their URLs.

First inspect the page manually or run a diagnostic session. Then configure your automation to block the exact script URLs.

Block the widget with Playwright

Use the exact script URLs extracted from the page:

from playwright.async_api import (
    async_playwright,
)

MODULE_SCRIPT_URL = (
    "https://cdn.example.com/"
    "v2/widget.module.min.js"
)

NOMODULE_SCRIPT_URL = (
    "https://cdn.example.com/"
    "v2/widget.js"
)

async def main() -> None:
    async with async_playwright() as playwright:
        browser = await playwright.chromium.launch(
            headless=False
        )

        page = await browser.new_page()

        blocked_urls = {
            MODULE_SCRIPT_URL,
            NOMODULE_SCRIPT_URL,
        }

        async def block_widget(route) -> None:
            if route.request.url in blocked_urls:
                await route.abort()
                return

            await route.continue_()

        await page.route(
            "**/*",
            block_widget,
        )

        await page.goto(
            "https://example.com/login"
        )

        # Retrieve and apply the token here.

        await browser.close()

Blocking exact URLs is safer than blocking every request containing the word friendlycaptcha.

Block the widget with Puppeteer

Configure interception before opening the page:

import puppeteer from "puppeteer";

const moduleScriptUrl =
  "https://cdn.example.com/"
  + "v2/widget.module.min.js";

const nomoduleScriptUrl =
  "https://cdn.example.com/"
  + "v2/widget.js";

const blockedUrls = new Set([
  moduleScriptUrl,
  nomoduleScriptUrl
]);

const browser =
  await puppeteer.launch({
    headless: false
  });

const page =
  await browser.newPage();

await page.setRequestInterception(
  true
);

page.on(
  "request",
  (request) => {
    if (
      blockedUrls.has(
        request.url()
      )
    ) {
      request.abort();
      return;
    }

    request.continue();
  }
);

await page.goto(
  "https://example.com/login"
);

// Retrieve and apply the token here.

await browser.close();

Do not call setRequestInterception() after the widget has already loaded.

Block the widget with Selenium

Chrome DevTools Protocol can block exact URLs before navigation:

from selenium import webdriver

MODULE_SCRIPT_URL = (
    "https://cdn.example.com/"
    "v2/widget.module.min.js"
)

NOMODULE_SCRIPT_URL = (
    "https://cdn.example.com/"
    "v2/widget.js"
)

driver = webdriver.Chrome()

driver.execute_cdp_cmd(
    "Network.enable",
    {},
)

driver.execute_cdp_cmd(
    "Network.setBlockedURLs",
    {
        "urls": [
            MODULE_SCRIPT_URL,
            NOMODULE_SCRIPT_URL,
        ]
    },
)

driver.get(
    "https://example.com/login"
)

# Retrieve and apply the token here.

Configure blocking before calling driver.get().

How to apply the token

The standard Friendly Captcha solution field is:

frc-captcha-solution

A common field looks like this:

<input
  type="hidden"
  name="frc-captcha-solution"
  class="frc-captcha-solution">

Insert the token into that field:

const token =
  "TOKEN_FROM_SOLVECAPTCHA";

const field = document.querySelector(
  'input[name="frc-captcha-solution"],'
  + "input.frc-captcha-solution"
);

if (!field) {
  throw new Error(
    "Friendly Captcha solution field "
    + "was not found."
  );
}

field.value = token;

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

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

Do not rely only on the CSS class. The field name is more important during form submission.

Handle a custom solution field name

The widget may specify:

<div
  class="frc-captcha"
  data-apikey="SITE_KEY"
  data-solution-field-name="verification_token">
</div>

The expected field name is then:

verification_token

Use the configured name:

const widget = document.querySelector(
  ".frc-captcha[data-apikey],"
  + "[data-apikey]"
);

const fieldName =
  widget?.getAttribute(
    "data-solution-field-name"
  )
  || "frc-captcha-solution";

const field = document.querySelector(
  `input[name="${CSS.escape(fieldName)}"]`
);

if (!field) {
  throw new Error(
    `Solution field not found: ${fieldName}`
  );
}

field.value =
  "TOKEN_FROM_SOLVECAPTCHA";

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

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

Use the actual field name configured by the page.

Call the callback

A Friendly Captcha widget may define a callback:

<div
  class="frc-captcha"
  data-apikey="SITE_KEY"
  data-callback="doneCallback">
</div>

After applying the token, call:

doneCallback(
  "TOKEN_FROM_SOLVECAPTCHA"
);

For a nested callback such as:

application.forms.doneCallback

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.doneCallback"
);

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

Do not call an unrelated function only because its name contains captcha or submit.

Use the callback configured by the current widget.

Complete browser-side token application

The following helper:

  • finds the widget;
  • detects the expected field name;
  • updates the solution field;
  • dispatches browser events;
  • resolves the configured callback;
  • returns diagnostic information.
function applyFriendlyCaptchaToken(
  token
) {
  if (
    typeof token !== "string"
    || token.trim() === ""
  ) {
    throw new TypeError(
      "A non-empty token is required."
    );
  }

  const widget = document.querySelector(
    ".frc-captcha[data-apikey],"
    + "[data-apikey]"
  );

  const fieldName =
    widget?.getAttribute(
      "data-solution-field-name"
    )
    || "frc-captcha-solution";

  const escapedFieldName =
    typeof CSS !== "undefined"
    && typeof CSS.escape === "function"
      ? CSS.escape(fieldName)
      : fieldName.replace(
          /["\\]/g,
          "\\$&"
        );

  const fields = [
    ...document.querySelectorAll(
      `input[name="${escapedFieldName}"]`
    )
  ];

  if (fields.length === 0) {
    throw new Error(
      `Friendly Captcha field not found: ${fieldName}`
    );
  }

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

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

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

  const callbackName =
    widget?.getAttribute(
      "data-callback"
    )
    || null;

  let callbackCalled = false;

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

    if (
      typeof callback
      === "function"
    ) {
      callback(token);
      callbackCalled = true;
    }
  }

  return {
    fieldName,
    fieldsUpdated:
      fields.length,
    callbackName,
    callbackCalled
  };
}

Usage:

const result =
  applyFriendlyCaptchaToken(
    "TOKEN_FROM_SOLVECAPTCHA"
  );

console.log(result);

Submit the form correctly

After applying the token and calling the callback, continue the page’s normal workflow.

Prefer:

form.requestSubmit();

instead of:

form.submit();

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

Example:

const field = document.querySelector(
  'input[name="frc-captcha-solution"]'
);

const form = field?.closest(
  "form"
);

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

form.requestSubmit();

Do not submit the first form on the page without confirming that it contains the Friendly Captcha field.

Applying the token with Selenium

Example:

token = solution.token

result = driver.execute_script(
    """
    const token = arguments[0];

    const widget = document.querySelector(
      ".frc-captcha[data-apikey],"
      + "[data-apikey]"
    );

    const fieldName =
      widget?.getAttribute(
        "data-solution-field-name"
      )
      || "frc-captcha-solution";

    const field = document.querySelector(
      `input[name="${fieldName}"]`
    );

    if (!field) {
      throw new Error(
        `Solution field not found: ${fieldName}`
      );
    }

    field.value = token;

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

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

    const callbackName =
      widget?.getAttribute(
        "data-callback"
      );

    let callbackCalled = false;

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

      if (
        typeof callback
        === "function"
      ) {
        callback(token);
        callbackCalled = true;
      }
    }

    return {
      fieldName,
      callbackName,
      callbackCalled
    };
    """,
    token,
)

print(result)

Continue with the intended button click or form submission after verifying that the field was updated.

Common API errors

ERROR_WRONG_USER_KEY

The SolveCaptcha API key is invalid.

Copy it again from your account settings and remove any spaces or line breaks.

ERROR_ZERO_BALANCE

The account balance is insufficient.

Add funds before creating another task.

ERROR_CAPTCHA_UNSOLVABLE

The task could not be completed with the submitted parameters.

Check:

  • sitekey;
  • pageurl;
  • version;
  • module_script;
  • nomodule_script;
  • optional proxy settings;
  • whether the page still uses the same captcha configuration.

Extract fresh parameters and create a new task.

CAPCHA_NOT_READY

The task is still processing.

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

Do not create a duplicate task.

Website-side errors

Token is rejected

Common causes include:

  • incorrect Friendly Captcha version;
  • incorrect site key;
  • incorrect page URL;
  • native widget already initialized;
  • token inserted into the wrong field;
  • custom solution field name ignored;
  • callback not called;
  • page state changed;
  • token used after the form was reset.

Confirm every parameter against the current page.

Solution field is missing

The native widget may normally create the field during initialization.

When its scripts are blocked, the field may not exist automatically.

For an application you control, render the expected hidden input in the form:

<input
  type="hidden"
  name="frc-captcha-solution">

For third-party authorized testing, inspect the page’s expected request and form structure before adding any field dynamically.

Callback is not accessible

The callback may be defined inside:

  • a JavaScript module;
  • a framework component;
  • a closure;
  • a non-global scope.

In that case, calling window[callbackName] will not work.

Inspect the normal application flow and determine how the token is passed to the form or request.

Widget still loads after blocking

Possible causes include:

  • only the module script was blocked;
  • the nomodule fallback still loaded;
  • the website uses a custom script URL;
  • another loader imports the widget dynamically;
  • blocking started after page navigation.

Record the Network panel and block the exact active script URLs before loading the page.

Common implementation mistakes

Using createTask and getTaskResult

Incorrect:

createTask
getTaskResult
FriendlyCaptchaTaskProxyless
FriendlyCaptchaTask
clientKey

Correct:

in.php
res.php
method=friendly_captcha
key

Using websiteKey instead of sitekey

Incorrect:

websiteKey=7KXMD4TQ9PL8RW

Correct:

sitekey=7KXMD4TQ9PL8RW

Using websiteURL instead of pageurl

Incorrect:

websiteURL=https://example.com

Correct:

pageurl=https://example.com

Using camelCase script parameters

Incorrect:

moduleScript
nomoduleScript

Correct:

module_script
nomodule_script

Omitting version for v2

The default is v1.

Always send:

version=v2

when the website uses Friendly Captcha v2.

Guessing the version

Inspect the actual scripts and configuration.

Do not assume that every new website uses v2 or that every jsDelivr URL uses v1.

Blocking every Friendly Captcha request

Block the widget script URLs required to prevent initialization.

Do not automatically block unrelated API, form, or application requests.

Blocking scripts too late

Configure interception before navigation.

Updating the wrong field

Check:

data-solution-field-name

before assuming the field is named:

frc-captcha-solution

Calling only the callback

The form may still expect the hidden field.

Update the field first, then call the callback.

Updating only the field

Some applications also depend on their configured callback.

Using form.submit()

Prefer:

requestSubmit()

to preserve normal validation and submit events.

Reusing a token

Treat the solution as specific to the current captcha configuration and form workflow.

Best practices

Always send the version explicitly

Use:

version=v1

or:

version=v2

Do not rely on the default.

Extract script URLs from the current page

Send the actual values found in:

type="module"
nomodule

Do not copy script URLs from another website or an old integration.

Inspect before blocking

Use one diagnostic session to collect:

  • site key;
  • version;
  • module script;
  • nomodule script;
  • callback;
  • solution field name.

Then configure blocking for the automated session.

Block exact script URLs

Exact matching reduces the risk of interrupting unrelated website functionality.

Preserve the expected form structure

Confirm which field, callback, and form are used by the page.

Use controlled polling

Poll every five seconds while the API returns:

CAPCHA_NOT_READY

Set a reasonable total timeout and stop retrying after it expires.

Log diagnostic information

Record:

  • task ID;
  • page URL;
  • Friendly Captcha version;
  • site key identifier;
  • script URLs;
  • task creation time;
  • result time;
  • error codes;
  • field name;
  • callback name.

Do not log:

  • SolveCaptcha API keys;
  • proxy passwords;
  • authentication cookies;
  • complete sensitive form data.

Pre-launch checklist

Before deploying the integration, confirm:

  • [ ] The page uses Friendly Captcha.
  • [ ] The data-apikey value was extracted.
  • [ ] The site key is sent as sitekey.
  • [ ] The exact page URL is sent as pageurl.
  • [ ] The Friendly Captcha version was identified.
  • [ ] version=v1 or version=v2 is sent explicitly.
  • [ ] The module script URL was extracted when available.
  • [ ] The nomodule script URL was extracted when available.
  • [ ] Script blocking is configured before navigation.
  • [ ] Only the required widget scripts are blocked.
  • [ ] The task ID is read from request.
  • [ ] res.php is polled every five seconds.
  • [ ] The completed token is read from request.
  • [ ] The custom solution field name is respected.
  • [ ] Input and change events are dispatched.
  • [ ] The configured callback is called when required.
  • [ ] The correct form is submitted with requestSubmit().

Frequently asked questions

Which SolveCaptcha method should I use?

Use:

method=friendly_captcha

Which endpoint creates the task?

Use:

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

Which endpoint returns the result?

Use:

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

Where can I find the site key?

Look for:

data-apikey

on the Friendly Captcha widget.

Which API parameter receives the site key?

Use:

sitekey

Does SolveCaptcha support Friendly Captcha v2?

Yes.

Send:

version=v2

What happens when version is omitted?

SolveCaptcha uses:

v1

Are module_script and nomodule_script required?

They are optional.

Include them when they are available because they identify the active widget implementation more precisely.

Is a proxy required?

No. Proxy parameters are optional for the documented Friendly Captcha method.

Where is the token returned?

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

request

Where should the token be inserted?

The standard field name is:

frc-captcha-solution

Check data-solution-field-name because the website can customize it.

Should the callback be called?

Call it when the widget defines:

data-callback

Why must the native widget be blocked?

If the widget initializes, it may overwrite the external token or create an incompatible internal state.

Should both module and nomodule scripts be blocked?

Block every widget script that the active browser can load.

Using the exact extracted URLs is the safest approach.

Can I submit the form with form.submit()?

Prefer:

form.requestSubmit()

because it preserves normal validation and submit events.

How often should I poll the result?

Poll every five seconds while the response is:

CAPCHA_NOT_READY

Conclusion

Bypassing Friendly Captcha with SolveCaptcha requires the correct version, site key, page URL, and token application flow.

The correct process is:

  1. Confirm that the page uses Friendly Captcha.
  2. Extract the site key from data-apikey.
  3. Identify whether the page uses v1 or v2.
  4. Record the module script URL.
  5. Record the nomodule script URL.
  6. Check the configured callback.
  7. Check data-solution-field-name.
  8. Configure exact script blocking before navigation.
  9. Submit method=friendly_captcha to in.php.
  10. Pass the site key as sitekey.
  11. Pass the full page URL as pageurl.
  12. Send the correct version.
  13. Include module_script and nomodule_script when available.
  14. Save the task ID returned in request.
  15. Poll res.php every five seconds.
  16. Read the completed token from request.
  17. Insert the token into the expected solution field.
  18. Dispatch input and change events.
  19. Call the configured callback when required.
  20. Continue the form’s normal submission workflow.

Use the SolveCaptcha Friendly Captcha documentation to verify the current parameters before deploying the integration.