> 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/start-here/client-hints.md).

# Client Hints

The Accept-CH client hint headers the Hyper Solutions APIs return with each payload, which endpoints return them, and how to send them on your requests.

The Akamai, DataDome, Incapsula and Kasada APIs return the **Accept-CH client hint headers** for the device each payload was generated for. Chrome sends these hints to sites that ask for them, so a request that claims one device in its payload and another (or none) in its hints stands out.

### Which endpoints return them

| Product   | Endpoints                              | Response key  |
| --------- | -------------------------------------- | ------------- |
| Akamai    | `/v2/sensor`, `/sbsd`, `/v1/telemetry` | `headers`     |
| DataDome  | `/interstitial`, `/slider`, `/tags`    | `headers`     |
| Incapsula | `/reese84`                             | `headers`     |
| Kasada    | `/payload`                             | `clientHints` |

Kasada already uses `headers` for the `x-kpsdk-*` values every `/tl` request needs, so its hints come in a separate `clientHints` object. Keep the two apart: the `x-kpsdk-*` headers always go on the request, the hints only when the site asks for them.

The hints are absent for Safari user agents on Incapsula and for mobile user agents on Kasada.

### The hints

Each object is keyed by lowercase header name, with values ready to send verbatim (already serialized the way Chrome sends them, do not re-quote):

| Group                   | Headers                                                                                                                                                                                                                                             |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User-Agent client hints | `sec-ch-ua`, `sec-ch-ua-mobile`, `sec-ch-ua-platform`, `sec-ch-ua-arch`, `sec-ch-ua-bitness`, `sec-ch-ua-model`, `sec-ch-ua-platform-version`, `sec-ch-ua-full-version`, `sec-ch-ua-full-version-list`, `sec-ch-ua-wow64`, `sec-ch-ua-form-factors` |
| Device                  | `sec-ch-device-memory`, `sec-ch-dpr`, `sec-ch-viewport-width`, `sec-ch-viewport-height`, and the legacy `device-memory`, `dpr`, `viewport-width`                                                                                                    |
| Network                 | `ect`, `rtt`, `downlink`                                                                                                                                                                                                                            |
| User preferences        | `sec-ch-prefers-color-scheme`, `sec-ch-prefers-reduced-motion`, `sec-ch-prefers-reduced-transparency`                                                                                                                                               |

### How to send them

Send the hints the way Chrome does, on the requests to the site that follow the payload:

* `sec-ch-ua`, `sec-ch-ua-mobile` and `sec-ch-ua-platform` are low-entropy hints that Chrome sends on every secure request, whether the site asked or not.
* Send any other hint **only** when the site asked for it in its `Accept-CH` response header (or `Critical-CH`). Chrome remembers the site's `Accept-CH` for that origin, so keep sending the requested hints on later requests to it.
* Use the values from the latest payload for that session, not hardcoded values from an old browser capture.
* Keep the [header order](/request-based-basics/header-order.md) of a real Chrome request.

{% hint style="warning" %}
Do not send every hint on every request. A site that never asked for `sec-ch-ua-full-version-list` and receives it anyway sees a request no browser would make.
{% endhint %}

### In the SDKs

Every SDK result that carries hints exposes them as a map, `nil`/`None`/`undefined` when the API returned none:

| SDK                                              | Minimum version | Field                                    |
| ------------------------------------------------ | --------------- | ---------------------------------------- |
| Go, `github.com/Hyper-Solutions/hyper-sdk-go/v4` | `v4.0.0`        | `ClientHints` (`map[string]string`)      |
| Python, `hyper-sdk`                              | `4.0.0`         | `client_hints` (`dict`)                  |
| JS / TS, `hyper-sdk-js`                          | `5.0.0`         | `clientHints` (`Record<string, string>`) |

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

```go
res, err := session.GenerateSensorData(ctx, sensorInput)
if err != nil {
    // Handle error
}

// accepted holds the lowercase header names from the site's Accept-CH response header
for name, value := range res.ClientHints {
    if name == "sec-ch-ua" || name == "sec-ch-ua-mobile" || name == "sec-ch-ua-platform" || accepted[name] {
        req.Header.Set(name, value)
    }
}
```

{% endcode %}
{% endtab %}

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

```python
res = session.generate_sensor_data(sensor_input)

# accepted holds the lowercase header names from the site's Accept-CH response header
low_entropy = {"sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform"}
for name, value in (res.client_hints or {}).items():
    if name in low_entropy or name in accepted:
        headers[name] = value
```

{% endcode %}
{% endtab %}

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

```javascript
const res = await generateSensorData(session, sensorInput);

// accepted holds the lowercase header names from the site's Accept-CH response header
const lowEntropy = new Set(["sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform"]);
for (const [name, value] of Object.entries(res.clientHints ?? {})) {
    if (lowEntropy.has(name) || accepted.has(name)) {
        headers[name] = value;
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

On Kasada, read `ClientHints` / `client_hints` / `clientHints` from the payload result; its `Headers` / `headers` stay the `x-kpsdk-*` values.
