API reference

Solve a captcha

One authenticated POST returns a token. Authenticate with your API key in the x-api-key header. Only successful solves are billed.

Endpoint

POSThttps://api.kagedcap.io/solve
FieldTypeRequiredDescription
taskstringyesThe solve type — one of the tasks listed below.
urlstringmost tasksThe page URL the captcha appears on. Optional for Kasada. For evaluate, auth.* hosts default to verify_phone; anything else to join_queue.
sitekeystringreCAPTCHAThe reCAPTCHA site key. Not needed for tmpt, evaluate, or Kasada (applied server-side).
actionstringtmpt onlyRequired for tmpt (Event or Login). Optional everywhere else — for reCAPTCHA v3 it binds the token to your site’s action, and omitting it performs a no-action solve; for evaluate it overrides the action derived from the URL. Not used by v2 or Kasada.
proxystringper taskRequired for non-ProxyLess tasks, evaluate, and KasadaLogin; omit for ProxyLess and KasadaReload.
phone_numberstringevaluateverify_phone only. Must include the country prefix (e.g. +12025550123). Goes into context.phoneNumber and sizes keystroke telemetry.
queueIdstringevaluatejoin_queue only. Goes into context.queueId — e.g. "examplequeue01". Omitted when empty.
eventIdstringevaluatejoin_queue only. Goes into context.eventId — e.g. "1A2B3C4D5E6F7A8B".
userAgentstringnoA real desktop UA, used by reCAPTCHA, tmpt and evaluate. The token embeds it, so match the browser you replay with. For evaluate it also selects the device profile — screen, GPU and client hints move with it — so a UA matching your traffic scores better than none. Kasada ignores it and returns its own.
secretKeystringnoYour reCAPTCHA secret, to get a verified score back.
sitestringKasadaKasada site flow to solve (e.g. "ticketmaster", "footlocker"). Defaults server-side.
kpsdk_stnumberKasadaReloadServer timestamp from a prior KasadaLogin — the base for the refreshed x-kpsdk-cd.
hashstringKasadaReloadSession PoW hash (sessionHash) from the prior KasadaLogin — required to compute a valid x-kpsdk-cd.
x_kpsdk_ctstringKasadaReloadSession token from the prior KasadaLogin. Required — its leading chars seed the cd; echoed back unchanged.
x_kpsdk_vstringKasadaReloadSDK version from the prior KasadaLogin, echoed back unchanged.
x_kpsdk_hstringKasadaReloadSignature from the prior KasadaLogin, echoed back unchanged.

Always send a real desktop userAgent — the token embeds it, so it should match the browser your traffic presents. A mismatched or missing UA lowers the score; when omitted, a default is used.

Async solving (reCAPTCHA only)

/solve holds the connection open for the whole solve, so a busy fleet has to refuse work it would otherwise queue. The async endpoint splits that in two: you submit and get an id back immediately, then poll for the result or let a callback bring it to you. Waiting costs you no socket and costs us no capacity, so a burst queues instead of coming back as concurrency_limit_exceeded.

reCAPTCHA onlyA job carries a single token, which is the whole result for a ReCaptcha* task and not for the others — Kasada returns a header set with no token at all, and Evaluate returns a decision alongside it. Send TicketmasterTmptTask, EvaluateTask, KasadaLogin and KasadaReload to /solve. Our SDKs already do this for you.

POSThttps://api.kagedcap.io/v2/solve

The body is the /solve body exactly, plus an optional callback_url. Send an Idempotency-Key header and a resent submit returns the original job instead of buying a second solve — the key is durable and works across shards, so a retry after a network blip is free.

202 Accepted — queued, not finished
{ "success": true, "id": "8f14e45f-ceea-467a-9e4b-2c1d3a5f7b90", "status": "running" }
GEThttps://api.kagedcap.io/v2/solve/{id}

Poll about every 5 seconds. Read status, never successsuccess only turns true once status is done, so treating it as the completion test reports a still-running solve as a failure. running means keep going; done and failed are terminal.

