# UNLOCK API documentation

Send a CAPTCHA as a task, then collect the answer. The API uses the same method names, task names and error codes as other solver APIs, so most existing code works after you change the host and the key.

## How a request works

1. **Create a task** (createTask). POST /createTask with your key and a task object. You get a taskId. The price is taken from the balance now.
2. **Check the result** (getTaskResult). Wait about 5 seconds, then POST /getTaskResult every 5 seconds until status is ready.
3. **Use the answer**. Put the token or text from solution into the page form. If the task fails, the charge goes back to the balance.

## Basics

- **Base URL:** https://unlock-api.com
- **API key:** 32 characters, on your account page. Send it as clientKey (JSON) or key (in.php, res.php).
- **Format:** JSON methods: POST with a JSON body. in.php and res.php: GET or POST form fields; add json=1 for JSON replies.
- **Limits:** 1,200 requests per minute per account, result checks included. Wait about 5 seconds after creating a task, then check every 5 seconds.
- **Time limit:** A task not solved within 5 minutes fails with ERROR_CAPTCHA_UNSOLVABLE and its charge returns to the balance.
- **Billing:** The task price is taken when the task is created. Failed tasks are refunded automatically.

## Quick start

Solve a reCAPTCHA v2 with the JSON methods. Replace YOUR_API_KEY with your key.

### Python

```python
import time, requests

API = "https://unlock-api.com"
KEY = "YOUR_API_KEY"

task = requests.post(f"{API}/createTask", json={
    "clientKey": KEY,
    "task": {
      "type": "RecaptchaV2TaskProxyless",
      "websiteURL": "https://example.com/login",
      "websiteKey": "6LcR_RsTAAAAAFJR_5ZdbQd0SMxL7yIDmSXH1-lo"
    }
}).json()
if task["errorId"]:
    raise SystemExit(task["errorCode"])

for _ in range(60):   # 60 x 5 s = the 5 minute task limit
    time.sleep(5)
    r = requests.post(f"{API}/getTaskResult", json={"clientKey": KEY, "taskId": task["taskId"]}).json()
    if r["errorId"]:
        raise SystemExit(f"{r['errorCode']}: {r.get('errorDescription', '')}")   # failed tasks are refunded
    if r["status"] == "ready":
        print(r["solution"]["gRecaptchaResponse"])
        break
else:
    raise SystemExit("timed out")
```

### Node.js

```javascript
const API = "https://unlock-api.com";
const KEY = "YOUR_API_KEY";
const post = (path, body) => fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }).then((r) => r.json());

const task = await post("/createTask", {
  clientKey: KEY,
  task: {
    "type": "RecaptchaV2TaskProxyless",
    "websiteURL": "https://example.com/login",
    "websiteKey": "6LcR_RsTAAAAAFJR_5ZdbQd0SMxL7yIDmSXH1-lo"
  },
});
if (task.errorId) throw new Error(task.errorCode);

let solution = null;
for (let n = 0; n < 60 && !solution; n++) { // 60 x 5 s = the 5 minute task limit
  await new Promise((r) => setTimeout(r, 5000));
  const r = await post("/getTaskResult", { clientKey: KEY, taskId: task.taskId });
  if (r.errorId) throw new Error(r.errorCode + ": " + r.errorDescription); // failed tasks are refunded
  if (r.status === "ready") solution = r.solution;
}
if (!solution) throw new Error("timed out");
console.log(solution.gRecaptchaResponse);
```

### PHP

