SurSolver
Docs menu

Docs / Guides

Quickstart

Send your first captcha task from curl, Playwright, Puppeteer, Selenium or a browser extension — five steps from an API key to a solved captcha.

SurSolver is an HTTP API. You send the page URL and the parameters your code already reads from the page, and you get back the token, cookies or fields the page expects. There is nothing to install — no SDK, no browser extension, no plugin.

Five steps take you from an empty account to a solved captcha. Steps 1, 2, 4 and 5 are the same for everyone; step 3 is the code you paste. Pick the tool you will send with — the link jumps to that tool’s section in step 3:

Step 1: Create an account and an API key

  1. Sign up at sursolver.com. One account holds one prepaid key.
  2. Open Dashboard → API keys and click Create key. Copy the key when it appears: it is shown once and cannot be retrieved later. It looks like ssk_live_…; Change on the same page replaces it with a new one.
  3. Top up the balance from the same dashboard. A task is charged when the API accepts it; Billing & packages explains what that means for failures.

The key travels in the request body as clientKey. The solver endpoints have no auth header, so never put the key in a URL.

Step 2: Know what you are sending

POST /createTask takes one JSON object:

snippet.json
{
  "clientKey": "ssk_live_…",
  "task": {
    "type": "ReCaptchaV2TaskProxyLess",
    "websiteURL": "https://example.com/login",
    "websiteKey": "6Le..."
  }
}
  • type picks the solver; the example above solves reCAPTCHA v2 without a proxy.
  • websiteURL is the page the captcha sits on. websiteKey is the site key you can read from that page’s HTML.
  • Types that must run from your own IP take proxy fields as well — see Proxies.

The public catalog, GET /v1/task-types (no key needed), lists every type, the exact field names it accepts and what its answer carries. The API stores your task object as sent.

Step 3: Send it with your tool

The call is the same everywhere; only the tool that makes it differs. Pick yours from the list at the top of this page — the sections below are in the same order.

REST API

Any language with an HTTP client works. This is the whole call from a shell.

  1. Keep the key in an environment variable so it stays out of your code:

    solve.sh
    export SURSOLVER_KEY="ssk_live_…"
  2. Create the task:

    solve.sh
    curl -fsS -X POST "https://api.sursolver.com/createTask" \
      -H "Content-Type: application/json" \
      -d '{
        "clientKey": "'"$SURSOLVER_KEY"'",
        "task": {
          "type": "ReCaptchaV2TaskProxyLess",
          "websiteURL": "https://example.com/login",
          "websiteKey": "6Le..."
        }
      }'
  3. Read taskId from the reply and continue with step 4. The fields your type takes are in GET /v1/task-types.

Playwright

  1. npm install playwright, then submit the task with Playwright’s own request context — no second HTTP library needed:

    solve.js
    const { request } = require('playwright');
    
    async function createTask() {
      const api = await request.newContext();
      const res = await api.post('https://api.sursolver.com/createTask', {
        data: {
          clientKey: process.env.SURSOLVER_KEY,
          task: {
            type: 'ReCaptchaV2TaskProxyLess',
            websiteURL: 'https://example.com/login',
            websiteKey: '6Le...',
          },
        },
      });
      if (!res.ok()) throw new Error(`HTTP ${res.status()}`); // transport failure
      return res.json(); // { errorId, taskId, status }
    }
  2. Once step 4 hands you the token, put it into the page and submit the form:

    solve.js
    await page.evaluate((token) => {
      document.querySelector('#g-recaptcha-response').value = token;
    }, solution.gRecaptchaResponse);
  3. Got taskId? Go to step 4. The fields your type takes are in GET /v1/task-types.