200 response — done
{
  "success": true,
  "id": "8f14e45f-ceea-467a-9e4b-2c1d3a5f7b90",
  "status": "done",
  "token": "03AFcWeA...",
  "solve_ms": 1840,
  "elapsed_ms": 2110,
  "created_at": "2026-09-10T00:00:00Z",
  "completed_at": "2026-09-10T00:00:02Z"
}
200 response — failed
{ "success": false, "id": "8f14e45f-...", "status": "failed", "error": "solver_unavailable" }
FieldTypeDescription
idstringThe job id, echoed back. Scoped to your account — another account’s id answers 404.
statusstringrunning, done, or failed. This is the completion test — not success, which only turns true once status is done.
tokenstring | nullThe solved token. Null while running, and null again once the result has expired.
errorstringOnly on failed — the same error codes the synchronous endpoint returns.
solve_msnumberTime the solver itself took, once the job is done.
elapsed_msnumberWall clock from submit to completion, including any time queued.
created_atstringWhen the job was accepted (ISO 8601).
completed_atstringWhen it finished. Absent while running.
callbackobjectPresent when you supplied a callback_url: { delivered, attempts }.

Callbacks

Pass callback_url and we POST the finished job to it, so you need not poll at all. It must be a public https URL; private and loopback addresses are rejected up front with callback_url_invalid. Every delivery is signed — verify X-Kagedcap-Signature before trusting the body, and compare it in constant time. Delivery is retried with backoff, and polling always remains authoritative: a callback that never gets through can never cost you the token.

Callback request
POST https://your-app.example.com/hooks/kagedcap
X-Kagedcap-Signature: sha256=<hmac of the raw body>
X-Kagedcap-Delivery: 1

{ "id": "8f14e45f-...", "status": "done", "token": "03AFcWeA...", "solve_ms": 1840, "elapsed_ms": 2110 }

Two things to design around. Results expire: the token is cleared about five minutes after completion — a reCAPTCHA token is dead in roughly two anyway — so a late poll returns done with no token rather than a result you can use. Score is v1 only: a job carries no score or verification block, so if you need those, use /solve.

Direct fleet endpoints (advanced)

You never have to think about this. api.kagedcap.io/solve reads the task and forwards it to the right solver fleet for you. If you're latency-sensitive and already know which fleet a request targets, you can skip that routing hop and POST straight to the fleet subdomain — same path, same body, same x-api-key header, identical response.

FleetEndpointTasks
reCAPTCHArecap.kagedcap.io/solveall ReCaptcha* tasks (v2/v3, enterprise, proxyless)
TMPTtmpt.kagedcap.io/solveTicketmasterTmptTask
Evaluateevaluate.kagedcap.io/solveEvaluateTask
Kasadakasada.kagedcap.io/solveKasadaLogin, KasadaReload

Send each task to its own fleet — a task posted to the wrong subdomain is rejected, whereas the default endpoint always routes it correctly. When in doubt, keep using api.kagedcap.io/solve; the routing overhead is a few milliseconds and it never sends a task to the wrong place.

Solve types

The task field selects the type. reCAPTCHA and tmpt return the same envelope — success, token, score, verification — with score populated only for v3. Evaluate adds decision (allow / challenge / block). Kasada returns its own envelope (the x-kpsdk-* headers), shown below.

reCAPTCHA v3

ReCaptchaV3TaskReCaptchaV3TaskProxyLess

Invisible, score-based. Returns a token and a 0.0–1.0 score. Requires a sitekey and an action.

Request body
{
  "task": "ReCaptchaV3Task",
  "url": "https://example.com/login",
  "sitekey": "6Lc…",
  "action": "login",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
  "proxy": "http://user:pass@1.2.3.4:8888"
}
200 response
{
  "success": true,
  "task": "ReCaptchaV3Task",
  "token": "03AFcWeA…",
  "score": 0.9,
  "verification": null
}

The ProxyLess variant (ReCaptchaV3TaskProxyLess) must omit proxy.

reCAPTCHA v3 Enterprise

ReCaptchaV3EnterpriseTaskReCaptchaV3EnterpriseTaskProxyLess

