SurSolver

BlogCaptchaFox solver API: solve CaptchaFoxTask end to end

CaptchaFox solver API: solve CaptchaFoxTask end to end

· Updated · 8 min read

This guide walks you through one CaptchaFox solve from start to finish. You will find the sk_ sitekey, create a CaptchaFoxTask that carries your proxy, poll for the token and submit it in cf-captcha-response from the same IP. The code is Python with requests and Playwright, written against the SurSolver API.

The point to keep in mind throughout: a CaptchaFox token is bound to the IP that solved it. Everything below, from the task to the form submission, is organised around keeping one proxy session constant.

What you get back

CaptchaFox is a privacy-first, behavioural captcha built and hosted in Europe. It scores cursor movement, click timing and scroll patterns together with technical signals and network context, and shows a visible challenge only to suspicious sessions: a slide puzzle, a one-click confirmation or an audio fallback.

A page that uses it loads cdn.captchafox.com/api.js and has a container such as <div class="captchafox" data-sitekey="...">. On success the widget adds a hidden input with the response token, submitted as the POST field cf-captcha-response. The site then verifies the token server-side, optionally together with the IP it came from.

A solved task returns a token string, plus userAgent when the solver reported one. The user agent is conditional, so read it with a default. The token is single-use and short-lived. The CaptchaFox solver guide covers how to recognise the widget.

The script host is not a task parameter

Some sites load the widget from a different script host. United Internet properties such as mail.com, gmx.com and web.de use a dedicated host under s.uicdn.com, and their tokens carry a MAM_ prefix. Some other solvers expose an apiServer field to select that host. The SurSolver task has no such parameter; it takes the website URL, the sitekey and a proxy, plus an optional user agent.

So view the page source before you spend a task: if the widget's <script src> points at cdn.captchafox.com, the task targets your site. If it points elsewhere, a standard token may be rejected, and SurSolver has no setting to change that.

Parameters

Field Required What it is
type yes CaptchaFoxTask; CaptchaFoxToken is accepted as another spelling
websiteURL yes The exact page that shows the widget
websiteKey yes The sitekey, which starts with sk_
proxy yes Your proxy, as a string or as split fields
userAgent no The user agent of the browser that will submit the token

CaptchaFox has no proxyless name. CaptchaFoxTaskProxyLess answers ERROR_TYPE_NOT_SUPPORTED, and a task without proxy details is rejected with ERROR_INVALID_REQUEST.

The proxy goes in task.proxy, for example http://user:password@203.0.113.10:8000. A string with four colon-separated parts is always read as user:password:host:port. Split fields (proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword) are the explicit alternative; the proxies guide lists every format.

Find the sitekey

CaptchaFox sitekeys start with sk_. Try these in order:

  • Page source. Press Ctrl+U and search for captchafox, sk_, data-key or CaptchaFox.init. Look for a container with data-sitekey or data-key, or a CaptchaFox.init({ key: "sk_..." }) call.
  • Network tab. This is the most reliable method. Open DevTools, reload, filter requests by captchafox or challenge, and read the key from the query parameters or the response.
  • Console. Try document.querySelector('[data-key]')?.dataset.key.

The captchafox container with its data-sitekey attribute holding the documented CaptchaFox test sitekey, selected in the DevTools Elements panel on a local demo signup form

On some sites the key changes per session or page load. If a setup that worked starts failing, read the key again from a fresh page load before you suspect anything else. Identify any captcha and extract its parameters covers the general method.

Create the task and poll

solve.sh
curl -sS -X POST "https://api.sursolver.com/createTask" \
  -H "Content-Type: application/json" \
  -d '{
    "clientKey": "YOUR_API_KEY",
    "task": {
      "type": "CaptchaFoxTask",
      "websiteURL": "https://www.example.com/signup",
      "websiteKey": "sk_YOUR_SITEKEY",
      "proxy": "http://user:password@203.0.113.10:8000"
    }
  }'

An accepted task answers:

snippet.json
{"errorId":0,"taskId":"tsk_9c1f4e2a7b0d5e3f81a62c47","status":"idle"}

Poll getTaskResult every 3 seconds. {"errorId":0,"status":"processing"} means keep waiting. When the task is done:

snippet.json
{
  "errorId": 0,
  "status": "ready",
  "solution": {
    "token": "7828075fb55ecbd9146745ee6f2bec475b88076d36e23050f1fb28359ffca15d",
    "userAgent": "Mozilla/5.0 ..."
  }
}

API errors are HTTP 200 with errorId: 1; stop polling at the first one. An unexpected server fault is an HTTP 500 without errorId. The API sets no polling ceiling, so add a deadline in your own loop. Tokens live only a few minutes, so a short deadline is fine.

Submit cf-captcha-response

On a standard integration the token goes into the POST field cf-captcha-response, and the site verifies it with CaptchaFox. Custom integrations rename it. Complete the captcha once by hand and read the request in the Network tab: observed variants include cf_token, captcha_response, captchafox_token, a header such as X-Captcha-Token, or a field inside a JSON body. Use whatever the site's own request uses. How to submit a captcha token goes through the patterns.

