Skip to content

API Reference

CaptchaLa provides RESTful API for all server-side integrations.

Base URL

https://apiv1.captcha.la

Authentication

All API requests require authentication headers:

X-App-Key:    YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET

WARNING

X-App-Secret is server-side only. Never expose it to browsers, mobile apps, or public repos.

Server-side Validation API

💡 Use a server SDK to skip the boilerplate. Wraps the endpoint, handles retries, surfaces typed errors:

After user passes the frontend CAPTCHA, your server needs to validate the token returned by SDK. Below are the server-side validation endpoints.

Server-side Token Validation

Validate pass_token from frontend SDK (with pt_ prefix). Requires X-App-Key and X-App-Secret headers.

bash
POST /v1/validate
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
Content-Type: application/json

{ "pass_token": "pt_xxx", "client_ip": "1.2.3.4" }

client_ip is optional but recommended — the end-user's IP from your inbound request. Used for additional risk checks; safe to omit.

json
{
  "code": 0,
  "data": {
    "valid": true,
    "challenge_id": "ch_xxx",
    "action": "login",
    "uid": null,
    "captcha_args": {
      "platform": "web",
      "user_ip": "1.2.3.4",
      "referer": "https://your-site.com/login",
      "pkg": null,
      "solved_at": 1750000000,
      "risk_score": 12
    }
  }
}

TIP

Validate data.valid === true and that data.action matches the scene you expected (reject if a token from a pay flow is presented at /login). Tokens are single-use.

captcha_args

captcha_args echoes the context recorded at solve time — for logging / your own risk scoring:

  • platformweb / android / ios / flutter / windows / …
  • user_ip — the end-user IP seen when the challenge was solved
  • referer — solve page URL (web); null on native
  • pkg — app package / bundle id (native); null on web
  • solved_at — solve completion time (unix seconds)
  • risk_score — solve-time risk score (0-100, higher = riskier)

Token Types

The SDK may hand your frontend different token types depending on service conditions. The prefix tells you everything:

PrefixIssued when/v1/validate returnsRecommended handling
pt_Verification passed normallyvalid: true (single-use)Accept
dg_Plan quota exhausted (degraded mode)valid: false, degraded: true, reasonYour decision — see below
offline_Main API unreachable; issued by the bypass workerVerified against the backup API automatically when using a server SDKAccept after backup verification
co_Main and backup unreachable; generated client-sideCannot be verified server-sideApply your own risk controls (rate limits, etc.)

Degraded Mode (quota exhausted)

When an app has exhausted its monthly quota (and overage billing is not available), the widget does not interrupt your end-user flow. Instead of showing an error, the SDK completes immediately with a dg_ token:

  • The end user sees no error — your form submits normally.
  • Your server's /v1/validate returns:
json
{
  "code": 0,
  "data": {
    "valid": false,
    "degraded": true,
    "reason": "quota_exhausted",
    "expired": false
  }
}

Key properties:

  • valid is always false for degraded tokens — secure by default. If your code only checks valid, nothing changes for you.
  • Whether to let the request through is your decision: accept valid || degraded to keep conversions flowing while you upgrade your plan, or reject and show your own fallback.
  • dg_ tokens are HMAC-signed per app and expire after 5 minutes. Issuing them costs no quota and is never billed.
  • Server SDKs expose this as isDegraded() / getDegradedReason() (PHP) and result.Degraded / result.DegradedReason (Go).

INFO

Expired subscriptions never trigger degraded mode — they automatically fall back to the Free plan and keep serving within the free quota. Degraded mode only covers usage beyond your plan's quota.

Server-issued Challenge Token

Issue a short-lived server_token from your backend. The browser then passes this token when initializing a challenge, proving the request originated from your trusted server.

Issue Server Token

Call this endpoint from your own server only. Requires X-App-Key + X-App-Secret. Response contains a server_token (sct_ prefix) that the frontend forwards to the challenge initialization.

bash
POST /v1/server/challenge/issue
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
Content-Type: application/x-www-form-urlencoded

action=login&ttl=300&max_uses=1&bind_ip=1.2.3.4
json
{
  "code": 0,
  "data": {
    "server_token": "sct_xxxxxxxxxxxx",
    "expires_in": 300,
    "issued_at": 1713600000
  }
}

Body Parameters (form-urlencoded)

FieldDescription
actionBusiness scene, e.g. login, register, pay. Must match the action used in the challenge initialization.
ttlToken lifetime in seconds. Default 300, max 900.
max_usesMaximum times the token may be consumed. Default 10.
bind_ipBind token to a client IP. Initialization from another IP is rejected.
bind_device_idBind token to a specific device id.
bind_fingerprintBind token to a specific browser fingerprint.

Initialize Challenge

Called by the SDK to start a CAPTCHA challenge. Accepts an optional server_token issued by /v1/server/challenge/issue.

FieldDescription
app_keyYour public App Key.
actionBusiness scene. Must match the action used when issuing the server_token.
server_tokenOptional; required when the app has server_token_required = true.

INFO

If server_token_required is enabled for this app in the dashboard, the challenge initialization will reject requests that do not carry a valid server_token.

Error Codes

CodeDescription
invalid_app_keyInvalid App Key
invalid_app_secretInvalid App Secret
challenge_expiredChallenge expired
challenge_not_foundChallenge not found
invalid_answerInvalid answer
token_expiredToken expired
token_already_usedToken already used
token_not_foundToken not found
quota_exceededQuota exceeded
rate_limitedRate limited
rate_limit_exceededToo many issuance requests for this app. Back off and retry.

MIT-licensed examples · CaptchaLa is operated independently