Enterprise sitekeys. Same shape as v3 — a sitekey and an action.

Request body
{
  "task": "ReCaptchaV3EnterpriseTask",
  "url": "https://www.example.com",
  "sitekey": "6Lc…",
  "action": "checkout",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
  "proxy": "http://user:pass@1.2.3.4:8888"
}
200 response
{
  "success": true,
  "task": "ReCaptchaV3EnterpriseTask",
  "token": "03AFcWeA…",
  "score": 0.9,
  "verification": null
}

reCAPTCHA v2 · Invisible

ReCaptchaV2TaskReCaptchaV2TaskProxyLess

Invisible v2. No action and no score — the token is verified pass/fail via Google siteverify, so score is always null.

Request body
{
  "task": "ReCaptchaV2Task",
  "url": "https://example.com/",
  "sitekey": "6Lc…  (a v2 invisible key)",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
  "proxy": "http://user:pass@1.2.3.4:8888"
}
200 response
{
  "success": true,
  "task": "ReCaptchaV2Task",
  "token": "03AGdBq…",
  "score": null,
  "verification": null
}

No action is required. v2 checkbox (which can pop an image challenge) is not supported.

TMPT Solver

TicketmasterTmptTask

Mints a Ticketmaster tmpt cookie. url must be a Ticketmaster host and action must be Event or Login. No sitekey (applied server-side); proxy is optional. Send the userAgent you will replay the cookie with — it is used for the underlying solve, so a mismatch weakens the result. The returned token is the tmpt cookie value.

Request body
{
  "task": "TicketmasterTmptTask",
  "url": "https://www.ticketmaster.com/event/…",
  "action": "Event",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
  "proxy": "http://user:pass@1.2.3.4:8888"
}
200 response
{
  "success": true,
  "task": "TicketmasterTmptTask",
  "token": "eyJhbGciOi…",
  "score": null,
  "verification": null
}

Score verification does not apply, so score and verification are always null.

Evaluate

EvaluateTask

Runs Ticketmaster APS protect() — mints the page reCAPTCHA, posts /epsf/v1/evaluate, and returns the APS decision token. url and proxy are required. No sitekey (applied server-side). userAgent is optional but recommended — it picks the device profile the fingerprint is built from, so send the UA you will replay with. auth.* URLs default to verify_phone; any other Ticketmaster host defaults to join_queue. The returned token is the EPSF allow token (replay it on the next APS step).

Request body
// verify_phone — the default for auth.* hosts
{
  "task": "EvaluateTask",
  "url": "https://auth.ticketmaster.com/as/authorization.oauth2?client_id=…&redirect_uri=https://identity.ticketmaster.com/exchange",
  "proxy": "http://user:pass@1.2.3.4:8888",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
  "phone_number": "+12025550123"
}

// join_queue — the default for any other Ticketmaster host
{
  "task": "EvaluateTask",
  "url": "https://www.ticketmaster.com/event/1A2B3C4D5E6F7A8B",
  "proxy": "http://user:pass@1.2.3.4:8888",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
  "queueId": "examplequeue01",
  "eventId": "1A2B3C4D5E6F7A8B"
}
200 response
{
  "success": true,
  "task": "EvaluateTask",
  "token": "eyJhbGciOi…",
  "decision": "allow"
}

The action is inferred from the URL host — pass action explicitly only to override it. decision is allow, challenge, or block; challenge means APS wants MFA/PoW/Persona next.

Kasada · Login

KasadaLogin

Returns the full Kasada header set (x-kpsdk-ct/cd/v/h) plus the identity headers (user-agent + client hints) to replay, and reload — Kasada's trust verdict (true = high-trust token). Requires a proxy — the token is IP-bound. No sitekey; site defaults server-side. Keep kpsdk_st, hash, and the x_kpsdk_* values to drive a later KasadaReload.

