VexSolver

Kasada ACTIVE

KPSDK x-kpsdk-* minter — full solve, or a request-based payload transformer.

Overview

Kasada protects sites with an ips.js bytecode runtime (a dispatcher VM). A client runs it, builds a payload, and POSTs it to /tl to mint a cleared x-kpsdk-ct token; every subsequent request to a protected endpoint then carries a fresh x-kpsdk-cd proof-of-work.

VexSolver reimplements that runtime as a pure-Go VM — fully request-based, no browser, no headless Chromium. Every request-side header is reverse-engineered and generated natively, not replayed from a template. You can use it two ways:

  • Full solvePOST /api/solve. We do every network hop and hand back the token bundle. Simplest to integrate.
  • Payload transformerPOST /api/kasada/payload. You own the TLS session, proxy, cookie jar and header order; we only turn the ips.js into a /tl body. This is the Hyper-Solutions-compatible contract.
Which one should I use?
Use the transformer (/api/kasada/payload) for production scraping: the token is minted from your egress IP, so it never has to be replayed from a different IP than it was earned on, and there is no url field for us to misroute. Use full solve (/api/solve) when you just want a token and do not want to make the fetches yourself.

Payload transformer — POST /api/kasada/payload

A drop-in for the Hyper Solutions Kasada endpoint (docs.hypersolutions.co/api-reference/kasada) — an existing Hyper integration repoints by changing only the base URL. You fetch the challenge yourself and send us the ips.js; we return the exact headers and the base64 body to POST to /tl.

Request fields:

  • script — the raw ips.js body you fetched (required)
  • ipsLink — the ips.js URL with x-kpsdk-v and x-kpsdk-im appended as query params (required). Without x-kpsdk-im the payload is not bound to your handshake and /tl rejects it.
  • userAgent — the exact UA your TLS client sends. Desktop and mobile both supported.
  • acceptLanguage — your Accept-Language header
  • ip — your egress IP (informational)
  • ctoptional. Your preflight x-kpsdk-ct (from your own /fp or the block page). Supply it and we return a complete header set you can send verbatim. We will not mint one — it is bound to your session.
  • dvoptional, mobile only. Your x-kpsdk-dv. It is a per-app-build constant; supplied ⇒ echoed back, omitted ⇒ absent. We never fabricate it.
curl -X POST https://api.vexsolver.com/api/kasada/payload \
  -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
    "acceptLanguage": "en-US,en;q=0.9",
    "ip": "203.0.113.7",
    "ct": "0f3HMyTUeHEy8vuVEHmB...",
    "ipsLink": "https://www.twitch.tv/149e9513-.../2d206a39-.../ips.js?x-kpsdk-v=j-1.2.308&x-kpsdk-im=AALP1ZtNVs0v...",
    "script": "<the full ips.js body you fetched>"
  }'

Response — attach headers to your /tl POST and send the base64-decoded payload as the body:

{
  "headers": {
    "x-kpsdk-ct": "0f3HMyTUeHEy8vuVEHmB...",
    "x-kpsdk-im": "AALP1ZtNVs0vAz3hL1IT...",
    "x-kpsdk-v":  "j-1.2.308",
    "x-kpsdk-dt": "13cz64jzaez034dy49aw52cz218y79wb2zdjez8asy11jbx9jiwc1h0",
    "Content-Type": "application/octet-stream"
  },
  "payload": "<base64 — decode to bytes before POSTing to /tl>"
}

Full transformer flow (what your client does around us)

The transformer does exactly one step: ips.js → /tl body. The fetches on either side are yours, through your own proxy and TLS client, so the whole session shares one IP.

  1. Fetch the challenge. GET the protected page (or /fp). The response carries a preflight x-kpsdk-ct header, a KPSDK:MC:<im> marker in the body (that <im> is your x-kpsdk-im), and the ips.js src.
  2. Fetch ips.js from that src, appending ?x-kpsdk-v=…&x-kpsdk-im=<im>. Keep both the URL and the body.
  3. Call /api/kasada/payload with script (the ips.js body), ipsLink (that URL), your ct, userAgent, ip, acceptLanguage.
  4. POST /tl yourself with the returned headers and the base64-decoded payload as the body. The server responds with the cleared x-kpsdk-ct, an x-kpsdk-st, and x-kpsdk-cr: true.
  5. For every protected request, mint a fresh x-kpsdk-cd via /api/kasada/cd (below) and send it alongside the cleared x-kpsdk-ct.

Per-request proof-of-work — POST /api/kasada/cd (FREE)

