New major versions of all three SDKs are out: Go v4.0.0, Python 4.0.0 and JS 5.0.0, plus hyper-sdk-playwright 1.0.0-beta.16. They carry four API changes, and one change to the SDKs themselves that makes this the last major you will need for a while.
Key Takeaways
- Every generate call now returns a result object instead of loose values. New fields arrive as properties from now on, so a field added later no longer means another major version. This is the breaking part of the upgrade.
- Client hints, on every product. Akamai, DataDome, Incapsula and Kasada now return the full set of Accept-CH client hints for the device the payload describes: 24 headers, ready to send.
- Akamai-BM-Telemetry. Some sites protect their API calls with an
Akamai-BM-Telemetryrequest header instead of a sensor post. Two new endpoints generate that header for you.- SBSD
scriptUrl. Send the src of the SBSD script tag and the payload carries the script's own values, the way a browser's does.- Kasada
strict. Opt in to refuse a payload when a script contains a signal we cannot identify yet, instead of getting one with that signal filled in.
Upgrade the SDK
Go github.com/Hyper-Solutions/hyper-sdk-go/v4 v4.0.0
Python hyper-sdk 4.0.0
JS/TS hyper-sdk-js 5.0.0
Playwright hyper-sdk-playwright 1.0.0-beta.16
On Go the module path moves to /v4, so it is an import change as well as a version bump. The /v3 module is now marked deprecated and will not get these features.
If you use hyper-sdk-playwright, upgrade to 1.0.0-beta.16 and you are done. It handles client hints, the SBSD scriptUrl and the telemetry header for you.
Result objects
This is the one change that touches code you already have. Every generate call returns a single result object, and you read the payload, the context and the new client hints off it.
Go, before and after:
// v3
sensorData, sensorContext, err := session.GenerateSensorData(ctx, input)
// v4
res, err := session.GenerateSensorData(ctx, input)
sensorData := res.Payload
sensorContext = res.Context
hints := res.ClientHints
Python:
# 3.x
payload, context = session.generate_sensor_data(sensor_input)
# 4.0.0
res = session.generate_sensor_data(sensor_input)
payload, context, hints = res.payload, res.context, res.client_hints
JS/TS. Calls that resolved to a plain string now resolve to an object too, so generateReese84Sensor, generatePixelData, generateTagsPayload, generateKasadaPow and generateBotIDHeader need a .payload:
// 4.x
const reese84 = await generateReese84Sensor(session, input);
// 5.0.0
const { payload, clientHints } = await generateReese84Sensor(session, input);
Two smaller breaking changes come with it:
- SBSD no longer takes an index. SBSD chains through
context: the first call sends the script with an empty context, and every call after it sends the context it got back. A hard block, an SBSD POST with atparameter, always starts a new chain with the script and an empty context. - DataDome's hints move to
clientHints. In JS, the interstitial and slider results used to return them asheaders.
Client hints
Chrome sends client hints on every request, and sites that ask for more through an Accept-CH response header get more. Until now you had to make those values up, and a sec-ch-viewport-width that does not match the device in your payload is exactly the kind of mismatch that gets a session flagged.
The APIs now return all 24 hints for the device the payload was generated for: the sec-ch-ua family, device memory and pixel ratio, viewport, network (ect, rtt, downlink) and the sec-ch-prefers-* preferences. Brands and full versions come from your user agent; everything else comes from the device.
You do not send all 24 on every request. The rule is the browser's:
sec-ch-ua,sec-ch-ua-mobileandsec-ch-ua-platformgo on every request.- Any other hint only goes to an origin whose
Accept-CHheader asked for it. - Cross-origin requests get only the first three.
The hints are under ClientHints / client_hints / clientHints on every result. On Kasada they sit next to headers, which still holds the x-kpsdk-* headers you send on the protected request, so forwarding the whole headers map stays safe. The client hints guide has the full key list and code.
Akamai-BM-Telemetry
Some Akamai sites never post sensor data. Instead, the page's own XHR and fetch calls to the protected API carry an Akamai-BM-Telemetry header, and the API answers 403 without a valid one.
Two new endpoints cover it:
/v1/telemetry/paramsreturns the path of the site's get_params request. Request it once per session and keep thejfrom its response./v1/telemetrytakes the same input as a sensor request plus thatj, and returns the complete header value. Call it before every protected request, chainingcontext, and never reuse a header.
Go:
params, err := session.GetTelemetryParams(ctx, &hyper.TelemetryParamsInput{PageUrl: pageURL, Script: script})
// GET pageOrigin + params.Path once and keep the j from its JSON response
res, err := session.GenerateTelemetry(ctx, &hyper.TelemetryInput{
SensorInput: hyper.SensorInput{
Abck: abck, Bmsz: bmsz, PageUrl: pageURL, ScriptUrl: scriptURL,
UserAgent: userAgent, IP: ip, AcceptLanguage: acceptLanguage,
Context: params.Context,
},
J: j,
})
// send res.Header as Akamai-BM-Telemetry, keep res.Context for the next request
Python and JS follow the same shape (get_telemetry_params / generate_telemetry, getTelemetryParams / generateTelemetry). The Akamai-BM-Telemetry guide shows how to recognise these sites and walks through the flow in all three languages.
SBSD scriptUrl
The SBSD endpoint takes an optional scriptUrl: the absolute src of the SBSD script tag, query included, exactly as the page loads it.
A browser's SBSD fingerprint includes signals about the script itself, and those differ between an SBSD script on a normal page and one on a block page. With scriptUrl set, the payload carries the script's own values; without it, the API falls back to defaults. Send it whenever you have the script tag.
res, err := session.GenerateSbsdData(ctx, &hyper.SbsdInput{
// ...the fields you already send
ScriptUrl: "https://example.com/.well-known/sbsd?v=7ac10a5c-7a4e-fddd-611f-39dcf23f1722&t=99543528",
})
It is script_url= in Python and the last constructor argument of SbsdInput in JS.
Kasada strict
Kasada ships new signals now and then. When a script contains one we cannot identify yet, the API fills it in as best it can and returns a payload. That is the right default for most integrations.
If you would rather fail fast, set strict. A script with an unidentified signal then returns an error instead of a payload, and you can retry later or fall back.
res, err := session.GenerateKasadaPayload(ctx, &hyper.KasadaPayloadInput{
// ...the fields you already send
Strict: true,
})
It is strict=True in Python and the last constructor argument of KasadaPayloadInput in JS. It defaults to off.
If something is unclear
Working end-to-end examples for every language and TLS client are in the examples repo, already on the new versions, and the API reference is in the docs. Our Claude Code and Codex plugins know about all of this too.
Read this through first, then come and ask. We are in Discord and happy to look at a capture if your integration does something the examples do not cover.
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.