Request body
{
  "task": "KasadaLogin",
  "site": "ticketmaster",
  "proxy": "http://user:pass@1.2.3.4:8888"
}
200 response
{
  "success": true,
  "task": "KasadaLogin",
  "site": "ticketmaster",
  "headers": {
    "user-agent": "Mozilla/5.0 …",
    "sec-ch-ua": "\"Chromium\";v=\"131\", …",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\""
  },
  "x_kpsdk_ct": "…",
  "x_kpsdk_cd": "…",
  "x_kpsdk_v": "…",
  "x_kpsdk_h": "…",
  "kpsdk_st": 1712345678901,
  "hash": "d49a6b7b…",
  "reload": true,
  "user_agent": "Mozilla/5.0 …"
}

Unlike reCAPTCHA/tmpt there is no token — send the returned headers with your request. Reuse the same proxy for the request the token was minted for. Keep hash + kpsdk_st + x_kpsdk_ct to refresh the cd via KasadaReload.

Kasada · Reload

KasadaReload

Refreshes x-kpsdk-cd from a prior KasadaLogin — no proxy, no browser (a few hashes). Send back kpsdk_st, hash, and x_kpsdk_ct (all required) from the login response; the cd is recomputed for that exact token. Use it to keep a session fresh.

Request body
{
  "task": "KasadaReload",
  "kpsdk_st": 1712345678901,
  "hash": "d49a6b7b…",
  "x_kpsdk_ct": "…",
  "x_kpsdk_v": "…",
  "x_kpsdk_h": "…"
}
200 response
{
  "success": true,
  "task": "KasadaReload",
  "x_kpsdk_ct": "…",
  "x_kpsdk_cd": "…",
  "x_kpsdk_v": "…",
  "x_kpsdk_h": "…",
  "kpsdk_st": 1712345678901,
  "hash": "d49a6b7b…"
}

x_kpsdk_ct/v/h and hash are echoed unchanged; only x_kpsdk_cd changes. x_kpsdk_ct must be the token the refreshed cd will ride with — its leading chars are folded into the cd.

Check balance

GEThttps://api.kagedcap.io/v1/balance
200 response
{ "available_micros": "2998000", "display": "$2.998" }

Balance is stored in micros — 1,000,000 micros = $1.00. Only successful solves are billed; a per-solve price is quoted on your dashboard.

Billing & top-ups

Add funds from your dashboard → Top up. Top-ups are a signed-in action, not an API call — there's no endpoint to script with your key.

Card: pay by card through Stripe. Balance is credited the moment the payment confirms — usually instant.
Crypto: pay with BTC, ETH, USDC and more via a hosted checkout. Your balance is credited when the payment is confirmed on-chain (typically a few minutes), never at redirect — so it's safe to close the tab once you've sent.
Promo codes: apply a code at checkout to add a bonus to a qualifying top-up.
Dedicated plans: steady, high-volume traffic can move to a monthly dedicated fleet instead of per-solve billing — see pricing on your dashboard or ask support.

SDKs

Official clients for five languages — each handles auth, task selection, and errors. Pass version: "v2" for reCAPTCHA v2 invisible; enterprise for the Enterprise family; use kasadaLogin / kasadaReload for Kasada (the login session carries its headers into the reload for you).

All repos on GitHub ↗

Every SDK's solve() picks the transport for you: reCAPTCHA goes over the async endpoint and is polled to completion, everything else over /solve. Either way the call blocks and hands you that fleet's full result — you never choose between them.

TypeScriptnpm install @kagedcap/sdk
GitHub ↗
import { KagedCapClient } from '@kagedcap/sdk';
const kc = new KagedCapClient(process.env.KAGEDCAP_API_KEY!);

// v3 (enterprise)
const { token } = await kc.solve({ sitekey, url, action, enterprise: true, proxy });
// v2 invisible — no action
const v2 = await kc.solve({ sitekey, url, version: 'v2', proxy });

// Kasada — the session keeps the headers and resends them on reload
const k = await kc.kasadaLogin({ site: 'ticketmaster', proxy });
const fresh = await kc.kasadaReload(k);
Node.jsnpm install kagedcap
GitHub ↗
const { KagedCapClient } = require('kagedcap');
const kc = new KagedCapClient(process.env.KAGEDCAP_API_KEY);