Three conditions have to hold at submission:

  1. Same IP. Send the request through the proxy you gave the task. If the IPs differ, the site's verification fails with invalid-ip-match.
  2. Same user agent. When the solution includes userAgent, submit with it. When it does not, the result does not say which browser the solve presented; use your client's own user agent and confirm the first submission is accepted before scaling up.
  3. Straight away. The token is single-use and expires within minutes. A replay or a late submit comes back as timeout-or-duplicate.

Python and Playwright examples

This script solves the task and submits the form with an HTTP client that uses the same proxy:

solve.py
import os
import time

import requests

API = 'https://api.sursolver.com'
API_KEY = os.environ['SURSOLVER_API_KEY']
PAGE_URL = 'https://www.example.com/signup'
SUBMIT_URL = 'https://www.example.com/signup'
SITEKEY = 'sk_YOUR_SITEKEY'
PROXY_USER = 'user'
PROXY_PASSWORD = 'password'
PROXY_HOST = '203.0.113.10'
PROXY_PORT = 8000
PROXY_URL = f'http://{PROXY_USER}:{PROXY_PASSWORD}@{PROXY_HOST}:{PROXY_PORT}'
DEFAULT_USER_AGENT = (
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
    '(KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36'
)
POLL_SECONDS = 3
DEADLINE_SECONDS = 180  # client-side limit, the API does not set one


def call(path, body):
    res = requests.post(f'{API}{path}', json={'clientKey': API_KEY, **body}, timeout=30)
    res.raise_for_status()  # HTTP 500 has no errorId: a server fault
    data = res.json()
    if data.get('errorId') != 0:
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data


def solve_captchafox():
    created = call('/createTask', {'task': {
        'type': 'CaptchaFoxTask',
        'websiteURL': PAGE_URL,
        'websiteKey': SITEKEY,
        'proxy': PROXY_URL,
        'userAgent': DEFAULT_USER_AGENT,
    }})
    task_id = created['taskId']
    deadline = time.monotonic() + DEADLINE_SECONDS
    while time.monotonic() < deadline:
        time.sleep(POLL_SECONDS)
        result = call('/getTaskResult', {'taskId': task_id})
        if result['status'] == 'ready':
            return result['solution']
    raise TimeoutError(f'task {task_id} still processing after {DEADLINE_SECONDS}s')


solution = solve_captchafox()
user_agent = solution.get('userAgent', DEFAULT_USER_AGENT)

session = requests.Session()
session.proxies = {'http': PROXY_URL, 'https': PROXY_URL}
response = session.post(
    SUBMIT_URL,
    headers={'User-Agent': user_agent},
    data={'email': 'user@example.com', 'cf-captcha-response': solution['token']},
    timeout=30,
)
print(response.status_code)

If the form needs a real browser, launch Chromium through the same proxy. This continues the script above and reuses solve_captchafox:

solve.py
from playwright.sync_api import sync_playwright

SET_TOKEN = """(token) => {
  const form = document.querySelector('form');
  let field = form.querySelector('[name="cf-captcha-response"]');
  if (!field) {
    field = document.createElement('input');
    field.type = 'hidden';
    field.name = 'cf-captcha-response';
    form.appendChild(field);
  }
  field.value = token;
}"""

solution = solve_captchafox()

with sync_playwright() as p:
    browser = p.chromium.launch(proxy={
        'server': f'http://{PROXY_HOST}:{PROXY_PORT}',
        'username': PROXY_USER,
        'password': PROXY_PASSWORD,
    })
    context = browser.new_context(user_agent=solution.get('userAgent', DEFAULT_USER_AGENT))
    page = context.new_page()
    page.goto(PAGE_URL)
    page.evaluate(SET_TOKEN, solution['token'])
    page.click('form [type="submit"]')
    page.wait_for_load_state('networkidle')
    print(page.url)
    browser.close()

Both scripts assume a plain form field. If your target reads the token from a header or JSON body, change only the submit step. The Playwright recipe shows how.

Why a valid token gets rejected

A task that finishes with errorId: 0 and a token that the site refuses is almost always a context mismatch. Work through this list:

  • Different IP at submit time. Route the submission through the task's proxy, for browsers and HTTP clients alike.
  • A proxy that rotates. Use one sticky session that lasts at least five minutes, and one session per solve. A proxy that changes exit IP on every request breaks the match.
  • A user agent that differs from the one in the solution, or from the userAgent you sent.
  • A stale or replayed token. Poll, then submit immediately.
  • The wrong field. If cf-captcha-response does nothing, sniff the real name from a manual solve.
  • A stale sitekey. Read it again from a fresh page load.
  • A different script host. If the page loads the widget from a host other than cdn.captchafox.com, the token may not be accepted, as described earlier.

Pick a proxy in a country that suits the target. The proxies post covers sticky sessions and proxy types, and the user agent and headers post covers the header side.

What it costs

A task is charged when createTask accepts it, and a successful task keeps its charge. A task that finishes as failed automatically returns its original charge once, and a retry is a new task with a new charge, so fix the mismatch before you loop. Prices are on the pricing page; Billing & packages explains the rules, and volume pricing goes through support.

Next steps

Check the widget's script host, grab the sk_ key and pick a sticky proxy session. Then send one CaptchaFoxTask with the parameters listed by the public catalog (GET /v1/task-types) and submit the result through the same proxy. If you also work with Procaptcha, the Prosopo guide follows the same flow without the mandatory proxy.

More articles