```php
<?php
$api = "https://unlock-api.com";
$key = "YOUR_API_KEY";
function post($url, $body) {
  $c = curl_init($url);
  curl_setopt_array($c, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode($body)]);
  return json_decode(curl_exec($c), true);
}
$task = post("$api/createTask", ["clientKey" => $key, "task" => json_decode('{"type":"RecaptchaV2TaskProxyless","websiteURL":"https://example.com/login","websiteKey":"6LcR_RsTAAAAAFJR_5ZdbQd0SMxL7yIDmSXH1-lo"}', true)]);
if ($task["errorId"]) exit($task["errorCode"]);
for ($n = 0; $n < 60; $n++) { // 60 x 5 s = the 5 minute task limit
  sleep(5);
  $r = post("$api/getTaskResult", ["clientKey" => $key, "taskId" => $task["taskId"]]);
  if ($r["errorId"]) exit($r["errorCode"] . ": " . $r["errorDescription"]); // failed tasks are refunded
  if ($r["status"] === "ready") break;
}
if ($r["status"] !== "ready") exit("timed out");
print_r($r["solution"]["gRecaptchaResponse"]);
```

### Go

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

const api = "https://unlock-api.com"
const key = "YOUR_API_KEY"

func post(method string, body map[string]any) map[string]any {
	b, _ := json.Marshal(body)
	res, err := http.Post(api+"/"+method, "application/json", bytes.NewReader(b))
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var out map[string]any
	json.NewDecoder(res.Body).Decode(&out)
	return out
}

func main() {
	task := post("createTask", map[string]any{
		"clientKey": key,
		"task": map[string]any{
			"type": "RecaptchaV2TaskProxyless",
			"websiteURL": "https://example.com/login",
			"websiteKey": "6LcR_RsTAAAAAFJR_5ZdbQd0SMxL7yIDmSXH1-lo",
		},
	})
	if task["errorId"].(float64) != 0 {
		panic(task["errorCode"])
	}
	for n := 0; n < 60; n++ { // 60 x 5 s = the 5 minute task limit
		time.Sleep(5 * time.Second)
		r := post("getTaskResult", map[string]any{"clientKey": key, "taskId": task["taskId"]})
		if r["errorId"].(float64) != 0 {
			panic(r["errorCode"]) // failed tasks are refunded
		}
		if r["status"] == "ready" {
			fmt.Println(r["solution"])
			return
		}
	}
	panic("timed out")
}
```

### Java

```java
import java.net.URI;
import java.net.http.*;
import java.util.regex.*;

public class Solve {
  static final String API = "https://unlock-api.com", KEY = "YOUR_API_KEY";
  static final HttpClient HTTP = HttpClient.newHttpClient();

  static String post(String method, String json) throws Exception {
    var req = HttpRequest.newBuilder(URI.create(API + "/" + method))
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(json)).build();
    return HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body();
  }

  public static void main(String[] args) throws Exception {
    String task = "{\"type\":\"RecaptchaV2TaskProxyless\",\"websiteURL\":\"https://example.com/login\",\"websiteKey\":\"6LcR_RsTAAAAAFJR_5ZdbQd0SMxL7yIDmSXH1-lo\"}";
    String created = post("createTask", "{\"clientKey\":\"" + KEY + "\",\"task\":" + task + "}");
    Matcher id = Pattern.compile("\"taskId\":(\\d+)").matcher(created);
    if (!id.find()) throw new RuntimeException(created);
    for (int n = 0; n < 60; n++) { // 60 x 5 s = the 5 minute task limit
      Thread.sleep(5000);
      String r = post("getTaskResult", "{\"clientKey\":\"" + KEY + "\",\"taskId\":" + id.group(1) + "}");
      if (r.contains("\"errorId\":1")) throw new RuntimeException(r); // failed tasks are refunded
      if (r.contains("\"status\":\"ready\"")) { System.out.println(r); return; }
    }
    throw new RuntimeException("timed out");
  }
}
```

### C#

```csharp
using System.Net.Http.Json;
using System.Text.Json;

const string Api = "https://unlock-api.com";
const string Key = "YOUR_API_KEY";
var http = new HttpClient();

async Task<JsonElement> Post(string method, object body) =>
    await (await http.PostAsJsonAsync($"{Api}/{method}", body)).Content.ReadFromJsonAsync<JsonElement>();

