NSLSolver
API Reference

Errors

Every status code returned by the API, the exact error strings, and what to do about each one.

Errors

All error responses share the same shape:

{ "success": false, "error": "<human-readable message>" }

The HTTP status code is the primary signal — the message is for humans. Branch your retry logic on the status, not the string.

Reference table

StatusWhenRetry?Action
400Validation failedNoFix the offending field before retrying.
401Missing or invalid API keyNoVerify X-API-Key matches an active key.
402Insufficient balanceNoTop up. Reading /balance will show $0 or near it.
403Type/IP/domain not allowedNoUpdate key permissions, allowlist your IP, or stop targeting a banned domain.
429CPM bucket emptyYesWait, then retry. Capacity refills continuously.
503Backend unavailable or failedYesRetry with exponential backoff. Not billed.

400 — Validation

The error string names the field that failed. The most common shapes:

Generic
{ "success": false, "error": "Invalid JSON" }
{ "success": false, "error": "Invalid type. Valid types: turnstile, challenge, kasada, akamai" }
Turnstile
{ "success": false, "error": "site_key is required" }
{ "success": false, "error": "site_key contains invalid characters" }
{ "success": false, "error": "url is required" }
{ "success": false, "error": "url must use http or https" }
{ "success": false, "error": "url must not target internal addresses" }
{ "success": false, "error": "action contains invalid characters" }
{ "success": false, "error": "cdata contains invalid characters" }
Challenge
{ "success": false, "error": "proxy is required" }
{ "success": false, "error": "proxy format must be protocol://[user:pass@]host:port" }
Kasada
{ "success": false, "error": "kasada_config required for kasada type" }
{ "success": false, "error": "kasada_config.p_js_path contains invalid characters" }
{ "success": false, "error": "kasada_config.fp_host contains invalid characters" }
{ "success": false, "error": "kasada_config.tl_host contains invalid characters" }
{ "success": false, "error": "ua_version must be between 80 and 300" }
Akamai
{ "success": false, "error": "user_agent is required for akamai type" }
{ "success": false, "error": "proxy is required" }
{ "success": false, "error": "proxy format must be protocol://[user:pass@]host:port" }

Length and character limits

FieldPattern / rangeMax length
urlhttp/https, public host2048
site_key[a-zA-Z0-9_-]128
action, cdata[a-zA-Z0-9._\-/: ]256 / 512
proxy(http|https|socks4|socks5)://[user:pass@]host:port512
user_agentNo control characters512
kasada_config.p_js_pathStarts with /, no ..512
kasada_config.fp_host, tl_hostHostname ([a-zA-Z0-9.-]), not an internal address256
kasada_config.cd_constantHex string128
Request body total1 MB

401 — Authentication

{ "success": false, "error": "Missing API key. Use X-API-Key header." }
{ "success": false, "error": "Invalid API key" }

See Authentication for the header format.

402 — Insufficient balance

{ "success": false, "error": "Insufficient balance" }

The pre-check failed because the key's balance is below the per-call cost. Top up; failed solves do not consume balance, so a 402 doesn't waste funds.

403 — Access denied

{ "success": false, "error": "IP not whitelisted" }
{ "success": false, "error": "Captcha type 'kasada' is not allowed for your account" }
{ "success": false, "error": "This domain is not allowed" }
{ "success": false, "error": "Access denied" }

Access denied is returned when the originating IP is on the global ban list. Update the IP, request, or permission set as appropriate.

429 — Rate limited

{ "success": false, "error": "Rate limit exceeded: max 600 captchas per minute" }
{ "success": false, "error": "Rate limit exceeded" }

The first form comes from the /solve CPM bucket. The second comes from the /balance per-IP limit (30/min). See Rate limits for sizing strategies.

503 — Backend errors

{ "success": false, "error": "No backend servers available for this captcha type" }
{ "success": false, "error": "Backend error" }
{ "success": false, "error": "Solve failed" }

These are transient — a worker is unavailable or returned an unparseable response. Retry with exponential backoff. The CPM token is refunded automatically.

Retry policy in one sentence

Retry 429 and 503 with exponential backoff. Do not retry 4xx outside of 429 — the request is broken and won't succeed without changes.

Error handling skeleton

Python
import time, requests

def solve(payload, attempts=4):
    for i in range(attempts):
        r = requests.post(
            "https://api.nslsolver.com/solve",
            headers={"X-API-Key": "YOUR_KEY"},
            json=payload,
            timeout=120,
        )
        data = r.json()

        if r.status_code == 200:
            return data
        if r.status_code in (400, 401, 402, 403):
            raise RuntimeError(f"non-retryable ({r.status_code}): {data['error']}")
        if r.status_code in (429, 503):
            time.sleep(min(2 ** i, 8))
            continue
        raise RuntimeError(f"unexpected ({r.status_code}): {data['error']}")

    raise RuntimeError("attempts exhausted")

On this page