Kasada requires a fresh x-kpsdk-cd on every request to a protected endpoint — a single cd replayed across requests is a stale proof and starts failing. So this endpoint is a sub-millisecond local computation and is billed at 0 credits: scraping 10k pages calls it 10k times and costs nothing extra.

  • st — the x-kpsdk-st from your /tl response (required)
  • ct — the cleared x-kpsdk-ct from your /tl response (required)
  • domain — the protected host (required)
  • rstoptional but recommended. Your local clock in ms when the /tl response landed. Supplying it makes d the real round-trip; omit it and we model a plausible one. Do not derive it from wall-clock age — st is issued once, cd is per-request.
curl -X POST https://api.vexsolver.com/api/kasada/cd \
  -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "st": 1777215098000,
    "rst": 1777215098310,
    "ct": "0dhS2BUe4yb8whd4APoV...",
    "domain": "www.twitch.tv"
  }'

# response
{ "payload": "{\"workTime\":...,\"id\":\"...\",\"answers\":[15,14],\"d\":310,\"st\":1777215098000}" }

Send payload as the x-kpsdk-cd header on your protected request, next to x-kpsdk-ct.

Headers we generate (all reverse-engineered, pure Go)

  • x-kpsdk-ct — 175-char cleared token, from the VM cipher output
  • x-kpsdk-im — integrity marker binding the payload to your handshake
  • x-kpsdk-v — version (j-1.2.308 default)
  • x-kpsdk-dt — device timing digest: 14 duration fields differenced from a monotone timeline, base32-encoded in random order. Fully reversed — produced, not templated. Returned inside the payload headers.
  • x-kpsdk-cd — per-request proof-of-work (geometric-difficulty nonce search). Minted by /api/kasada/cd.

x-kpsdk-cr, x-kpsdk-st, x-kpsdk-h and friends are responseheaders the server issues back — you read them, you do not generate them.

Full solve — POST /api/solve

Prefer to hand us the whole job? /api/solve does every hop — fetch, VM run, /tl POST, cr=true verify — and returns the token bundle in cookie_map. Pass a page url (we auto-discover the challenge) or a direct pjs_url to skip discovery.

curl -X POST https://api.vexsolver.com/api/solve \
  -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "vendor": "kasada",
    "url": "https://www.twitch.tv",
    "pjs_url": "https://k.twitchcdn.net/149e9513-.../2d206a39-.../p.js"
  }'

Response:

{
  "success": true,
  "vendor": "kasada",
  "source": "go_kasada",
  "target": "https://www.twitch.tv",
  "elapsed_ms": 1820,
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
  "cookie_map": {
    "x-kpsdk-ct": "0dlCkpczkzzZ9bVkjiBiUOZwDDfFm3Cs...",
    "x-kpsdk-cd": "{\"workTime\":...,\"answers\":[15,14],\"d\":310,\"st\":...}",
    "x-kpsdk-v":  "j-1.2.308",
    "x-kpsdk-dt": "13cz64jzaez034dy49aw52cz218y79wb2zdjez8asy11jbx9jiwc1h0",
    "x-kpsdk-im": "AALTLVJfWjWK797ZyO5QFiScq8Sds...",
    "KP_UIDz":     "0dlCkpczkzzZ9bVkjiBiUOZwDDfFm3Cs...",
    "KP_UIDz-ssn": "0dlCkpczkzzZ9bVkjiBiUOZwDDfFm3Cs..."
  }
}

Replay: send your protected request with the same User-Agent, the KP_UIDz cookies, and the x-kpsdk-ct + a fresh x-kpsdk-cd (mint it per-request via /api/kasada/cd).

Vercel BotID is built on Kasada

Vercel BotID's Deep tier IS Kasada KPSDK proxied through a UUID-namespaced path (/<deploy>/<kasada>/p.js). When you hit a BotID-protected site (v0.app, vercel.com, etc.), VexSolver auto-routes to the BotID flow which uses this same Kasada engine plus the x-is-human JWE signature for the Basic tier. See Vercel BotID.

Tested live on
footlocker.com, nike.com, champssports.com, twitch.tv, passport.twitch.tv, arcteryx.com, sso.godaddy.com, sso.secureserver.net, realestate.com.au, kick.com. New domains: pass vendor: "kasada" + pjs_url (full solve), or send us the ips.js via /api/kasada/payload, and we route them immediately.

Typical performance

  • Transformer (/api/kasada/payload): ~1–2 s in-process VM run
  • Per-request cd (/api/kasada/cd): sub-millisecond, free
  • Full solve, warm (/api/solve): ~1–2 s via the native fast-replay path
  • Full solve, cold rotation: falls back to the full goja VM (~30 s). Still succeeds (cr=true); subsequent solves of that rotation are warm.
  • Verified with x-kpsdk-cr: true on every tested deployment

Pricing

/api/kasada/payload and /api/solve: 1 credit per success — failures are refunded automatically. /api/kasada/cd is free, so the correct per-request-PoW usage pattern is never punished.