var task = await Post("createTask", new {
    clientKey = Key,
    task = JsonSerializer.Deserialize<JsonElement>("""
        {"type":"RecaptchaV2TaskProxyless","websiteURL":"https://example.com/login","websiteKey":"6LcR_RsTAAAAAFJR_5ZdbQd0SMxL7yIDmSXH1-lo"}
        """)
});
if (task.GetProperty("errorId").GetInt32() != 0) throw new Exception(task.GetProperty("errorCode").GetString());

for (var n = 0; n < 60; n++) { // 60 x 5 s = the 5 minute task limit
    await Task.Delay(5000);
    var r = await Post("getTaskResult", new { clientKey = Key, taskId = task.GetProperty("taskId").GetInt64() });
    if (r.GetProperty("errorId").GetInt32() != 0) throw new Exception(r.GetProperty("errorCode").GetString()); // failed tasks are refunded
    if (r.GetProperty("status").GetString() == "ready") { Console.WriteLine(r.GetProperty("solution")); return; }
}
throw new Exception("timed out");
```

### cURL

```bash
curl -s https://unlock-api.com/createTask \
  -H "Content-Type: application/json" \
  -d '{"clientKey":"YOUR_API_KEY","task":{"type":"RecaptchaV2TaskProxyless","websiteURL":"https://example.com/login","websiteKey":"6LcR_RsTAAAAAFJR_5ZdbQd0SMxL7yIDmSXH1-lo"}}'
# {"errorId":0,"taskId":1001}

curl -s https://unlock-api.com/getTaskResult \
  -H "Content-Type: application/json" \
  -d '{"clientKey":"YOUR_API_KEY","taskId":1001}'
