> For the complete documentation index, see [llms.txt](https://docs.disasm.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.disasm.dev/kasada/general-information.md).

# Overview

Read this before solving any Kasada challenge - what the service does, what it deliberately does not do, and the tokens you will be handling.

Read this page in full before integrating Kasada. It covers what the API computes, what stays on your side, and the vocabulary the rest of these guides assumes.

{% hint style="success" %}
Base URL: **`https://ksd.antibotapi.com`**
{% endhint %}

Every request requires your API key in the `x-api-key` header - see [Authentication](/getting-started/authentication.md).

## What Kasada is

Kasada protects a site with a JavaScript sensor rather than a cookie challenge. A protected origin serves four endpoints under a per-customer path: `/mfc` for first contact, `/fp` for the challenge page, `/ips.js` for the interrogation script, and `/tl` for the payload that script produces. The script is regenerated per session and heavily obfuscated, and it interrogates the browser it is running in - screen, GPU, audio stack, fonts, locale, timezone - then enciphers the answers into the body of a `POST` to `/tl`. Accepting that body is what mints the session tokens the site's own API then demands.

Those tokens are the `x-kpsdk-*` header family, and every request to a protected endpoint afterwards carries them, plus a small proof of work.

## How our API works

* **No Kasada JavaScript is executed.** We decompile the script you send us, recover its handler, and build the payload directly.
* **It computes; it never connects.** We do not fetch the block page, the script, or anything else, and we never post to the target. You already hold the session - the cookies, the proxy, the tokens - and a solver that fetched its own script would be solving a different session's script than the one your cookies belong to.
* **Synchronous.** A script this process has not seen costs around 200 ms of CPU; the same build again is 11-19 ms, because the recovery is cached per build and only the assembly is paid per request.
* **Self-updating.** Kasada rotates the script build frequently; the solver tracks it with no action on your side.
* **Chrome on Windows only.** Every device the payload draws from is a Windows Chrome capture, so the platform, screen, GPU and font list describe a Windows machine whatever user agent you send. Anything else is refused rather than billed - see [Payload](/kasada/payload.md).

Your HTTP client still has to match the browser you are claiming to be at the TLS and HTTP/2 level - see [TLS & Fingerprinting](/getting-started/tls-fingerprinting.md).

## The token vocabulary

Every one of these travels as an `x-kpsdk-` header. Knowing which are computed and which are echoed is most of the integration.

| Token | Where it comes from                                                                                                                          | What you do with it                                                                                                                                                      |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ct`  | The `KP_UIDz` query parameter on the `ips.js` link. We read it off the link and return it.                                                   | Send it on the `/tl` POST. The `/tl` **response** carries a rotated `ct` - that newer one is what the protected request and the proof of work use.                       |
| `dt`  | Computed by us, from encoder constants recovered out of the exact script build you sent.                                                     | Send it on the `/tl` POST. It is bound to that build, so it cannot be reused across a rotation.                                                                          |
| `v`   | The `x-kpsdk-v` query parameter on the `ips.js` link.                                                                                        | Send it on every request in the flow. It is the SDK version the whole handshake declares.                                                                                |
| `im`  | The `x-kpsdk-im` query parameter on the `ips.js` link, which the challenge page originally posted to `window.parent` as `KPSDK:MC:<im>:...`. | Send it on the `/tl` POST.                                                                                                                                               |
| `r`   | A `/tl` **response** header.                                                                                                                 | Returned empty, because we do not make that request. Do not send it.                                                                                                     |
| `dv`  | Not set by any `ips.js` build we have seen, so there is nothing to recover it from.                                                          | Returned empty. Do not send it.                                                                                                                                          |
| `h`   | An `/mfc` **response** header. We return the literal `01`, which is what a browser sends **on** `/mfc`, before any token exists to echo.     | Not sent on `/tl`. It belongs to the protected request, and there you send the value your own `/mfc` response served - a browser echoes that one unchanged from then on. |
| `fc`  | An `/mfc` **response** header.                                                                                                               | Returned empty. It carries feature flags and a log level and is not an input to anything we compute.                                                                     |
| `cd`  | Computed by us from a separate call - see [Proof of work](/kasada/proof-of-work.md).                                                         | Send it on every request to the site's own protected endpoints. One fresh blob per request.                                                                              |

The empty ones - `r`, `dv` and `fc` - are present in the response rather than absent, so a client iterating the map does not have to special-case their shape. **Do not send a header whose value is empty.** An empty value and an absent field are different things on the wire, and the sensor reads the difference.

{% hint style="warning" %}
`ct` rotates. The token on the `ips.js` link is the one you present to `/tl`; the token in the `/tl` response is the one everything afterwards uses. Presenting a stale `ct` is the most common way a correct payload still fails.
{% endhint %}

## The flow, end to end

1. **Request** a protected page or API and get blocked.
2. **`GET /mfc`** on the sensor host. Keep the `x-kpsdk-h` it responds with.
3. **`GET /fp?x-kpsdk-v=...`** - the challenge page. It returns the first `x-kpsdk-ct` in a header and the interrogation manifest inside a `postMessage('KPSDK:MC:<im>:...')`.
4. **`GET /ips.js?KP_UIDz=<ct>&x-kpsdk-v=<v>&x-kpsdk-im=<im>`** - the interrogation script, keyed to this session.
5. **Solve** - send the script and its link to `POST /tl` and get the headers and payload back.
6. **`POST /tl`** on the sensor host, body `application/octet-stream`. Keep the rotated `x-kpsdk-ct` and the `x-kpsdk-st` from the response.
7. **Solve the proof of work** - send `st`, the rotated `ct` and the site's domain to `POST /cd`.
8. **Request the protected endpoint** carrying `x-kpsdk-ct`, `x-kpsdk-h`, `x-kpsdk-v` and `x-kpsdk-cd`.

Steps 1-4 and 6-8 happen entirely on your side, from your proxy, in your cookie jar. Steps 5 and 7 are the only calls to us, and only step 5 is billed.

{% hint style="info" %}
Steps 2-4 are an ordinary browser handshake and you must perform them yourself - the script served at step 4 is bound to the cookies and tokens the first three established. Handing us a script fetched by anything other than the session you will spend it on produces a payload that is refused.
{% endhint %}

### The script has to be fresh

The script's decode seed is derived from the wall clock in roughly five-hour buckets, and the search window reaches about four days either side. A script captured today solves all day. One from last week fails every solve on the checksum and comes back as a `502`. If a target that has been working starts returning `502`, fetch a new `ips.js` before looking for anything else.

## Billing

**A solve is one `POST /tl`.** That is the billable unit, and it is what the per-solve rate on your plan refers to.

**`POST /cd` is free and unmetered on every plan.** It is a hash search against a salt we already hold, and a caller needs one blob per protected request - billing it would charge several times over for one session. It is still logged, so your proof-of-work volume shows up on the dashboard; it is simply never charged.

Refused requests are not billed. A `400`, `401`, `403` or `502` costs nothing.

## Whitelisting

Kasada targets are approved per host. Until the host you are solving for is on your account's whitelist, `POST /tl` and `POST /cd` answer `403` and the message names the host that was refused:

```json
{
  "success": false,
  "error": {
    "type": "validation",
    "message": "site \"k.example.net\" is not in the whitelist"
  }
}
```

A **site here is a host**, not a registrable domain. For `/tl` the host is read off the `ips_link` you send; for `/cd` it is the `domain` field.

{% hint style="warning" %}
**One property can need two rows.** The host serving `ips.js` and the host the proof-of-work salt is filed under are not always the same. Twitch serves the script from `k.twitchcdn.net`, runs the page on `www.twitch.tv`, and files its salt under the protected API, `gql.twitch.tv` - so an account solving Twitch end to end needs both `k.twitchcdn.net` and `gql.twitch.tv` approved. A whitelist covering only the script host gives you a payload and then a `403` on the proof of work.
{% endhint %}

Request a target through the dashboard, and list both hosts if the script host and the API host differ.

## Next

* [Payload](/kasada/payload.md) - the `POST /tl` guide, start to finish.
* [Proof of work](/kasada/proof-of-work.md) - the `POST /cd` guide.
* [API Reference](/kasada/api-reference.md) - every field and every status code.
