Solve a captcha
One authenticated POST submits the solve and returns a job id; poll it for the result. Authenticate with your API key in the x-api-key header. Only successful solves are billed.
Endpoint
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.
Solving is submit and poll: the POST answers immediately with a job id, then you poll for the result. Waiting costs you no socket and costs us no capacity, so a burst queues instead of coming back as concurrency_limit_exceeded. Every task family goes through this one endpoint — reCAPTCHA, tmpt, Evaluate and Kasada — and a finished job carries that family's full result, exactly as the solve-type cards below show.
The body is the request above. 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.
{ "success": true, "id": "8f14e45f-ceea-467a-9e4b-2c1d3a5f7b90", "status": "running" }Poll about every 5 seconds. Read status, never success — success 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.
{
"success": true,
"id": "8f14e45f-ceea-467a-9e4b-2c1d3a5f7b90",
"status": "done",
"task": "ReCaptchaV3EnterpriseTask",
"token": "03AFcWeA...",
"score": 0.9,
"solve_ms": 1840,
"elapsed_ms": 2110,
"created_at": "2026-09-10T00:00:00Z",
"completed_at": "2026-09-10T00:00:02Z"
}{ "success": true, "status": "done", "task": "EvaluateTask", "token": "<epsf allow token>", "decision": "allow" }{ "success": false, "id": "8f14e45f-...", "status": "failed", "error": "solver_unavailable" }Two things to design around. Results expire: the result is cleared about five minutes after completion — a reCAPTCHA token is dead in roughly two anyway — so a late poll returns done with the result fields gone rather than something you can use; read it promptly. Detect completion by status, not by the token: the token is null while a job is still running, so only status === "done" tells you it is ready.
Direct fleet endpoints (advanced)
You never have to think about this. api.kagedcap.io/v2/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 submit straight to the fleet subdomain — same path, same body, same x-api-key header, identical response. Polling needs no such care: the job store is shared, so GET /v2/solve/{id} answers from any host.
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/v2/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. Each card shows the body you submit and the result a finished job carries once status is done. 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
Invisible, score-based. Returns a token and a 0.0–1.0 score. Requires a sitekey; action is optional and binds the token to your site’s action when supplied.
{
"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"
}{
"success": true,
"task": "ReCaptchaV3Task",
"token": "03AFcWeA…",
"score": 0.9,
"verification": null
}The ProxyLess variant (ReCaptchaV3TaskProxyLess) must omit proxy.
reCAPTCHA v3 Enterprise
Enterprise sitekeys. Same shape as v3 — a sitekey and an action.
{
"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"
}{
"success": true,
"task": "ReCaptchaV3EnterpriseTask",
"token": "03AFcWeA…",
"score": 0.9,
"verification": null
}reCAPTCHA v2 · Checkbox and Invisible
Both v2 widget shapes. No action and no score — a v2 token is pass/fail via Google siteverify, so score is always null. Send "invisible": true for an invisible sitekey.
{
"task": "ReCaptchaV2Task",
"url": "https://example.com/",
"sitekey": "6Lc… (a v2 sitekey)",
"invisible": true,
"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"
}{
"success": true,
"task": "ReCaptchaV2Task",
"token": "03AGdBq…",
"score": null,
"challenged": false,
"verification": null
}No action is required. `invisible` tells us which widget shape the sitekey is: true for invisible, false for checkbox. Omit it and we work it out from the widget, which costs an extra round trip — so send it when you know. Getting it wrong fails fast rather than silently solving the other shape. `challenged` reports whether an image challenge had to be answered; checkbox keys usually present one.
TMPT Solver
Mints a tmpt cookie. url must be an event or login page on the target site 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.
{
"task": "TicketmasterTmptTask",
"url": "https://www.example.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"
}{
"success": true,
"task": "TicketmasterTmptTask",
"token": "eyJhbGciOi…",
"score": null,
"verification": null
}Score verification does not apply, so score and verification are always null.
Evaluate
Runs the APS protect() flow — 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 supported host defaults to join_queue. The returned token is the EPSF allow token (replay it on the next APS step).
// verify_phone — the default for auth.* hosts
{
"task": "EvaluateTask",
"url": "https://auth.example.com/as/authorization.oauth2?client_id=…&redirect_uri=https://identity.example.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 supported host
{
"task": "EvaluateTask",
"url": "https://www.example.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"
}{
"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
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.
{
"task": "KasadaLogin",
"site": "footlocker",
"proxy": "http://user:pass@1.2.3.4:8888"
}{
"success": true,
"task": "KasadaLogin",
"site": "footlocker",
"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
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.
{
"task": "KasadaReload",
"kpsdk_st": 1712345678901,
"hash": "d49a6b7b…",
"x_kpsdk_ct": "…",
"x_kpsdk_v": "…",
"x_kpsdk_h": "…"
}{
"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
{
"amount_micros": "3000000",
"held_micros": "2000",
"available_micros": "2998000",
"display": "$2.998",
"subscriptions": [
{
"id": "7f1c…",
"package": "v3 Enterprise · 50k / week",
"sku": "v3_enterprise",
"period": "week",
"status": "active",
"quota": 50000,
"used": 12345,
"remaining": 37655,
"period_start": "2026-09-21T14:03:11.000Z",
"period_end": "2026-09-28T14:03:11.000Z",
"renews_at": "2026-09-28T14:03:11.000Z",
"expires_at": null
}
]
}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.
subscriptions lists the solve packages on the key you called with — packages belong to one key, not the account — and is empty when it has none. status is active, canceling or past_due. renews_at is when the next period starts and used resets; it is null once canceled, when expires_at says when the package ends instead.
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.
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 method submits to /v2/solve, polls to completion, and hands you that fleet's full result — so a call still blocks until the solve finishes, without holding a connection open for the wait. Low-level submit/poll primitives are there when you want to drive the wait yourself.
npm install @kagedcap/sdkimport { 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: 'footlocker', proxy });
const fresh = await kc.kasadaReload(k);npm install kagedcapconst { 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: 'footlocker', proxy });
const fresh = await kc.kasadaReload(k); // reuses k's kpsdk_st + x_kpsdk_*pip install kagedcapfrom 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="footlocker", proxy=proxy) fresh = kc.kasada_reload(k) # resends k's kpsdk_st + x_kpsdk_*
go get github.com/kagedcap/kagedcap-sdk-go/v2kc := 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: "footlocker", Proxy: proxy})
fresh, _ := kc.KasadaReload(login) // reuses login's kpsdk_st + x_kpsdk_*cargo add kagedcaplet 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("footlocker".into()), proxy, ..Default::default() })?;
let fresh = kc.kasada_reload(&login)?; // reuses login's kpsdk_st + x_kpsdk_*AI agents
Let an AI coding agent wire KagedCap into your project. Drop in the skill below and your agent installs the right SDK for your language, generates a typed wrapper around your KAGEDCAP_API_KEY, and adds a connectivity test — no copy-pasting from these docs.
Download SKILL.md ↓Errors
Errors arrive in two places. Request-time errors — auth, validation, balance, limits — come back as the HTTP status on the POST. Solve outcomes arrive in the poll: a job with status failed and error set to one of the codes below; for those rows the HTTP column shows the class the code belongs to, not a status your poll returns.