API — Captcha solving
A separate listener speaking the anti-captcha.com protocol: point third-party software at it and solve images, access cookies, RCv2, CFt, GT and AW through your own proxies.
What this is
BlankTrail Proxy can accept captcha-solving tasks over the anti-captcha.com protocol. It is a separate listener on its own port: software that already speaks that protocol only needs to be pointed at your address instead of the solving service's.
The listener is separate rather than a set of routes on the dashboard port, for two reasons. Official protocol clients hit the host root, so they take the root paths entirely. And the captcha API may well be needed on the LAN where the dashboard must not be visible to anyone.
Turning it on
Three fields in the captcha_api section of the configuration file. The switch and the address can also be changed in the dashboard: Settings → the Captcha API card.
captcha_api:
enabled: true
addr: ":8892"
allow_lan: false
max_concurrent: 10
task_ttl: "5m"
port_idle_timeout: "10m"
| Field | What it does |
|---|---|
| enabled | Whether to start the listener. Off by default. |
| addr | Listener address. An empty host means loopback until LAN access is allowed. |
| allow_lan | Accept requests from other machines even while the general Allow LAN is off. The captcha API follows that general dashboard setting, and this field opens it on top of it — separately from the proxy ports. On in the Docker image. |
| max_concurrent | Cap on concurrent tasks. Zero means no cap at all. |
| task_ttl | How long a finished task's answer is kept. |
| port_idle_timeout | How long an idle port opened for a task's proxy is kept before closing. |
In a container
Everything but the switch is already set up in the image: port 8892 is declared, the compose files shipped with it publish the port on 127.0.0.1, and the image configuration lets the listener bind beyond the loopback. All that is left is turning it on — with a variable or right in the dashboard. The override is one-way: variables turn things on and set values, but cannot switch off what the configuration has enabled.
docker run -d \
-e BT_CAPTCHA_API_ENABLED=1 \
-p 127.0.0.1:8892:8892 \
... blanktrail-proxy
Key and access
clientKey is your dashboard's API key — the very same one. There is no separate key for the captcha API: a second secret with its own storage and rotation is not worth introducing for a single surface.
- The key travels in the request BODY, as the protocol requires, not in a header.
- Key guessing is rate-limited: after a run of failures an address is refused regardless of whether the next key is correct.
- Requests from other machines are refused until LAN access is allowed — by the general Allow LAN setting or by the allow_lan field — including getQueueStats and test.
Supported task types
| Type | What it does |
|---|---|
| ImageToTextTask | Reads the text on an image with a local model. |
| AntiGateTask | Passes the site's protection and returns the access cookies. |
| RecaptchaV2Task | Solves RCv2 from the page address and site key, through your proxy. |
| RecaptchaV2TaskProxyless | The same, but without your proxy — a direct exit. |
| TurnstileTask | Gets a CFt token from the page address and site key, through your proxy. |
| TurnstileTaskProxyless | The same, without your proxy. |
| GeeTestTask | Solves GTv3 or GTv4 from the site's parameters, through your proxy. |
| GeeTestTaskProxyless | The same, without your proxy. |
| AmazonTask | Passes an AW wall and returns its token, through your proxy. |
| AmazonTaskProxyless | The same, without your proxy. |
Other protocol types answer ERROR_TASK_NOT_SUPPORTED. That is a normal answer, not a failure: the client sees it right at task creation and can go to another service without waiting.
The Proxyless variants solve from the installation's own exit — the direct address of the machine BlankTrail Proxy runs on. If proxy fields are sent, such tasks do not use them.
How it works
- Create a task with createTask and get a numeric taskId.
- Poll getTaskResult while the answer says processing.
- Receive ready and the solution object — its contents depend on the task type.
Exit and profile
Every task except ImageToTextTask is solved by the solver's browser, and it goes through a product port opened for the task's proxy. That is the same path as automatic solving on a proxy port. The browser's requests leave from your proxy's exit (for the Proxyless variants, from the installation's exit), with the TLS and HTTP/2 fingerprints and the user-agent of that port's profile. The token or cookies are obtained under the very identity the port presents to the site.
The port is opened for a proxy-plus-profile pair and lives until the idle time set by port_idle_timeout. Later tasks with the same proxy and the same profile go through the same port.
How the profile is chosen
The userAgent you send does not go on the wire. It SELECTS the nearest profile by browser family, major version and operating system, and what goes on the wire is that profile's user-agent — together with the TLS and HTTP/2 fingerprints that match it.
If userAgent is not sent or not recognised (Opera, Yandex Browser, Safari without a Version/ token), the port gets Chrome for Windows at the newest version in the profile database — the same as a port with a Chrome profile and no version set. AntiGateTask and AmazonTask have no userAgent field and always use this profile.
Each such task is an attempt on the Challenge Breaker card of the dashboard, alongside the solves on proxy ports.
Reading an image
The image is passed as base64. The answer carries the recognised text in the text field.
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "ImageToTextTask",
"body": "iVBORw0KGgoAAAANSUhEUg..."
}
}
Access cookies
AntiGateTask passes the site's protection and returns the cookies. Alongside them the answer carries the address of the port through which those cookies must be presented.
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "AntiGateTask",
"websiteURL": "https://example.com/",
"proxyType": "http",
"proxyAddress": "203.0.113.7",
"proxyPort": 8080,
"proxyLogin": "user",
"proxyPassword": "pass"
}
}
RCv2
The task needs the page address and the site's public key. The variant without your proxy is called RecaptchaV2TaskProxyless and requires no proxy fields.
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "RecaptchaV2Task",
"websiteURL": "https://example.com/login",
"websiteKey": "6Lc_aCMTAAAAA...",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"proxyType": "http",
"proxyAddress": "203.0.113.7",
"proxyPort": 8080
}
}
The answer is gRecaptchaResponse and userAgent (see the getTaskResult example below).
CFt
The task needs the page address and the site key (websiteKey, a string like 0x4AAAA…). The optional action, cData and chlPageData are passed to the widget as is. The variant without your proxy is TurnstileTaskProxyless.
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "TurnstileTaskProxyless",
"websiteURL": "https://example.com/login",
"websiteKey": "0x4AAAAAAA...",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
}
}
The answer is token and userAgent. The token goes into the form's cf-turnstile-response field or to the widget's callback; send the request carrying it with the userAgent from the answer.
{
"errorId": 0,
"status": "ready",
"solution": {
"token": "0.Zk3bE8...",
"userAgent": "Mozilla/5.0 ..."
}
}
GT
GTv3 and GTv4 are supported; the version field sets the version (3 or 4, 3 by default). The variant without your proxy is GeeTestTaskProxyless.
- v3: needs the site's gt and challenge. The challenge is single-use — get it from the site right before creating the task. The optional geetestApiServerSubdomain sets the API server if the site does not use GT's default.
- v4: needs the captchaId — in the gt field or in initParameters.captcha_id. The other initParameters are passed to initGeetest4 as is.
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "GeeTestTaskProxyless",
"websiteURL": "https://example.com/login",
"gt": "0f1e2d3c4b5a69788796a5b4c3d2e1f0",
"version": 4,
"initParameters": {"riskType": "slide"},
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
}
}
The v3 answer is challenge, validate and seccode; the v4 answer is captcha_id, lot_number, pass_token, gen_time and captcha_output. userAgent comes with them. Your request sends these fields to the site, and the site verifies them with GT.
{
"errorId": 0,
"status": "ready",
"solution": {
"captcha_id": "0f1e2d3c4b5a69788796a5b4c3d2e1f0",
"lot_number": "7c1b2a...",
"pass_token": "a8d9...",
"gen_time": "1790770800",
"captcha_output": "Qx3k...",
"userAgent": "Mozilla/5.0 ..."
}
}
AW
The task needs the address of a page where the site shows an AW wall. The websiteKey, iv, context, challengeScript and captchaScript fields are accepted for compatibility but not needed: the solver takes all of that from the page itself. The variant without your proxy is AmazonTaskProxyless.
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "AmazonTask",
"websiteURL": "https://example.com/",
"proxyType": "http",
"proxyAddress": "203.0.113.7",
"proxyPort": 8080,
"proxyLogin": "user",
"proxyPassword": "pass"
}
}
The answer is token and userAgent. token is the value of the aws-waf-token cookie: set it in your request under that name and send the request with the userAgent from the answer. The ip field of the getTaskResult answer is the exit address the token was obtained from.
{
"errorId": 0,
"status": "ready",
"solution": {
"token": "a1b2c3d4-...:AAABk...",
"userAgent": "Mozilla/5.0 ..."
},
"ip": "203.0.113.7"
}
Methods
/createTaskclientKey requiredCreates a task and returns a numeric taskId.
{
"errorId": 0,
"taskId": 12
}/getTaskResultclientKey requiredReturns the task's state, and the solution object once it is ready.
{
"clientKey": "YOUR_API_KEY",
"taskId": 12
}{
"errorId": 0,
"status": "ready",
"solution": {
"gRecaptchaResponse": "03AGdBq26...",
"userAgent": "Mozilla/5.0 ..."
}
}/getBalanceclientKey requiredReturns the balance. The product has no billing, so the value is constant; zero means the licence is inactive.
/getQueueStatsno key requiredLoad: how many tasks are running and how busy the solver pool is.
/reportIncorrectImageCaptchaclientKey requiredAccepted for compatibility: the product cannot learn from reports.
/reportIncorrectRecaptchaclientKey requiredThe same for RC.
/reportCorrectRecaptchaclientKey requiredThe same, for confirming a correct solution.
/testno key requiredReturns the parsed request body. Useful when a third-party client sends the wrong thing.
Error codes
The codes and their names are the protocol's: client libraries compare the errorCode string itself. Below are the ones this service returns.
| errorId | errorCode | When |
|---|---|---|
| 1 | ERROR_KEY_DOES_NOT_EXIST | The key is wrong, or the address is temporarily blocked for guessing. |
| 2 | ERROR_NO_SLOT_AVAILABLE | No free solvers right now, or the max_concurrent cap is reached. |
| 10 | ERROR_ZERO_BALANCE | The licence is inactive: renew it, tasks are not accepted. |
| 11 | ERROR_IP_NOT_ALLOWED | The request came from another machine while LAN access is not allowed — neither by the general Allow LAN nor by allow_lan. |
| 12 | ERROR_CAPTCHA_UNSOLVABLE | Solving failed. You may retry, but the profile stays the same until the port closes on idle; if the failure repeats, check the page address and the site parameters: the key, gt, challenge. |
| 16 | ERROR_NO_SUCH_CAPCHA_ID | No task with that taskId: it finished and was dropped once task_ttl elapsed, or it belongs to another key. |
| 23 | ERROR_TASK_NOT_SUPPORTED | The task type is not supported, the required model is not installed, or the task is of the wrong kind — for example an AWS captcha inside a form (wafType: widget). |
| 25 | ERROR_PROXY_CONNECT_REFUSED | Your proxy refused the connection. Fix the proxy; retrying the task is pointless. |
| 26 | ERROR_PROXY_CONNECT_TIMEOUT | Your proxy could not be reached within the allotted time. |
| 27 | ERROR_PROXY_READ_TIMEOUT | Your proxy accepted the connection and went silent. |
| 49 | ERROR_PROXY_NOT_AUTHORISED | Your proxy rejected the login or the password. |
Limits
- Tasks live in memory and do not survive a restart: afterwards the old taskId answers ERROR_NO_SUCH_CAPCHA_ID.
- A task that does not finish within its budget is declared failed and frees its place in the cap.
- There is no callback: the callbackUrl field is accepted and ignored, the result is fetched by polling.
- Tasks for the same site through the same proxy run one after another — a property of the anti-replay design, not a configuration cap.