const { token } = await kc.solve({ sitekey, url, action, enterprise: true, proxy });
const v2 = await kc.solve({ sitekey, url, version: 'v2', proxy });

const k = await kc.kasadaLogin({ site: 'ticketmaster', proxy });
const fresh = await kc.kasadaReload(k); // reuses k's kpsdk_st + x_kpsdk_*
Pythonpip install kagedcap
GitHub ↗
from kagedcap import KagedCapClient
kc = KagedCapClient(api_key)

res = kc.solve(sitekey=…, url=…, action="login", enterprise=True, proxy=proxy)
v2  = kc.solve(sitekey=…, url=…, version="v2", proxy=proxy)

k = kc.kasada_login(site="ticketmaster", proxy=proxy)
fresh = kc.kasada_reload(k)  # resends k's kpsdk_st + x_kpsdk_*
Gogo get github.com/kagedcap/kagedcap-sdk-go/v2
GitHub ↗
kc := kagedcap.New(apiKey)

res, _ := kc.Solve(kagedcap.SolveParams{Sitekey: …, URL: …, Action: "login", Enterprise: true, Proxy: proxy})
v2,  _ := kc.Solve(kagedcap.SolveParams{Sitekey: …, URL: …, Version: "v2", Proxy: proxy})

login, _ := kc.KasadaLogin(kagedcap.KasadaParams{Site: "ticketmaster", Proxy: proxy})
fresh, _ := kc.KasadaReload(login) // reuses login's kpsdk_st + x_kpsdk_*
Rustcargo add kagedcap
GitHub ↗
let kc = KagedCapClient::new(api_key);

let res = kc.solve(SolveParams { sitekey, url, action, enterprise: true, proxy, ..Default::default() })?;
let v2  = kc.solve(SolveParams { sitekey, url, version: Some("v2".into()), proxy, ..Default::default() })?;

let login = kc.kasada_login(KasadaParams { site: Some("ticketmaster".into()), proxy, ..Default::default() })?;
let fresh = kc.kasada_reload(&login)?; // reuses login's kpsdk_st + x_kpsdk_*

Errors

HTTPCodeMeaning
401unauthorizedMissing or invalid API key.
403account_suspendedAccount suspended — contact support.
403host_not_allowedThis key's allowlist doesn't include the page host.
400validation_errorBad request shape (see message).
400proxy_required / proxy_not_allowedProxy present/absent for the wrong task.
402insufficient_fundsTop up your balance.
402key_spend_cap_reachedThis key hit its spend cap.
429concurrency_limit_exceededAt your concurrency limit — extra solves briefly queue; a 429 means the queue filled or the wait timed out. Back off and retry.
429key_frozenKey auto-frozen after a burst of failed solves (usually bad proxies). Wait the cool-off in the message, then retry.
502solve_failedThe solve failed (not billed). Do NOT retry the same request.
503no_capacityNo solver capacity — retry shortly.
503maintenanceSolving is paused for maintenance — retry shortly.
504solve_timeoutThe solve timed out (not billed).

Rate limits & retries

Billing: only a 200 with a token is charged. Failures, timeouts, and rejections are free.
Concurrency: each account has a max number of in-flight solves. Past it, extra solves are briefly queued rather than rejected — you just see a slightly slower response as one of your in-flight solves frees a slot. You only get 429 concurrency_limit_exceeded if the queue is full or the wait times out; then back off and retry.
Frozen keys: a burst of failed solves (usually dead/blocked proxies) can temporarily freeze a key — its requests return 429 key_frozen with a cool-off in the message. Fix the proxies and wait it out; hammering with retries just extends it.
Retry policy: retry 429, 503, and 504 with backoff. Never blindly retry 502 solve_failed — it was a real attempt (often a bad/blocked proxy); fix the input first.
Idempotency: send an Idempotency-Key header to make a retried request safe. Pass x-request-id to correlate logs; it's echoed back on every response.
Check a solve: GET https://api.kagedcap.io/v1/solves/{request_id} returns a past result — useful if your client disconnected after a successful solve.
SupportAPI & SDK docs — KagedCap