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-apikeyvalue was extracted. - [ ] The site key is sent as
sitekey. - [ ] The exact page URL is sent as
pageurl. - [ ] The Friendly Captcha version was identified.
- [ ]
version=v1orversion=v2is 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.phpis 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:
- Confirm that the page uses Friendly Captcha.
- Extract the site key from
data-apikey. - Identify whether the page uses v1 or v2.
- Record the module script URL.
- Record the nomodule script URL.
- Check the configured callback.
- Check
data-solution-field-name. - Configure exact script blocking before navigation.
- Submit
method=friendly_captchatoin.php. - Pass the site key as
sitekey. - Pass the full page URL as
pageurl. - Send the correct
version. - Include
module_scriptandnomodule_scriptwhen available. - Save the task ID returned in
request. - Poll
res.phpevery five seconds. - Read the completed token from
request. - Insert the token into the expected solution field.
- Dispatch input and change events.
- Call the configured callback when required.
- Continue the form’s normal submission workflow.
Use the SolveCaptcha Friendly Captcha documentation to verify the current parameters before deploying the integration.