> For the complete documentation index, see [llms.txt](https://docs.hypersolutions.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hypersolutions.co/akamai-web/akamai-bm-telemetry.md).

# Akamai-BM-Telemetry Header

How to handle sites that protect their API with an Akamai-BM-Telemetry request header instead of a sensor\_data post, using the Hyper Solutions telemetry endpoints and SDKs.

Some sites protect their API host with an **`Akamai-BM-Telemetry`** request header instead of a `sensor_data` post. The page loads the usual Akamai script, but instead of posting sensor data, the script attaches a freshly built header to every protected XHR/fetch call. The protected host rejects calls without a valid header.

### Recognising it

* The site's protected API calls carry a header like:

  ```http
  Akamai-BM-Telemetry: a=74CA869260DD6EF307EA308F541F4DF5&&&e=E7C2...~3421233~3491378&&&sensor_data=MzsxOzI7...&&&j=AAQAAAAG%2f%2f...
  ```
* There is **no** `sensor_data` POST to the script URL.
* The page makes a `GET /_bm/get_params?type=web-jsto&v=...` request once, and its JSON response holds a `j` value.
* The protected host answers **403** when the header is missing or invalid, often while page loads and other requests succeed.

The header has up to four fields, joined by `&&&`:

| Field         | Meaning                                                                                                    |
| ------------- | ---------------------------------------------------------------------------------------------------------- |
| `a`           | The `_abck` id, taken from the `_abck` cookie when its second field is `-1`; empty otherwise.              |
| `e`           | The raw `bm_sz` cookie.                                                                                    |
| `sensor_data` | The base64 sensor payload.                                                                                 |
| `j`           | The `j` from the page's get\_params response, verbatim (still URL-encoded). Absent when the site has none. |

You do not need to build any of this yourself: [`POST /v1/telemetry`](/api-reference/akamai.md#akamai-bm-telemetry) returns the complete header value.

### Flow

#### Step 1: Load the page and fetch the script

Load the page and parse and fetch the Akamai script exactly as in the [sensor flow](/akamai-web/getting-started.md) (steps 1 to 3). After the page load you hold the `_abck` and `bm_sz` cookies, the script content and the script URL.

{% hint style="info" %}
Use the cookies the page itself holds. When the protected calls go to another host without credentials (as on Paylogic), the browser sends no cookies there and ignores the `Set-Cookie` headers those responses carry, so `_abck` and `bm_sz` stay the page-load values.
{% endhint %}

#### Step 2: Get the get\_params path

Call [`POST /v1/telemetry/params`](/api-reference/akamai.md#akamai-bm-telemetry) with the page URL and the script (or its sha256 as [`scriptHash`](https://github.com/Hyper-Solutions/gitbook/tree/master/script-hash.md)). It returns:

* `payload`: the get\_params path, exactly as the browser requests it, for example `/_bm/get_params?type=web-jsto&v=aNjc97OfvDBoc9gY88Sbui9EwsB6tx66vij5EVYP8U0=`. The tag is not URL-encoded: send the path as returned, `+`, `/` and `=` included.
* `context`: a context you can send on the first telemetry call instead of the script.

`/v1/telemetry/params` does not count towards your usage.

#### Step 3: Request get\_params once

`GET` the path on the page's origin, with the page's cookies, like the browser does (`sec-fetch-site: same-origin`, `sec-fetch-mode: cors`). The response is JSON:

```json
{"j": "AAQAAAAG%2f%2f%2f%2f%2f99sZNhXo9OUfpaWareApE2kD6zC6RBsYamGwkmHxr55U9exw8q5tQjMA..."}
```

Keep `j` **verbatim**, still URL-encoded. Request get\_params once per session, not before every call.

#### Step 4: One telemetry header per protected call

Before **every** protected request, call [`POST /v1/telemetry`](/api-reference/akamai.md#akamai-bm-telemetry) with the sensor fields, `j`, and the `context` from the previous call (on the first call, the context from step 2, or the script and no context). Send the returned `payload` verbatim as the `Akamai-BM-Telemetry` header, and keep the returned `context` for the next call.

{% hint style="warning" %}
Never reuse a header. Each protected call needs a fresh one built from the previous call's context, the same way the browser builds a new header for every request.
{% endhint %}

### Code samples

{% tabs %}
{% tab title="Golang" %}
{% code overflow="wrap" %}

```go
import hyper "github.com/Hyper-Solutions/hyper-sdk-go/v4"

// Step 2: the get_params path, and a context that replaces the script
params, err := session.GetTelemetryParams(ctx, &hyper.TelemetryParamsInput{
    PageUrl: pageUrl,
    Script:  scriptContent,
})
if err != nil {
    // Handle error
}

// Step 3: GET pageOrigin + params.Path once, decode {"j": "..."} and keep j verbatim
j := getParams(pageOrigin + params.Path)

// Step 4: before every protected request
telemetryContext := params.Context
res, err := session.GenerateTelemetry(ctx, &hyper.TelemetryInput{
    SensorInput: hyper.SensorInput{
        Abck:           abckCookie, // _abck from the page load
        Bmsz:           bmszCookie, // bm_sz from the page load
        PageUrl:        pageUrl,
        ScriptUrl:      scriptUrl,
        UserAgent:      userAgent,
        AcceptLanguage: "en-US,en;q=0.9",
        IP:             clientIP,
        Context:        telemetryContext, // no Script once there is a context
    },
    J: j,
})
if err != nil {
    // Handle error
}
req.Header.Set("Akamai-BM-Telemetry", res.Header)
telemetryContext = res.Context
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code overflow="wrap" %}

```python
from hyper_sdk import TelemetryInput, TelemetryParamsInput

# Step 2: the get_params path, and a context that replaces the script
params = session.get_telemetry_params(TelemetryParamsInput(page_url=page_url, script=script_content))

# Step 3: GET page_origin + params.path once and keep j verbatim
j = get_params(page_origin + params.path)["j"]

# Step 4: before every protected request
telemetry_context = params.context
res = session.generate_telemetry(TelemetryInput(
    abck=abck_cookie,    # _abck from the page load
    bmsz=bmsz_cookie,    # bm_sz from the page load
    page_url=page_url,
    user_agent=user_agent,
    ip=client_ip,
    accept_language="en-US,en;q=0.9",
    context=telemetry_context,
    script="",           # no script once there is a context
    script_url=script_url,
    j=j,
))
headers["Akamai-BM-Telemetry"] = res.header
telemetry_context = res.context
```

{% endcode %}
{% endtab %}

{% tab title="JS / TS" %}
{% code overflow="wrap" %}

```javascript
import { getTelemetryParams, generateTelemetry, TelemetryParamsInput, TelemetryInput } from "hyper-sdk-js";

// Step 2: the get_params path, and a context that replaces the script
const params = await getTelemetryParams(session, new TelemetryParamsInput(pageUrl, scriptContent));

// Step 3: GET pageOrigin + params.path once and keep j verbatim
const j = (await getParams(pageOrigin + params.path)).j;

// Step 4: before every protected request
let telemetryContext = params.context;
const res = await generateTelemetry(session, new TelemetryInput(
    abckCookie,        // _abck from the page load
    bmszCookie,        // bm_sz from the page load
    pageUrl,
    userAgent,
    clientIP,
    "en-US,en;q=0.9",  // acceptLanguage
    telemetryContext,  // context
    "",                // script: none once there is a context
    scriptUrl,
    j
));
headers["Akamai-BM-Telemetry"] = res.header;
telemetryContext = res.context;
```

{% endcode %}
{% endtab %}

{% tab title="Playwright" %}
`hyper-sdk-playwright`'s `AkamaiHandler` handles this automatically from `1.0.0-beta.16`: it replaces the `Akamai-BM-Telemetry` header on every request that carries one with an API-generated header, chained across calls. Nothing to configure.
{% endtab %}
{% endtabs %}

These are the minimum SDK versions with the telemetry methods:

| SDK                                              | Minimum version | Methods                                      |
| ------------------------------------------------ | --------------- | -------------------------------------------- |
| Go, `github.com/Hyper-Solutions/hyper-sdk-go/v4` | `v4.0.0`        | `GenerateTelemetry`, `GetTelemetryParams`    |
| Python, `hyper-sdk`                              | `4.0.0`         | `generate_telemetry`, `get_telemetry_params` |
| JS / TS, `hyper-sdk-js`                          | `5.0.0`         | `generateTelemetry`, `getTelemetryParams`    |
| `hyper-sdk-playwright`                           | `1.0.0-beta.16` | automatic                                    |

### Troubleshooting

* **403 on the first protected call:** check that `scriptUrl` is the full URL of the Akamai script (the one in the page's script tag), and that `abck` and `bm_sz` are the page-load cookies.
* **The first calls pass, later ones fail:** you are reusing a header or a context. Every call takes a fresh header, and every telemetry request sends the context from the previous response.
* **`j` looks double-encoded:** send it exactly as get\_params returned it, without decoding or re-encoding.
* Send the protected request's other headers the way the site's own app does, including any [client hints](/start-here/client-hints.md) the site asks for.