Puppeteer

  1. Install Puppeteer and send the task with fetch — Puppeteer drives the browser but has no HTTP client of its own:

    solve.js
    async function createTask() {
      const res = await fetch('https://api.sursolver.com/createTask', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          clientKey: process.env.SURSOLVER_KEY,
          task: {
            type: 'ReCaptchaV2TaskProxyLess',
            websiteURL: 'https://example.com/login',
            websiteKey: '6Le...',
          },
        }),
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`); // transport failure
      return res.json(); // { errorId, taskId, status }
    }
  2. After step 4, hand the token to the open page:

    solve.js
    await page.evaluate((token) => {
      document.querySelector('#g-recaptcha-response').value = token;
    }, solution.gRecaptchaResponse);
  3. Head to step 4 with the taskId; the fields your type takes are in GET /v1/task-types.

Selenium

  1. Send the task from the same Python process that drives the browser — Selenium has no HTTP client of its own, so use requests:

    solve.py
    import os
    
    import requests
    
    created = requests.post(
        "https://api.sursolver.com/createTask",
        json={
            "clientKey": os.environ["SURSOLVER_KEY"],
            "task": {
                "type": "ReCaptchaV2TaskProxyLess",
                "websiteURL": "https://example.com/login",
                "websiteKey": "6Le...",
            },
        },
        timeout=30,
    )
    created.raise_for_status()  # a non-200 reply is a transport failure
    task_id = created.json()["taskId"]
  2. Once step 4 returns the token, write it into the page:

    solve.py
    driver.execute_script(
        "document.getElementById('g-recaptcha-response').value = arguments[0];",
        solution["gRecaptchaResponse"],
    )
  3. Continue with step 4; the fields your type takes are in GET /v1/task-types.

Browser extension

SurSolver ships no extension — the extension here is yours, and it calls the same API from its background service worker (Manifest V3).

  1. Ask the service worker for a task when the toolbar button is clicked:

    solve.js
    chrome.action.onClicked.addListener(async (tab) => {
      const { apiKey } = await chrome.storage.local.get('apiKey');
      const res = await fetch('https://api.sursolver.com/createTask', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          clientKey: apiKey,
          task: {
            type: 'ReCaptchaV2TaskProxyLess',
            websiteURL: tab.url,
            websiteKey: '6Le...',
          },
        }),
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`); // transport failure
      const { taskId } = await res.json();
      // Poll getTaskResult (step 4) for that taskId, then inject the token below.
    });

    Reading tab.url needs the "tabs" permission; the injection below needs "scripting" and the key is kept with "storage" — all three go in manifest.json.

  2. After step 4, inject the token into the tab:

    solve.js
    await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: (token) => {
        document.querySelector('#g-recaptcha-response').value = token;
      },
      args: [solution.gRecaptchaResponse],
    });
  3. Continue with step 4. Keep the key in chrome.storage.local, never in a content script, and remember that a key shipped inside a published extension is readable by everyone who installs it. The fields your type takes are in GET /v1/task-types.

Step 4: Poll for the answer

createTask returns a taskId immediately; the solver works in the background. Ask POST /getTaskResult for that id — with the taskId from step 3:

solve.sh
curl -fsS -X POST "https://api.sursolver.com/getTaskResult" \
  -H "Content-Type: application/json" \
  -d '{"clientKey":"'"$SURSOLVER_KEY"'","taskId":"tsk_9c1f4e2a7b0d5e3f81a62c47"}'

While the solver is working, the answer is:

snippet.json
{"errorId":0,"status":"processing"}

Poll every 3 seconds until status is ready; the solution object then carries what the page expects:

snippet.json
{"errorId":0,"status":"ready","solution":{"gRecaptchaResponse":"03AG...","userAgent":"Mozilla/5.0 ..."}}

Polling is not metered by the rate limits. Replies the API reports itself are HTTP 200: check errorId, and on errorId: 1 stop polling and read errorCode and errorDescription. A non-200 status or a body without errorId is a transport failure — see Errors. Results stay readable for 30 minutes from creation; after that the task answers ERROR_TASK_EXPIRED (see Task lifecycle).

Step 5: Use the solution

solution carries the values the page needs: usually the token for the widget’s hidden response field, sometimes cookies for challenge types, plus userAgent when the solver reported one. Send them from the same exit IP and with the same user agent that produced them.

POST /getBalance reports the cash balance of the key’s owner in USD:

snippet.json
{"errorId":0,"balance":9.987}

Complete runnable scripts in curl, Python, Node.js and Playwright are at the bottom of this page, generated from the same source as every task page.

Where to go next

A complete example

A complete, runnable script generated from the same source as the examples in theAPI reference. It runs as-is against https://api.sursolver.comonce your key is in place; change the fields it sends to match your type (GET /v1/task-types lists them).

solve.sh
API="https://api.sursolver.com"
KEY="YOUR_API_KEY"

# 1/2 Create the task. API errors are HTTP 200 with errorId 1; -f also fails on HTTP 5xx.
created=$(curl -fsS -X POST "$API/createTask" \
  -H "Content-Type: application/json" \
  -d '{"clientKey":"'"$KEY"'","task":{"type":"ReCaptchaV3TaskProxyLess","websiteURL":"https://example.com/login","websiteKey":"6Le..."}}') || { echo "createTask: transport failure" >&2; exit 1; }
echo "$created"
task_id=$(echo "$created" | sed -n 's/.*"taskId": *"\([^"]*\)".*/\1/p')
[ -n "$task_id" ] || { echo "createTask: no taskId (errorId 1)" >&2; exit 1; }

# 2/2 Poll getTaskResult until the task is ready (60 tries, 3 s apart).
attempts=60
while [ "$attempts" -gt 0 ]; do
  result=$(curl -fsS -X POST "$API/getTaskResult" \
    -H "Content-Type: application/json" \
    -d '{"clientKey":"'"$KEY"'","taskId":"'"$task_id"'"}') || { echo "getTaskResult: transport failure" >&2; exit 1; }
  if echo "$result" | grep -q '"errorId": *1'; then echo "$result" >&2; exit 1; fi
  if echo "$result" | grep -q '"status": *"ready"'; then echo "$result"; exit 0; fi
  attempts=$((attempts - 1))
  [ "$attempts" -gt 0 ] && sleep 3
done
echo "task $task_id was not ready after 60 polls" >&2
exit 1