# {"errorId":0,"status":"processing"}  then  {"errorId":0,"status":"ready","solution":{...},"cost":"0.001"}
```

## Methods

- [createTask](https://unlock-api.com/docs/create-task.md): Creates a task and returns its id. The price of the task type is taken from the balance when the task is accepted, and returned if the task fails.
- [getTaskResult](https://unlock-api.com/docs/get-task-result.md): Returns the state of a task. While it is being solved, status is processing. When it is done, status is ready and solution holds the answer. A failed task returns errorId 1 with the reason, and its charge is already back on the balance.
- [getBalance](https://unlock-api.com/docs/get-balance.md): Returns the account balance in US dollars.
- [getToken](https://unlock-api.com/docs/get-token.md): Creates a task and waits for the answer in the same request, up to 50 seconds. If the task is not done by then, the reply is status processing with the taskId; keep checking it with getTaskResult. Result callbacks are not sent for getToken tasks.
- [getQueueStats](https://unlock-api.com/docs/get-queue-stats.md): Returns the current queue: tasks waiting, tasks being solved and the average solving time over the last hour. The numbers come from real tasks, not estimates.
- [getSpendingStats](https://unlock-api.com/docs/get-spending-stats.md): Returns your spending for one UTC day, hour by hour: number of tasks and money spent. Failed and refunded tasks are not counted.
- [reportIncorrectImageCaptcha, reportIncorrectRecaptcha, reportIncorrectTokenCaptcha, reportCorrectRecaptcha, reportIncorrect, reportCorrect](https://unlock-api.com/docs/report.md): Report a correct or wrong answer. These calls are accepted so existing libraries keep working; a report does not change the charge.
- [test](https://unlock-api.com/docs/test.md): Echo for debugging. Returns the JSON body it received, without clientKey. No key check, no charge.

## Guides

- [API keys and security](https://unlock-api.com/docs/authentication.md): How to send your key, use more than one key and allow only your servers.
- [Limits, timing and billing](https://unlock-api.com/docs/limits.md): Request limits, how often to check results, the time limit and when you are charged.
- [Error codes](https://unlock-api.com/docs/errors.md): Every error code, what it means, what to do and whether you are charged.
- [Using a proxy](https://unlock-api.com/docs/proxies.md): When to solve through your own proxy and how to send it.
- [Result callbacks](https://unlock-api.com/docs/callbacks.md): Get the result sent to your server instead of checking for it.
- [Form API: in.php and res.php](https://unlock-api.com/docs/form-api.md): The classic in.php and res.php requests, for code written for older solver APIs.

## Task types

| CAPTCHA | Task type | in.php method | Reference |
|---|---|---|---|
| reCAPTCHA v2 | `RecaptchaV2TaskProxyless`, `RecaptchaV2Task` | `userrecaptcha` | [recaptcha-v2.md](https://unlock-api.com/docs/tasks/recaptcha-v2.md) |
| reCAPTCHA v2 Enterprise | `RecaptchaV2EnterpriseTaskProxyless`, `RecaptchaV2EnterpriseTask` | `userrecaptcha, enterprise=1` | [recaptcha-enterprise.md](https://unlock-api.com/docs/tasks/recaptcha-enterprise.md) |
| reCAPTCHA v3 | `RecaptchaV3TaskProxyless`, `RecaptchaV3Task` | `userrecaptcha, version=v3` | [recaptcha-v3.md](https://unlock-api.com/docs/tasks/recaptcha-v3.md) |
| Cloudflare Turnstile | `TurnstileTaskProxyless`, `TurnstileTask` | `turnstile` | [cloudflare-turnstile.md](https://unlock-api.com/docs/tasks/cloudflare-turnstile.md) |
| GeeTest v3 / v4 | `GeeTestTaskProxyless`, `GeeTestTask` | `geetest, geetest_v4` | [geetest.md](https://unlock-api.com/docs/tasks/geetest.md) |
| FunCaptcha (Arkose Labs) | `FunCaptchaTaskProxyless`, `FunCaptchaTask` | `funcaptcha` | [funcaptcha.md](https://unlock-api.com/docs/tasks/funcaptcha.md) |
| Amazon AWS WAF | `AmazonTaskProxyless`, `AmazonTask` | `amazon_waf` | [aws-waf-captcha.md](https://unlock-api.com/docs/tasks/aws-waf-captcha.md) |
| MTCaptcha | `MtCaptchaTaskProxyless`, `MtCaptchaTask` | `mt_captcha` | [mtcaptcha.md](https://unlock-api.com/docs/tasks/mtcaptcha.md) |
| Friendly Captcha | `FriendlyCaptchaTaskProxyless`, `FriendlyCaptchaTask` | `friendly_captcha` | [friendly-captcha.md](https://unlock-api.com/docs/tasks/friendly-captcha.md) |
| Lemin Cropped Captcha | `LeminTaskProxyless`, `LeminTask` | `lemin` | [lemin-captcha.md](https://unlock-api.com/docs/tasks/lemin-captcha.md) |
| DataDome | `DataDomeSliderTask` | `datadome` | [datadome-captcha.md](https://unlock-api.com/docs/tasks/datadome-captcha.md) |
| KeyCaptcha | `KeyCaptchaTaskProxyless`, `KeyCaptchaTask` | `keycaptcha` | [keycaptcha.md](https://unlock-api.com/docs/tasks/keycaptcha.md) |
| Capy Puzzle | `CapyTaskProxyless`, `CapyTask` | `capy` | [capy-puzzle.md](https://unlock-api.com/docs/tasks/capy-puzzle.md) |
| Cloudflare Challenge page | `AntiCloudflareTask` | `cloudflare_challenge` | [cloudflare-challenge.md](https://unlock-api.com/docs/tasks/cloudflare-challenge.md) |
| Cutcaptcha | `CutCaptchaTaskProxyless`, `CutCaptchaTask` | `cutcaptcha` | [cutcaptcha.md](https://unlock-api.com/docs/tasks/cutcaptcha.md) |
| CyberSiARA | `AntiCyberSiAraTaskProxyless`, `AntiCyberSiAraTask` | `cybersiara` | [cybersiara.md](https://unlock-api.com/docs/tasks/cybersiara.md) |
| atbCAPTCHA | `AtbCaptchaTaskProxyless`, `AtbCaptchaTask` | `atb_captcha` | [atbcaptcha.md](https://unlock-api.com/docs/tasks/atbcaptcha.md) |
| Tencent Captcha | `TencentTaskProxyless`, `TencentTask` | `tencent` | [tencent-captcha.md](https://unlock-api.com/docs/tasks/tencent-captcha.md) |
| Prosopo Procaptcha | `ProsopoTaskProxyless`, `ProsopoTask` | `prosopo` | [prosopo.md](https://unlock-api.com/docs/tasks/prosopo.md) |
| CaptchaFox | `CaptchaFoxTaskProxyless`, `CaptchaFoxTask` | `captchafox` | [captchafox.md](https://unlock-api.com/docs/tasks/captchafox.md) |
| Altcha | `AltchaTaskProxyless`, `AltchaTask` | `altcha` | [altcha.md](https://unlock-api.com/docs/tasks/altcha.md) |
| NetEase Yidun | `YidunTaskProxyless`, `YidunTask` | `yidun` | [yidun-captcha.md](https://unlock-api.com/docs/tasks/yidun-captcha.md) |
| Yandex SmartCaptcha | `YandexSmartCaptchaTaskProxyless`, `YandexSmartCaptchaTask` | `yandex` | [yandex-smartcaptcha.md](https://unlock-api.com/docs/tasks/yandex-smartcaptcha.md) |
| VK Captcha | `VKCaptchaTaskProxyless`, `VKCaptchaTask` | `vkcaptcha` | [vk-captcha.md](https://unlock-api.com/docs/tasks/vk-captcha.md) |
| Binance Captcha | `BinanceTaskProxyless`, `BinanceTask` | `binance` | [binance-captcha.md](https://unlock-api.com/docs/tasks/binance-captcha.md) |
| Custom task (Imperva, Temu and others) | `CustomTask` | `incapsula, temuimage, hunt, tspd, basilisk` | [custom-captcha-task.md](https://unlock-api.com/docs/tasks/custom-captcha-task.md) |
| Image to text | `ImageToTextTask` | `post, base64` | [image-captcha.md](https://unlock-api.com/docs/tasks/image-captcha.md) |
| Text question | `TextCaptchaTask` | `textcaptcha` | [text-captcha.md](https://unlock-api.com/docs/tasks/text-captcha.md) |
| Image grid | `GridTask` | `post, recaptcha=1` | [grid-captcha.md](https://unlock-api.com/docs/tasks/grid-captcha.md) |
| Click coordinates | `CoordinatesTask` | `post, coordinatescaptcha=1` | [coordinates-captcha.md](https://unlock-api.com/docs/tasks/coordinates-captcha.md) |
| Rotate | `RotateTask` | `rotatecaptcha` | [rotate-captcha.md](https://unlock-api.com/docs/tasks/rotate-captcha.md) |
| Audio | `AudioTask` | `audio` | [audio-captcha.md](https://unlock-api.com/docs/tasks/audio-captcha.md) |
| Bounding box | `BoundingBoxTask` | `bounding_box` | [bounding-box-captcha.md](https://unlock-api.com/docs/tasks/bounding-box-captcha.md) |
| Draw around | `DrawAroundTask` | `post, canvas=1` | [draw-around-captcha.md](https://unlock-api.com/docs/tasks/draw-around-captcha.md) |
| Drag and drop | `DragAndDropTask` | JSON only | [drag-drop-captcha.md](https://unlock-api.com/docs/tasks/drag-drop-captcha.md) |
| Complex image task | `ComplexImageTask` | JSON only | [complex-image-captcha.md](https://unlock-api.com/docs/tasks/complex-image-captcha.md) |

OpenAPI: https://unlock-api.com/openapi.json
