Documentation
Aries Solver solves reCAPTCHA v3 and Cloudflare Turnstile, and sends HTTP requests with a real browser's TLS fingerprint. Each is a single HTTP request. There is nothing to poll: the response either carries the result or an error explaining what to change.
POST /getRecaptchaV3 $2.00 per 1,000
Cloudflare Turnstile
POST /solve $0.70 per 1,000
TLS Bypass
POST /tls $0.70 per 1,000
Every request is JSON in and JSON out, and is charged only when it succeeds.
Your key holds one balance, in US dollars, shared by every endpoint. Each successful request deducts that endpoint's own price, listed below, so the same balance can be spent on any mix of them.
| Endpoint | What it does | Price |
|---|---|---|
POST /getRecaptchaV3 | Solves a reCAPTCHA v3 | $2.00 per 1,000 tokens |
POST /solve | Solves a Cloudflare Turnstile widget | $0.70 per 1,000 tokens |
POST /tls | Makes an HTTP request with a browser TLS fingerprint | $0.70 per 1,000 requests |
Base URL
Every endpoint also exists under a /v1 prefix. Both forms behave
identically, so /getRecaptchaV3 and /v1/getRecaptchaV3 are the
same endpoint.
Authentication
Pass your API key with every request. Keys look like
ar_live_ followed by 48 hexadecimal characters.
| Method | Example |
|---|---|
| Header | X-API-Key: ar_live_… |
| Bearer token | Authorization: Bearer ar_live_… |
| Request body | {"clientKey": "ar_live_…"} |
A key spends your balance. Never ship it in a browser bundle or a mobile app. If a key leaks, ask for a replacement on Telegram and the old one is revoked immediately.
Solve a reCAPTCHA v3
reCAPTCHA v3 and Enterprise
Submits a target page and returns a token for it. The call blocks until the solve finishes or the time budget runs out. Each solved token costs $0.002 ($2.00 per 1,000).
{
"url": "https://example.com/login",
"sitekey": "6LfCVLAUAAAAALFwwRnnCJ12",
"proxy": "http://user:pass@host:8080",
"action": "login",
"enterprise": false
}
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Full URL of the page holding the captcha, including the scheme. For example
https://example.com/login. |
sitekey |
string | Yes | The reCAPTCHA v3 site key from the target page. Usually begins with
6L. |
proxy |
string | Yes | Proxy the solve runs through, with scheme and port:
scheme://user:pass@host:port. Supports http,
https, socks4 and socks5. |
action |
string | No | The action name the page expects, such as login or
submit. Send it when you know it, because scores are often tied to it. |
enterprise |
boolean | No | Set to true for reCAPTCHA Enterprise. Leave it out for standard
v3. |
title |
string | No | Page title to present during the solve. Derived from the domain when omitted. |
Proxy format
The scheme and the port are both required. These are valid:
http://user:pass@198.51.100.10:8080
https://user:pass@198.51.100.10:8443
socks5://user:pass@198.51.100.10:1080
http://198.51.100.10:8080
A bare host:port, or a scheme with no port, is rejected with
Invalid proxy format before the solve starts.
Response
A solved request answers 200 with the token and the fingerprint of the browser that produced it.
{
"status": "success",
"data": {
"token": "03AFcWeA5_zX2...",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/145.0.0.0 Safari/537.36",
"sec_ch_ua": "\"Chromium\";v=\"145\", \"Google Chrome\";v=\"145\"",
"sec_ch_ua_platform": "\"Windows\"",
"sec_ch_ua_mobile": "?0",
"accept_lang": "en-US,en;q=0.9"
},
"credits": 998,
"balance_usd": 1.996
}
| Field | Description |
|---|---|
status | success on a solve, error otherwise. |
data.token | The reCAPTCHA v3 token to submit to the target site. |
data.user_agent | User agent of the solving browser. Send this with your request. |
data.sec_ch_ua | Client hint brand list matching the solving browser. |
data.sec_ch_ua_platform | Client hint platform, for example "Windows". |
data.sec_ch_ua_mobile | Client hint mobile flag, ?0 or ?1. |
data.accept_lang | Accept-Language of the solving browser. |
credits | How many more requests of this endpoint your balance covers, after this one was charged. |
balance_usd | Your balance in US dollars after this request was charged. |
Errors use the same envelope with a message instead of data:
{
"status": "error",
"message": "Insufficient credits"
}
Using the token
The token is bound to the browser that produced it. Send the returned fingerprint alongside it or the target site may score the request poorly.
- Set your request's
User-Agenttodata.user_agent. - Send
Sec-CH-UA,Sec-CH-UA-PlatformandSec-CH-UA-Mobilefrom the matching fields. - Set
Accept-Languagetodata.accept_lang. - Use the same proxy you passed to the solve.
Request the solve at the moment you need it rather than building a pool in advance. A token that has expired is rejected by the target site, not by this API.
Code examples
curl -X POST __BASE__/getRecaptchaV3 \ -H "X-API-Key: ar_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/login", "sitekey": "6LfCVLAUAAAAALFwwRnnCJ12", "proxy": "http://user:pass@host:8080", "action": "login" }'import requests API = "__BASE__/getRecaptchaV3" KEY = "ar_live_YOUR_KEY" payload = { "url": "https://example.com/login", "sitekey": "6LfCVLAUAAAAALFwwRnnCJ12", "proxy": "http://user:pass@host:8080", "action": "login", } r = requests.post(API, headers={"X-API-Key": KEY}, json=payload, timeout=180) data = r.json() if data["status"] != "success": raise RuntimeError(f"{r.status_code}: {data['message']}") solution = data["data"] # Reuse the solving browser's fingerprint on the target request. headers = { "User-Agent": solution["user_agent"], "Sec-CH-UA": solution["sec_ch_ua"], "Sec-CH-UA-Platform": solution["sec_ch_ua_platform"], "Sec-CH-UA-Mobile": solution["sec_ch_ua_mobile"], "Accept-Language": solution["accept_lang"], } print(solution["token"], data["credits"])const API = "__BASE__/getRecaptchaV3"; const KEY = "ar_live_YOUR_KEY"; async function solve() { const res = await fetch(API, { method: "POST", headers: { "X-API-Key": KEY, "Content-Type": "application/json" }, body: JSON.stringify({ url: "https://example.com/login", sitekey: "6LfCVLAUAAAAALFwwRnnCJ12", proxy: "http://user:pass@host:8080", action: "login", }), }); const data = await res.json(); if (data.status !== "success") { throw new Error(`${res.status}: ${data.message}`); } return data.data; } const solution = await solve(); // Reuse the solving browser's fingerprint on the target request. const headers = { "User-Agent": solution.user_agent, "Sec-CH-UA": solution.sec_ch_ua, "Sec-CH-UA-Platform": solution.sec_ch_ua_platform, "Sec-CH-UA-Mobile": solution.sec_ch_ua_mobile, "Accept-Language": solution.accept_lang, };package main import ( "bytes" "encoding/json" "fmt" "net/http" "time" ) type solution struct { Token string `json:"token"` UserAgent string `json:"user_agent"` } type reply struct { Status string `json:"status"` Message string `json:"message"` Data solution `json:"data"` Credits int `json:"credits"` } func main() { body, _ := json.Marshal(map[string]any{ "url": "https://example.com/login", "sitekey": "6LfCVLAUAAAAALFwwRnnCJ12", "proxy": "http://user:pass@host:8080", "action": "login", }) req, _ := http.NewRequest("POST", "__BASE__/getRecaptchaV3", bytes.NewReader(body)) req.Header.Set("X-API-Key", "ar_live_YOUR_KEY") req.Header.Set("Content-Type", "application/json") res, err := (&http.Client{Timeout: 180 * time.Second}).Do(req) if err != nil { panic(err) } defer res.Body.Close() var out reply json.NewDecoder(res.Body).Decode(&out) if out.Status != "success" { panic(fmt.Sprintf("%d: %s", res.StatusCode, out.Message)) } fmt.Println(out.Data.Token, out.Credits) }<?php $payload = [ 'url' => 'https://example.com/login', 'sitekey' => '6LfCVLAUAAAAALFwwRnnCJ12', 'proxy' => 'http://user:pass@host:8080', 'action' => 'login', ]; $ch = curl_init('__BASE__/getRecaptchaV3'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 180, CURLOPT_HTTPHEADER => [ 'X-API-Key: ar_live_YOUR_KEY', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode($payload), ]); $data = json_decode(curl_exec($ch), true); curl_close($ch); if ($data['status'] !== 'success') { throw new RuntimeException($data['message']); } $token = $data['data']['token'];
Cloudflare Turnstile API
Solve Turnstile widgets through POST /solve
Solve Cloudflare Turnstile widgets for any website. Provide the page URL and the widget's sitekey, and get back a valid Turnstile token. Each solved token costs $0.0007 ($0.70 per 1,000). Failed solves cost nothing.
Turnstile is solved through POST /solve with
"type": "turnstile".
Endpoint
Authentication
Requires the X-API-Key header with your API key.
Request
Content-Type: application/json
{
"type": "turnstile",
"site_key": "0x4AAAAAAABkMYinukE8nzY",
"url": "https://example.com/login",
"proxy": "http://user:pass@198.51.100.10:8080"
}
Turnstile parameters
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | Always "turnstile". |
site_key |
string | Yes | The widget's data-sitekey, for example
0x4AAAAAAA.... |
url |
string | Yes | The page the widget is embedded in, including the scheme. |
proxy |
string | No | scheme://[user:pass@]host:port, where the scheme is
http, https or socks5. Without one, the
solve runs from our own network. |
action |
string | No | The widget's data-action, if the site sets one. |
cdata |
string | No | The widget's data-cdata, if the site sets one. |
Turnstile takes site_key, with an underscore.
Finding the sitekey
It sits on the widget itself: a div with the class
cf-turnstile, or the options passed to turnstile.render().
Copy data-action and data-cdata from the same place when
they are there.
<div class="cf-turnstile" data-sitekey="0x4AAAAAAABkMYinukE8nzY" data-action="login"></div>
Turnstile response
A solved widget answers 200:
{
"status": "success",
"data": {
"token": "0.r8C5bJ2kX..."
},
"credits": 9.9993
}
A failure answers with the HTTP status from
Turnstile errors and a message in
data.message:
{
"status": "error",
"data": {
"message": "Cloudflare rejected site_key for this url"
},
"credits": 10
}
| Field | Type | Description |
|---|---|---|
data.token | string | The solved Turnstile token. Submit it as cf-turnstile-response. |
data.message | string | On an error, what went wrong. |
credits | number | Your remaining balance, in US dollars. Left out when the key itself is refused. |
When the solve produced them, data also carries user_agent,
sec_ch_ua, sec_ch_ua_platform, sec_ch_ua_mobile
and accept_lang. Send them with your target request when they are there,
through the same proxy, as described in Using the token.
Turnstile examples
import requests API = "__BASE__/solve" KEY = "ar_live_YOUR_KEY" r = requests.post(API, headers={"X-API-Key": KEY}, json={ "type": "turnstile", "site_key": "0x4AAAAAAABkMYinukE8nzY", "url": "https://example.com/login", }, timeout=180) data = r.json() if data["status"] == "success": token = data["data"]["token"] print("token", token[:60], "balance", data["credits"]) else: print(r.status_code, data["data"]["message"])const res = await fetch("__BASE__/solve", { method: "POST", headers: { "X-API-Key": "ar_live_YOUR_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ type: "turnstile", site_key: "0x4AAAAAAABkMYinukE8nzY", url: "https://example.com/login", }), }); const { status, data, credits } = await res.json(); if (status === "success") console.log("token", data.token, "balance", credits); else console.error(res.status, data.message);curl -X POST __BASE__/solve \ -H "X-API-Key: ar_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "turnstile", "site_key": "0x4AAAAAAABkMYinukE8nzY", "url": "https://example.com/login", "proxy": "http://user:pass@198.51.100.10:8080" }'
Turnstile errors
Failed requests are never charged.
| Status | Message | Cause | Retry? |
|---|---|---|---|
| 400 | Missing required fields: url, site_key | url or site_key is missing |
No, fix the request |
| 400 | invalid type, expected turnstile | type is missing or not "turnstile" |
No, fix the request |
| 400 | Invalid URL format | The URL is malformed | No, send a full URL with its scheme |
| 400 | invalid proxy scheme | The proxy is not http, https or socks5 | No, fix the proxy |
| 400 | invalid request: check url, site_key and proxy | A field was rejected | No, fix the request |
| 400 | Cloudflare rejected site_key for this url | The sitekey does not belong to that page | No, check both values |
| 401 | invalid API key | Authentication failed | No, check your API key |
| 402 | insufficient credits | Balance below the Turnstile price | After topping up |
| 403 | this sitekey/action is not supported | This sitekey or action is blocked | No |
| 429 | solver busy, please retry | The solver is at capacity | Yes, after the Retry-After header |
| 429 | queue full, reduce thread count | Too many of your requests at once | Yes, with lower concurrency |
| 500 | failed to get token, please try again | The solve failed, or Cloudflare or the proxy was unreachable | Yes, with backoff |
Turnstile best practices
- Use the exact page URL. Cloudflare checks the sitekey against the page it was issued for, so send the page the widget is on, not the home page.
- Pass
actionandcdatawhenever the widget setsdata-actionordata-cdata. - Solve just in time. A token lasts about five minutes and validates once.
- Do not retry a 400. The same request fails the same way; fix it first.
TLS Bypass
HTTP requests with a real browser's TLS fingerprint
Make HTTP requests with a real browser's TLS fingerprint to get past bot protection. You send the target URL and request details; the request goes out with the fingerprint you choose, and you get back the target's status, headers and body.
Endpoint
Authentication
Requires the X-API-Key header with your API key. The other forms under
Authentication work too.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | The target URL to make the request to: http or
https, query string included, up to 8,192 characters. |
method |
string | Yes | HTTP method: GET, POST, PUT,
PATCH, DELETE, HEAD or
OPTIONS. |
tls_config |
string | No | TLS configuration: CLOUDFLARE, AKAMAI or
CUSTOM (default: CUSTOM). |
headers |
object | No | Custom HTTP headers to include in the request, as pairs of name and string value. Up to 100 headers. |
body |
string | No | Request body for POST/PUT requests, up to 1 MB. Set
its Content-Type in headers. Above 64 KB, send your
key in a header rather than in the body. |
timeout |
number | No | Request timeout in seconds, from 1 to 120 (default: 30). |
TLS configuration options
| Value | Use it for |
|---|---|
CLOUDFLARE | Mimics a TLS fingerprint that Cloudflare accepts. Best for sites behind Cloudflare protection. |
AKAMAI | Mimics a TLS fingerprint tuned for Akamai. Use it for endpoints behind Akamai. |
CUSTOM | Our own general browser fingerprint. Works for most other sites. |
Example requests
Basic GET request
curl -X POST "__BASE__/tls" \
-H "X-API-Key: ar_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/data",
"method": "GET",
"tls_config": "CUSTOM"
}'
POST request with headers
curl -X POST "__BASE__/tls" \
-H "X-API-Key: ar_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/submit",
"method": "POST",
"tls_config": "CLOUDFLARE",
"headers": {
"Content-Type": "application/json",
"User-Agent": "Custom-Agent/1.0"
},
"body": "{\"data\": \"value\"}"
}'
With Cloudflare protection
curl -X POST "__BASE__/tls" \
-H "X-API-Key: ar_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://protected-site.com/api",
"method": "GET",
"tls_config": "CLOUDFLARE"
}'
Response format
A completed request answers 200:
{
"status": true,
"msg": "Success",
"data": {
"response": {
"status": true,
"status_code": 200,
"text": "Response body content",
"headers": {
"content-type": "application/json",
"server": "nginx"
},
"tls": {
"ja3": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
"ja3_hash": "e7d705a3286e19ea42f587b344ee6865",
"akamai": "1:65536,2:0,3:1000,4:6291456,6:262144|15663105|0|m,a,s,p",
"akamai_hash": "90224459f8bf70b7d0a8797eb916dbc9"
}
}
}
}
A failure answers with the HTTP status listed under Error codes:
{
"status": false,
"msg": "Target timed out",
"error": "TIMEOUT"
}
| Field | Description |
|---|---|
status | true when the request was made, false on an error. |
msg | Success, or what went wrong. |
error | On an error, its code. Branch on this, not on msg. |
data.response.status_code | The HTTP status the target answered with. |
data.response.text | The target's response body as text. Parse it yourself when it is JSON. |
data.response.headers | The target's response headers. A header sent more than once, such as set-cookie, is a list. |
data.response.tls | The fingerprint the request presented. See TLS fingerprint details. |
"status": true means the request was made and the target answered. Read
data.response.status_code to see what it answered: a
403 from the target still arrives inside a
200 from us.
Error codes
| Error code | Status | msg | Description |
|---|---|---|---|
INVALID_URL |
400 | Invalid URL format | The provided URL is malformed or invalid |
TIMEOUT |
504 | Target timed out | Request exceeded the timeout limit |
CONNECTION_ERROR |
502 | Target connection failed | Failed to establish connection to target |
TLS_ERROR |
502 | Target TLS handshake failed | TLS handshake failed. Try another tls_config |
RATE_LIMIT |
429 | Service Overloaded | Too many requests, please slow down. Wait for Retry-After |
INSUFFICIENT_CREDITS |
402 | Insufficient credits | Not enough balance in your account |
INVALID_API_KEY |
401 | Invalid API key | The API key is missing or wrong |
INVALID_REQUEST |
400 | Missing required fields | url is missing, or the body is not valid JSON |
| 400 | Invalid method | method is not one of the supported verbs |
|
| 400 | Invalid tls_config | tls_config is not CLOUDFLARE, AKAMAI or CUSTOM |
|
| 400 | Invalid headers | Not an object of strings, over 100 headers, or a value with a line break | |
| 400 | Invalid body | body is not text or JSON, or is over 1 MB |
|
| 400 | Invalid timeout | timeout is not a number from 1 to 120 |
|
| 413 | Request body too large | The JSON you sent is over the size limit | |
RESPONSE_TOO_LARGE |
502 | Response too large | The target sent back more than 16 MB |
SERVICE_UNAVAILABLE |
503 | Service temporarily at capacity | At capacity or in maintenance. Retry shortly |
REQUEST_FAILED |
500 | Request failed | The request could not be completed. Retry with backoff |
TLS fingerprint details
Every successful response includes the fingerprint the request presented, in
data.response.tls:
- JA3 hash (
ja3_hash): the client TLS fingerprint identifier. - Akamai hash (
akamai_hash): the Akamai style HTTP/2 fingerprint. - Full JA3 string (
ja3): the complete TLS parameters used.akamaiis the full HTTP/2 string.
Use them to verify which TLS configuration was used and to track fingerprint consistency.
Pricing
- $0.0007 per request ($0.70 per 1,000)
- Billed per successful request
- Failed requests are not charged
A request succeeds once the target returns any HTTP response, including a 4xx or 5xx, because the request was made. A timeout, a refused connection or a failed handshake costs nothing.
Best practices
- Choose the right config. Use
CLOUDFLAREfor sites behind Cloudflare andAKAMAIfor Akamai protection. - Handle timeouts. Set
timeoutto suit how long the target usually takes, and give your own HTTP client that long plus 30 seconds. - Retry logic. Use exponential backoff for transient errors
(
RATE_LIMIT,SERVICE_UNAVAILABLE,REQUEST_FAILED). APOST,PUT,PATCHorDELETEis never sent twice on your behalf, so retry one only when repeating it is safe. - Header matching. Include the headers the target site expects,
such as a
User-Agentthat fits the chosen config.
Code examples
import requests API_KEY = "ar_live_YOUR_KEY" headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"} payload = { "url": "https://httpbin.org/get", "method": "GET", "tls_config": "CUSTOM" } response = requests.post( "__BASE__/tls", headers=headers, json=payload, timeout=60 ) data = response.json() if data.get("status"): print("Success!") print("Response:", data["data"]["response"]["text"]) print("JA3 Hash:", data["data"]["response"]["tls"]["ja3_hash"]) else: print("Error:", data.get("error"), data.get("msg"))const API_KEY = "ar_live_YOUR_KEY"; const payload = { url: "https://httpbin.org/get", method: "GET", tls_config: "CUSTOM", }; const res = await fetch("__BASE__/tls", { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify(payload), }); const data = await res.json(); if (data.status) { console.log("Success!"); console.log("Response:", data.data.response.text); console.log("JA3 Hash:", data.data.response.tls.ja3_hash); } else { console.log("Error:", data.error, data.msg); }
Check your balance
Returns your balance in dollars and what it still covers on each endpoint.
POST works too. This call is free and does not count against your rate
limit budget in any meaningful way. credits is the number of reCAPTCHA v3
solves left, as it always was; remaining gives the same count for every
endpoint.
curl __BASE__/balance -H "X-API-Key: ar_live_YOUR_KEY"
{
"status": "success",
"credits": 998,
"balance_usd": 1.996,
"remaining": {
"recaptcha_v3": 998,
"turnstile": 2851,
"tls": 2851
}
}
Your dashboard
Sign in at /dashboard with any of your API keys to see your account in one place: your balance and what it covers on each product, usage over the last 24 hours and 30 days, requests per day, your keys, your limits, your recent requests with the result each one returned, and your top ups.
- The dashboard is read only. Nothing there changes your account or spends balance.
- Target sites are masked, for example
https://ex***.com/***: the path, query and most of the host are never sent to your browser, so a token in a URL cannot leak from a shared screen. - For TLS Bypass, each request shows the status the target answered, such as
Target 403, next to our own result. - Your key is exchanged once for a session that lasts up to 12 hours, or 2 hours without activity. The key itself is not stored in your browser.
- The session belongs to the key you signed in with: if that key is replaced or switched off, the session ends at once.
- Signing in is not an API request and is never charged.
Service status
No authentication required. Returns "maintenance" as the status while the
service is paused. Requests then answer 503
(SERVICE_UNAVAILABLE on /tls), and Turnstile answers
429 solver busy, please retry.
{
"status": "success",
"service": "Aries Solver"
}
Common errors
A reCAPTCHA v3 failure returns {"status": "error", "message": "…"}
with one of the messages below. The HTTP status and the message together tell you
whether to fix the request, top up, or retry. Turnstile and TLS have their own formats,
listed under Turnstile errors and
TLS error codes.
| Status | Message | Cause | Solution |
|---|---|---|---|
| 400 | Missing required fields | A required field such as url is empty |
Provide all required fields |
| 400 | Invalid URL format | Malformed URL | Use a full URL with scheme |
| 400 | Invalid sitekey format | Site key is not a plausible key | Copy the key from the target page |
| 400 | Missing required field: proxy | No proxy provided | Provide a valid proxy |
| 400 | Invalid proxy format | Malformed proxy URL | Check the proxy format |
| 400 | Bad proxy | Proxy not working | Use a different proxy |
| 400 | Invalid action | reCAPTCHA action is longer than 64 characters |
Send the action the page uses |
| 401 | Invalid API key | Authentication failed | Check your API key |
| 402 | Insufficient credits | Balance below this endpoint's price | Top up |
| 413 | Request body too large | The JSON you sent is over the size limit | Send a smaller request |
| 429 | Service Overloaded | Concurrency or rate limit hit | Retry after a moment |
| 500 | Error fetching token | A captcha solve failed | Retry with backoff |
| 503 | Service temporarily at capacity | All solvers busy, or maintenance | Retry shortly |
Retry 429, 500 and
503 with exponential backoff. Every
400, 401 and
403 will fail again unchanged, so fix the request instead.
402 means top up. A 502 or
504 from /tls describes the target, so
retry only if repeating that request is safe.
Rate limits
Each account has a ceiling on requests per minute and a cap on requests running at once, shared across every endpoint. Both are set per account and can be raised: ask on Telegram if you are hitting them.
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per minute. |
X-RateLimit-Remaining | Requests left in the current minute. |
X-RateLimit-Reset | Seconds until the window resets. |
Retry-After | Sent with 429. Wait this many seconds. |
Exceeding either limit returns 429 with
Service Overloaded (error code RATE_LIMIT on
/tls), or on /solve with
solver busy, please retry for the limit per minute and
queue full, reduce thread count for the cap on requests at once. Honour
Retry-After rather than retrying immediately.
Best practices
- Solve just in time. Tokens last about two minutes, so request one when you are ready to use it.
- Send the fingerprint back. The user agent, client hints and language in the response are part of the solve.
- Reuse the same proxy. Solve and submit from the same exit address.
- Pass the right action. Scores often depend on it matching the page.
- Back off on 429 and 5xx. Double the wait each attempt rather than retrying in a tight loop.
- Watch your balance. Read
balance_usdfrom each reCAPTCHA response andcreditsfrom each Turnstile response instead of polling/balance. - Set a generous client timeout. Allow at least 180 seconds; a solve can
take a while under load. For
/tls, allow yourtimeoutplus 30 seconds. - Know what gets resent. A
/tlsrequest withPOST,PUT,PATCHorDELETEis never sent twice on your behalf.GET,HEADandOPTIONSmay be retried automatically after a failure on our side.
Topping up
You top up one balance in dollars, and it never expires. It is spent at each endpoint's own price: reCAPTCHA v3 at $2.00 per 1,000 solves, Turnstile at $0.70 per 1,000 solves and TLS at $0.70 per 1,000 requests. Keys are issued by hand, so message us with the volume you expect and one will be created for you.
https://t.me/Anxioussoul