Skip to main content
News/General

Stop Uploading Challenge Scripts: scriptHash, and Kasada x-kpsdk-v from p.js

Upgrade the SDK and your DataDome, Incapsula and Akamai requests send a 64 character hash instead of a 600 to 800 KB challenge script. No code changes. Plus a Kasada endpoint that reads x-kpsdk-v from p.js.

Hyper Solutions3 min read
On this page
General
TLS fingerprint
Header order
Client hints

Every DataDome interstitial and slider, Incapsula reese84 and Akamai sensor or SBSD request used to carry the whole challenge script: 600 to 800 KB, uploaded again on every request, although the script is the same one thousands of other requests have already sent.

That is over. The APIs now accept the script's sha256 in its place, a 64 character string. Upgrade the SDK and your requests send the hash automatically. No code changes.

Key Takeaways

  • Upgrade and you are done. Go v4.1.0, Python 4.1.0 and JS 5.1.0 send the hash instead of the script on every request that takes one.
  • Requests shrink by up to 99%. In our tests an Akamai sensor request went from 198 KB to 1.3 KB on the wire, and a DataDome interstitial from 250 KB to 3.6 KB.
  • The script is uploaded once per version, not once per request. When nobody has sent a script yet, the API asks for it once with a 422 script_required, and the SDK sends it. From then on the hash is enough, for every customer.
  • Raw HTTP users add one field and one retry.
  • Kasada x-kpsdk-v from p.js. A new endpoint reads the version the page sends on /fp straight from the site's p.js, so you no longer hardcode it.

Upgrade the SDK

Go          github.com/Hyper-Solutions/hyper-sdk-go/v4   v4.1.0
Python      hyper-sdk                                    4.1.0
JS/TS       hyper-sdk-js                                 5.1.0
Playwright  hyper-sdk-playwright                         1.0.0-beta.18

Keep passing the script exactly as you do today. The SDK sends only its hash, and sends the script itself only when the API answers that it has not seen it yet. A request chained with an Akamai context carries no script and is sent as before.

How it works

  1. Your request carries scriptHash, the lowercase hex sha256 of the script exactly as the site served it, instead of script.
  2. If any client has sent that script in the last 7 days, the API already knows what it needs from it and generates the payload as if you had sent the script.
  3. If nobody has, the API answers HTTP 422 with {"error":"script_required"}. It is not billed. The request is repeated once with script, and the API keeps what it needs from the script for every later request.

A new script version therefore costs one extra round trip, once, for whichever request meets it first. Everything after that sends 64 bytes instead of the script.

Some requests are solved in a real browser on our side, which needs the script itself. Those answer script_required to a hash-only request too, and the same retry handles it.

Raw HTTP

Compute the hash over the exact bytes you would have sent as script:

sum := sha256.Sum256([]byte(script))
body["scriptHash"] = hex.EncodeToString(sum[:])
delete(body, "script")
// POST; on 422 {"error":"script_required"}, POST again with "script" and without "scriptHash"

Three rules:

  • Never send script and scriptHash together. The API answers 400.
  • On Akamai, scriptHash and context are mutually exclusive too. The first request of a chain sends scriptHash (or script), later ones send context, exactly as before.
  • Send one of the two. Incapsula reese84 answers 400 without either. DataDome still answers, but builds the payload for a fixed bundle version that may not match the page.

The scriptHash guide has the details and examples in Go, Python and JavaScript.

Kasada: x-kpsdk-v from p.js

The /fp request and the x-kpsdk-v header carry a version string such as j-1.2.797. It is decoded inside p.js at run time, so it is not in the script as plain text, and integrations have been hardcoding it and updating it by hand whenever Kasada ships.

Now you fetch p.js and ask the API:

res, err := session.GetKasadaVersion(ctx, &hyper.KasadaVersionInput{PageUrl: pageURL, Script: pjs})
// send res.Version on /fp?x-kpsdk-v=... and in the x-kpsdk-v header

It is get_kasada_version in Python and getKasadaVersion in JS, and it is not counted as usage.

If something is unclear

The docs cover every endpoint that takes scriptHash, and our Claude Code and Codex plugins know about it too, including that a script_required answer is expected and not something to debug.

Come and ask in Discord if your integration does something the SDKs do not cover.

Try it yourself

Solve Akamai, DataDome, Incapsula and Kasada in one request

Skip the browser. Generate the sensor data, tokens and cookies you just read about over plain HTTP, with a free one-week trial.