# Getting started

Hyper Solutions generates sensor data for bypassing Akamai Bot Manager, Incapsula, DataDome, and Kasada.

Hyper Solutions is a payload-generation API for **request-based scraping**: it generates the sensor data, cookies, and tokens that Akamai, Incapsula, DataDome, and Kasada expect from a real browser, so you can bypass them with plain HTTP requests, no headless browser.

### New here? Follow this path

1. [**Overview**](/start-here/overview): what the API does and the mental model behind it (the API never touches the target site; your code does). Two minutes, and everything else makes more sense.
2. [**Quickstart**](/start-here/quickstart): get an API key, install an SDK, and make your first authenticated call.
3. [**Core Requirements**](/start-here/core-requirements): the non-negotiables (browser-grade TLS client, header order, sticky proxies) that every integration needs.
4. [**Request-Based Basics**](/request-based-basics/tls-fingerprinting): the concepts behind TLS fingerprinting and header order, if they're new to you.

### Ready to bypass a site

Identify your target's anti-bot system and open its guide: **Akamai**, **Incapsula**, **DataDome**, or **Kasada**. Each has a Getting Started that walks the flow. Install a client library from [SDKs & Examples](/start-here/readme-1) (Python, JS/TS, or Go), or see the [API Reference](/api-reference/authentication) if you'd rather implement it directly.

For complete, end-to-end walkthroughs, see our [case studies on the blog](https://hypersolutions.co/blog) or grab working code from the [examples repository](https://github.com/Hyper-Solutions/hypersolutions-examples).

{% hint style="info" %}
Building with an AI agent? The [Claude Code plugin](/ai-plugins/claude-code-plugin) knows the whole API and can scaffold an integration from a single prompt.
{% endhint %}


# Overview

What Hyper Solutions does, how the API fits into your scraper, and the mental model that makes everything else in these docs make sense.

Hyper Solutions is a **payload-generation API** for request-based scraping. It generates the sensor data, cookies, and tokens that anti-bot systems (**Akamai Bot Manager, Incapsula/Imperva, DataDome, and Kasada**) expect from a real browser, so you can access protected sites with plain HTTP requests instead of running a headless browser.

## The mental model

The single most important thing to understand: **the API never talks to the target site.** It does not fetch pages or solve challenges over the network for you.

Instead, **your code** owns all traffic to the target. You collect the inputs a challenge needs (script contents, cookies, UUIDs, your IP, your User-Agent), send those inputs to `*.hypersolutions.co`, and get back the payload a real browser would have produced. You then POST that payload to the target yourself.

<figure><img src="/files/SXaO1l1kspVblXnkiYU6" alt="Your code fetches the page and script from the target site, sends those inputs to the Hyper Solutions API, gets back a generated payload or token, and posts it back to the target to obtain a valid cookie."><figcaption></figcaption></figure>

Because your code owns steps 1 and 5, **the quality of your HTTP client matters as much as the payload.** When a request is blocked even though the generated payload looks correct, the cause is almost always in your request (the TLS fingerprint, header order, cookies, or IP) not in the payload. That is why the [Core Requirements](/start-here/core-requirements) page exists and why every integration guide repeats them.

## What you're responsible for

* A **browser-grade TLS client** (standard HTTP libraries are blocked on sight).
* **Exact browser header order**, including HTTP/2 pseudo-headers.
* **Session consistency**: the same User-Agent, TLS fingerprint, IP, and header order for the whole flow.
* **Sticky proxies** whose IP matches what you send to the API.

See [Core Requirements](/start-here/core-requirements) for the details.

## What Hyper Solutions handles

| Anti-bot system         | What we generate                                                         |
| ----------------------- | ------------------------------------------------------------------------ |
| **Akamai Bot Manager**  | `_abck` sensor data, SBSD payloads, SEC-CPT (428) proof-of-work, pixel   |
| **Incapsula / Imperva** | `reese84` sensors (static + dynamic), `___utmvc` cookies                 |
| **DataDome**            | interstitial + slider payloads, tags telemetry                           |
| **Kasada**              | challenge tokens, per-request proof-of-work, Vercel BotID (`x-is-human`) |

## Where to go next

1. [**Quickstart**](/start-here/quickstart): get an API key and a working SDK in a few minutes.
2. [**Core Requirements**](/start-here/core-requirements): the non-negotiables every integration needs.
3. [**Request-Based Basics**](/request-based-basics/tls-fingerprinting): the concepts behind TLS fingerprinting and header order.
4. **Pick your anti-bot**: head to the matching guide (Akamai, Incapsula, DataDome, or Kasada) and follow its Getting Started.


# Quickstart

Get an API key, install an SDK, and make your first authenticated call to Hyper Solutions in a few minutes.

This gets you from zero to a working SDK and a verified API key. It does **not** bypass a site yet, for that, finish here and then follow the guide for your target's anti-bot system.

New to how the API fits together? Read the [Overview](/start-here/overview) first (2 minutes).

### 1. Get your API key

Create an account and grab a key from your dashboard: [hypersolutions.co/keys](https://hypersolutions.co/keys). Every request authenticates with this key via the `x-api-key` header, the SDKs handle that header for you.

### 2. Install an SDK

{% tabs %}
{% tab title="Go" %}

```bash
go get github.com/Hyper-Solutions/hyper-sdk-go/v2
```

{% endtab %}

{% tab title="Python" %}

```bash
pip install hyper-sdk
```

{% endtab %}

{% tab title="JS/TS" %}

```bash
npm install hyper-sdk-js
```

{% endtab %}
{% endtabs %}

### 3. Construct a session

{% tabs %}
{% tab title="Go" %}

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

session := hyper.NewSession("your-api-key")
```

{% endtab %}

{% tab title="Python" %}

```python
from hyper_sdk import Session

session = Session("your-api-key")
```

{% endtab %}

{% tab title="JS/TS" %}

```typescript
import { Session } from "hyper-sdk-js";

const session = new Session("your-api-key");
```

{% endtab %}
{% endtabs %}

### 4. Confirm your key works

Constructing the session above does no network I/O, so make one quick call to confirm your key works. The simplest is the `/ip` endpoint, which you'll use in real integrations too: it returns the outbound IP of your proxy, which you pass as an input to every sensor call (see [Core Requirements](/start-here/core-requirements)).

{% code overflow="wrap" %}

```bash
curl https://ip.hypersolutions.co/ip -H "x-api-key: your-api-key"
```

{% endcode %}

A successful response returns your IP as JSON:

```json
{ "ip": "1.2.3.4" }
```

If you get a `401`, your key is wrong or missing. Route this request through the **same sticky proxy** you'll use for scraping so the IP matches, see [IP](/request-based-basics/ip) for why this matters.

### 5. Bypass your first site

You're set up. Now:

1. Read the [Core Requirements](/start-here/core-requirements), the TLS client, header order, and proxy rules every integration needs.
2. Identify your target's anti-bot system and open its guide: **Akamai**, **Incapsula**, **DataDome**, or **Kasada**.
3. Follow that guide's Getting Started, then grab full working code from the [examples repository](https://github.com/Hyper-Solutions/hypersolutions-examples).

{% hint style="info" %}
Prefer to build with an AI agent? The [Claude Code plugin](/ai-plugins/claude-code-plugin) knows the whole API and can scaffold an integration for you from a single prompt.
{% endhint %}


# Core Requirements

The non-negotiable requirements that apply to every Hyper Solutions integration, regardless of which anti-bot system you're bypassing.

These five requirements apply to **every** product. If any one of them is wrong, you will be blocked no matter how good the generated payload is. Standard HTTP libraries (`requests`, `axios`, `net/http`, `fetch`) fail all of these by default, they have non-browser TLS fingerprints and no control over header order.

Every vendor guide assumes you already meet these, so it's worth getting them right once, here.

### 1. Browser-grade TLS client

Use an HTTP client that reproduces a real Chrome TLS handshake, for example [`tls-client`](https://github.com/bogdanfinn/tls-client) or [`azuretls-client`](https://github.com/Noooste/azuretls-client). Configure it with:

* The **latest Chrome profile** your client ships (a slightly older profile is acceptable, see [User Agents](/api-reference/user-agents)).
* **HTTP/3 disabled**: most proxies don't support it yet.
* **Random TLS extension order enabled.**

Full detail: [TLS Fingerprinting](/request-based-basics/tls-fingerprinting).

### 2. Exact browser header order

Header order (including the HTTP/2 pseudo-header order (`:method`, `:authority`, `:scheme`, `:path` for Chrome)) is one of the strongest fingerprinting signals. Browser DevTools does **not** show the real order, so never copy header order from it. Capture it with a proxy that preserves the wire order instead.

Full detail: [Header Order](/request-based-basics/header-order).

### 3. Session consistency

Keep the same **User-Agent, TLS fingerprint, IP address, and header order** for the entire flow, backed by a proper cookie jar. Your client-hint headers (`sec-ch-ua`, `sec-ch-ua-platform`) must stay consistent too. Mixing values mid-session is a reliable way to get flagged.

### 4. Sticky proxies, never rotating

The IP you send to the API must match the IP the target site actually sees. Rotating proxies issue a new IP per connection, so the API generates a payload for one IP while the target sees another, and you get blocked. Use **sticky/session proxies**, and pass your outbound IP (from `GET https://ip.hypersolutions.co/ip`) as the `ip` input.

Full detail: [IP](/request-based-basics/ip).

### 5. Matched Chrome versions

The Chrome version in your **User-Agent**, your **`sec-ch-ua`** header, and your **`sec-ch-ua-platform`** must all agree. A User-Agent claiming one version with client hints from another is an obvious automation signal. Update them together whenever you bump the version.

Full detail: [User Agents](/api-reference/user-agents).

{% hint style="info" %}
**Replay, don't hardcode.** Most generate endpoints return a `headers` object of client hints. Replay those on the target site rather than hardcoding your own, hardcoded hints from a stale browser capture are a common cause of blocks.
{% endhint %}

### Still blocked?

If you meet all five and are still blocked, the fault is almost always in the request rather than the payload. Work through the [Claude Code plugin](/ai-plugins/claude-code-plugin) or capture your traffic with [powhttp](/request-based-basics/installing-powhttp) to see exactly what went on the wire.


# SDKs & Examples

We have SDKs for the following languages: Golang, Python, NodeJS/TS.

## Examples

{% embed url="<https://github.com/Hyper-Solutions/hypersolutions-examples>" %}

## Golang

{% embed url="<https://github.com/Hyper-Solutions/hyper-sdk-go>" %}

## Python

{% embed url="<https://github.com/Hyper-Solutions/hyper-sdk-py>" %}

## JavaScript / TypeScript

{% embed url="<https://github.com/Hyper-Solutions/hyper-sdk-js>" %}


# TLS Fingerprinting

This page explains everything you need to know about TLS when making requests based modules.

### Why Antibots Use TLS Fingerprinting

TLS fingerprinting is a powerful technique employed by antibot systems to distinguish between legitimate browser traffic and automated requests. When a client initiates a TLS handshake, it reveals specific characteristics about the underlying software making the connection.

Standard HTTP request libraries (like Python's `requests`, Node.js's `axios`, or Go's `net/http`) have distinctly different TLS fingerprints compared to real web browsers. These libraries typically:

* Use different cipher suite preferences
* Support different TLS extensions
* Order TLS extensions in predictable patterns
* Have library-specific SSL/TLS implementation details

Since automated bots and scrapers commonly rely on these standard libraries, antibot systems can easily identify and block requests that don't match expected browser fingerprints. This creates an effective first line of defense against automated traffic, as legitimate users virtually never use request libraries directly.

### Solution: Browser-Matching TLS Clients

To bypass TLS fingerprinting, you need HTTP clients that can mimic real browser TLS behavior. Two excellent options are available:

#### tls-client

**Repository:** <https://github.com/bogdanfinn/tls-client>

A Go-based HTTP client that can impersonate various browsers' TLS fingerprints with high fidelity. Multiple wrappers for Python and Node.js are available and listed here: <https://bogdanfinn.gitbook.io/open-source-oasis/community-projects>

#### azuretls-client

**Repository:** <https://github.com/Noooste/azuretls-client>

A Go HTTP client designed to replicate browser TLS characteristics and bypass fingerprinting detection.

### Recommended Configuration

Use the **latest Chrome profile your client ships** (for example `Chrome133` or newer), and enable random TLS extension ordering:

* **Profile:** the most recent `ChromeNNN` profile available in your library
* **Random TLS extension order:** Enabled
* **HTTP/3 disabled:** most proxies don't support it yet, so force HTTP/2 (the version numbers in profile names like `Chrome133` are illustrative; use the latest your library ships)

Match the profile to the Chrome version in your User-Agent as closely as the library allows. Chrome's TLS ClientHello doesn't change on every release, so a slightly older profile is acceptable when your library hasn't shipped the newest one yet, see [User Agents](/api-reference/user-agents) for the current stable version and the fallback rule.

This configuration ensures your requests closely mimic genuine Chrome browser traffic.

By using these specialized clients with proper configuration, you can effectively bypass TLS fingerprinting while maintaining the convenience of programmatic HTTP requests.\
\
Get in touch: [discord.gg/akamai](https://discord.gg/akamai)


# Header Order

This page explains everything you need to know about headers and header order when making requests based modules.

### Why Header Order Matters

Header order is one of the most distinctive fingerprinting characteristics that differentiates real browsers from automated scripts. Both HTTP/1.1 and HTTP/2 preserve the exact order in which headers are sent, making it a powerful detection mechanism for antibot systems.

Different browser implementations send headers in distinctly different orders. Chrome, for example, always sends headers in a deterministic order that's specific to its implementation. HTTP/2 additionally uses pseudo-headers (prefixed with `:`) that also follow implementation-specific ordering patterns:

* **Chrome browsers** send pseudo-headers as: `:method`, `:authority`, `:scheme`, `:path`
* **Firefox browsers** send them as: `:method`, `:path`, `:authority`, `:scheme`
* **Safari browsers** use: `:method`, `:scheme`, `:path`, `:authority`

Normal HTTP request libraries (like Python's `requests`, Node.js's `axios`, or Java's `HttpURLConnection`) don't provide any control over header order, typically sending them in alphabetical order or the order they were added to the request. This makes header order an excellent way for antibot systems to detect automated scripts, even when they correctly match Chrome's TLS fingerprint.

This implementation-specific ordering makes it trivial for servers to identify the client type, regardless of User-Agent spoofing attempts.

{% hint style="info" %}
The Chrome version numbers in the examples below (TLS profiles like `Chrome_133`, and `sec-ch-ua`/User-Agent strings like Chrome 137/138) are **illustrative**. Always send the current Chrome stable version, and keep your `sec-ch-ua` and User-Agent versions matched to each other. Your TLS profile may lag slightly behind when your library hasn't shipped the newest one yet. See [User Agents](/api-reference/user-agents) for the current version and the fallback rule.
{% endhint %}

### Never Use Browser DevTools for Header Analysis

One of the most critical mistakes when building HTTP/2 scripts is relying on browser Developer Tools to understand header order. DevTools **do not** show headers in the order they're actually sent to the server.

For example, DevTools might display headers like this:

```
:authority tls.peet.ws
:method GET
:path /api/all
:scheme https
accept text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
accept-encoding gzip, deflate, br, zstd
accept-language en
priority u=0, i
sec-ch-ua "Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"
```

However, the actual order sent by the browser (as captured by Charles Web Proxy) is:

```
:method	GET
:authority	tls.peet.ws
:scheme	https
:path	/api/all
sec-ch-ua	"Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"
sec-ch-ua-mobile	?0
sec-ch-ua-platform	"Windows"
upgrade-insecure-requests	1
user-agent	Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
accept	text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
sec-fetch-site	none
sec-fetch-mode	navigate
sec-fetch-user	?1
sec-fetch-dest	document
accept-encoding	gzip, deflate, br, zstd
accept-language	en
priority	u=0, i
```

The difference is significant and using DevTools order will immediately expose your script as non-browser traffic.

### Recommended Tools for Header Analysis

**Always use one of these tools to capture real header order:**

1. **Charles Web Proxy** - Industry standard for intercepting HTTP/HTTPS traffic
2. **powhttp.com** - New project that shows exact header order without modifying TLS fingerprint as much as Charles Web Proxy.

These tools show headers exactly as they're received by the server, giving you the accurate order needed for successful browser mimicry.

### Implementing Header Order in TLS Clients

The Go-based TLS clients mentioned previously support explicit header ordering through configuration:

```go
http.HeaderOrderKey: {"sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "upgrade-insecure-requests", "user-agent", "accept", "sec-fetch-site", "sec-fetch-mode", "sec-fetch-user", "sec-fetch-dest", "accept-encoding", "accept-language", "priority"},
http.PHeaderOrderKey: {":method", ":authority", ":scheme", ":path"},
```

This allows you to precisely control both pseudo-header order (`PHeaderOrderKey`) and regular header order (`HeaderOrderKey`) to match your target browser.

### Debugging with Diffchecker

When building scripts, always compare your requests against real browser traffic:

1. Capture browser headers using Charles Web Proxy
2. Capture your script's headers using Charles Web Proxy
3. Paste both into **diffchecker.com** to easily spot differences
4. Adjust your script's header order to match the browser exactly

This side-by-side comparison makes discrepancies immediately obvious and helps ensure perfect mimicry.

### Common Pitfalls to Avoid

#### 1. Header Case Sensitivity

A fundamental difference between HTTP/1.1 and HTTP/2 is header casing. HTTP/1.1 headers use title case (e.g., `User-Agent`, `Content-Type`), while HTTP/2 headers are always lowercase (e.g., `user-agent`, `content-type`). Using the wrong case for your protocol version will immediately expose your script as automated traffic.

#### 2. Setting Content-Length Manually

Never manually set the `Content-Length` header in your header list. HTTP clients handle this automatically, and including it manually will cause it to be sent twice, resulting in request failures.

#### 3. Mismatched sec-ch-ua Headers

The `sec-ch-ua` header is version-specific and must match your User-Agent exactly. For example:

* Chrome 137: `"Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"`
* Chrome 138: `"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"`

Using the wrong string will immediately flag your request as suspicious.

#### 4. Missing Cookie Header in Order

A critical oversight is omitting "cookie" from your header order configuration. When cookies are present but not explicitly ordered, they get appended at the end of the header list. This is problematic because in recent Chrome versions, the `priority` header comes **after** cookies in the proper order. If cookies appear at the end instead, it breaks the expected sequence and exposes the automation.

Always include "cookie" in the appropriate position within your `HeaderOrderKey`, even if you're not sending cookies initially.

#### 5. Having 'Disable cache' enabled in DevTools

This adds two headers that a normal user will never include. Make sure it is disabled when recording traffic from your Chrome browser.

#### 6. Hardcoding \`sec-ch-ua-full-version-list\`

Especially on sites protected by DataDome, you will see this header added to your requests after solving DataDome:

```
sec-ch-ua-full-version-list	"Not)A;Brand";v="8.0.0.0", "Chromium";v="138.0.7204.158", "Google Chrome";v="138.0.7204.158"
```

You should never hardcode this value. At Hyper Solutions, we return this value from our APIs.

#### 7. Having duplicate cookies

This is an issue we sometimes see with people trying to either manage their own cookies (by not using a cookiejar) or people using a badly implemented cookiejar. You should always verify that you are not sending the same cookie names with different values in the same request. Example shown below.<br>

<figure><img src="/files/aLHxZYKa0V6CTpijKT6Q" alt=""><figcaption></figcaption></figure>

#### 8. Using Charles's External Proxy Feature

Charles has a feature called External Proxy where you can still route your traffic through charles while also using a (residential) proxy to connect to the site. This works great but there is one issue, it moves the `Content-Length` header to the bottom of the headers, which will result in wrong header order comparison.

### Charles Web Proxy Alternative

While Charles Web Proxy is the gold standard, some websites may block it because Charles modifies the TLS fingerprint when intercepting HTTPS traffic. In these cases, **powhttp.com** serves as an excellent alternative that provides accurate header order analysis without significantly influencing TLS settings like local proxies sometimes do.

### Example using tls-client

Here's a practical example showing how to implement proper header ordering using the tls-client library:

```go
package main

import (
	"fmt"
	"io"
	"log"
	
	http "github.com/bogdanfinn/fhttp"
	"github.com/bogdanfinn/fhttp/cookiejar"
	tls_client "github.com/bogdanfinn/tls-client"
	"github.com/bogdanfinn/tls-client/profiles"
)

func main() {
	jar, _ := cookiejar.New(nil)
	options := []tls_client.HttpClientOption{
		tls_client.WithTimeoutSeconds(30),
		tls_client.WithClientProfile(profiles.Chrome_133),
		tls_client.WithNotFollowRedirects(),
		tls_client.WithCookieJar(jar),
		tls_client.WithRandomTLSExtensionOrder(),
	}
	
	client, err := tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
	if err != nil {
		log.Println(err)
		return
	}
	
	req, err := http.NewRequest(http.MethodGet, "https://tls.peet.ws/api/all", nil)
	if err != nil {
		log.Println(err)
		return
	}
	
	req.Header = http.Header{
		"sec-ch-ua":                 {`"Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"`},
		"sec-ch-ua-mobile":          {"?0"},
		"sec-ch-ua-platform":        {`"Windows"`},
		"upgrade-insecure-requests": {"1"},
		"user-agent":                {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"},
		"accept":                    {"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"},
		"sec-fetch-site":            {"none"},
		"sec-fetch-mode":            {"navigate"},
		"sec-fetch-user":            {"?1"},
		"sec-fetch-dest":            {"document"},
		"accept-encoding":           {"gzip, deflate, br, zstd"},
		"accept-language":           {"en"},
		"priority":                  {"u=0, i"},
		
		http.HeaderOrderKey: {
			"sec-ch-ua",
			"sec-ch-ua-mobile", 
			"sec-ch-ua-platform",
			"upgrade-insecure-requests",
			"user-agent",
			"accept",
			"sec-fetch-site",
			"sec-fetch-mode",
			"sec-fetch-user",
			"sec-fetch-dest",
			"accept-encoding",
			"accept-language",
			"priority",
		},
		http.PHeaderOrderKey: {":method", ":authority", ":scheme", ":path"},
	}
	
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()
	
	log.Println(fmt.Sprintf("status code: %d", resp.StatusCode))
	readBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(string(readBytes))
}
```

This example demonstrates:

* Using Chrome 133 profile with random TLS extension order
* Proper header casing (lowercase for HTTP/2)
* Correct header order matching Chrome's behavior
* Proper pseudo-header order configuration
* Chrome 137 compatible sec-ch-ua headers

### Example using Python tls-client

For Python users, there's a wrapper for bogdanfinn's tls-client. The most up-to-date fork is available at: <https://github.com/Nintendocustom/Python-Tls-Client> (original: <https://github.com/FlorianREGAZ/Python-Tls-Client>).

```python
import tls_client

# Create session with Chrome profile and random TLS extension order
session = tls_client.Session(
    client_identifier="chrome_133",
    random_tls_extension_order=True
)

# Set pseudo-header order for HTTP/2
session.pseudo_header_order = [":method", ":authority", ":scheme", ":path"]

# Define headers in the exact order Chrome sends them
headers = {
    "sec-ch-ua": '"Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "sec-fetch-site": "none",
    "sec-fetch-mode": "navigate", 
    "sec-fetch-user": "?1",
    "sec-fetch-dest": "document",
    "accept-encoding": "gzip, deflate, br, zstd",
    "accept-language": "en",
    "priority": "u=0, i"
}

# Configure header order to match Chrome exactly, 
# do this for every request before sending it
session.header_order = [
    "sec-ch-ua",
    "sec-ch-ua-mobile",
    "sec-ch-ua-platform", 
    "upgrade-insecure-requests",
    "user-agent",
    "accept",
    "sec-fetch-site",
    "sec-fetch-mode",
    "sec-fetch-user", 
    "sec-fetch-dest",
    "accept-encoding",
    "accept-language",
    "priority"
]

# Make the request
response = session.get("https://tls.peet.ws/api/all", headers=headers)

print(f"Status Code: {response.status_code}")
print(response.text)
```

This Python example demonstrates the same principles as the Go version:

* Chrome profile with random TLS extension order
* Exact header order matching browser behavior
* Proper pseudo-header configuration
* Chrome 137 compatible headers\
  \
  Get in touch: [discord.gg/akamai](https://discord.gg/akamai)


# Proxies & IP

Most Hyper Solutions APIs take your proxy's outbound IP as an input. This page explains why, and why you must use sticky (not rotating) proxies.

Most of our sensor APIs require your proxy's **outbound IP** as an input, because the anti-bot expects the generated payload to match the IP the target site actually sees. Fetch it from the `/ip` endpoint:

## Get IP from session

> Get IP from session

```json
{"openapi":"3.0.0","servers":[{"url":"https://ip.hypersolutions.co"}],"paths":{"/ip":{"get":{"summary":"Get IP from session","description":"Get IP from session","operationId":"getIp","parameters":[{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"responses":{"200":{"description":"OK IP was returned","content":{"application/json":{"schema":{"type":"object","properties":{"ip":{"type":"string","description":"The IP"}}}}}}}}}}}
```

## Use sticky proxies, never rotating

The IP you send to the API **must** match the IP the target sees. Rotating proxies break this: they issue a new IP on each new connection (`CONNECT`), so you get one IP from `/ip` and the target sees another.

1. You call `/ip` and get `1.2.3.4`.
2. You generate a payload for the target using that IP.
3. Your proxy rotates when it opens the connection to the target.
4. The target sees `5.6.7.8`, but the payload was built for `1.2.3.4`, so you're blocked.

**Sticky (session) proxies** hold the same IP for the whole session, so `/ip`, the API, and the target all agree.

## How to use it

1. Configure your proxy in **sticky/session** mode.
2. Call `/ip` through that proxy to get your outbound IP.
3. Pass that IP as the `ip` input on every sensor call in the session.

See [Core Requirements](/start-here/core-requirements) for the other non-negotiables.


# Installing Charles WebProxy

How to install Charles WebProxy, trust its root certificate and enable SSL proxying so you can inspect the HTTPS traffic your scraper sends.

## Downloading and installing

The steps will be similar on macOS and Linux, for simplicity we focus on Windows.\
Download the binary here: <https://www.charlesproxy.com/download/>

## Trusting Root Certificate

Charles needs to have it's root certificate trusted by your computer in order to decrypt TLS connections. Click on `Help -> SSL Proxying -> Install Charles Root Certificate` .

A new window will open, click on `Install Certificate...` and select either `Current User` or `Local Machine`. This only matters if you want to use Charles on multiple users. Click `Next` .

You then need to select the certificate store. This is really important, as Charles needs to be in the `Trusted Root Certification Authorities` folder. You can place it here by selecting `Place all certificates in the following store` and selecting the correct folder after clicking on `Browse.`\
\
Then click `Next` and `Finish`, it should show like this:\
![](/files/X4cKNX1do0PNtRqov5uA)

## Decrypting TLS

By default, Charles will not decrypt `https` traffic. In order for this to work you need to go to `Proxy -> SSL Proxying Settings` and enable `Enable SSL Proxying`, you must also add a wildcard to the `Include` section. After you're done it should look like this:\
![](/files/KMegjcgbF3SliIG9gj6T)

## Finished!

You're all set! You can now route your script's traffic through `http://127.0.0.1:8888` and have it show up in Charles. Or you can enable `Proxy -> Windows Proxy` to have your browser automatically route it's traffic through Charles.

{% hint style="info" %}
Pro tip: Route your browser with a Proxy profile so that it only shows traffic in Charles when you need to.
{% endhint %}

I recommend reading their official documentation here: <https://www.charlesproxy.com/documentation/> as there are much more features that I won't cover in-depth here.\
\
Get in touch: [discord.gg/akamai](https://discord.gg/akamai)


# Installing powhttp

How to install powhttp and route your scraper through it to capture real wire traffic, including true header order and TLS fingerprint, when debugging blocks.

powhttp is a local HTTP debugging proxy built for request-based scraping. Unlike browser DevTools or a HAR export, it captures your script's **real wire traffic**, including the true header order and TLS fingerprint your HTTP client actually sent. That makes it the most reliable way to see why a request is being blocked.

## Downloading and installing

Download powhttp and follow the install steps on their website: <https://powhttp.com/>

## Routing your script through powhttp

Once powhttp is running, it exposes a capture proxy at:

```
http://127.0.0.1:8080
```

Point your script's HTTP client at this proxy. powhttp can chain to an upstream scraping proxy, so you can keep your normal proxy configured and add powhttp in front of it. Run your failing flow so the requests are captured.

{% hint style="info" %}
powhttp captures details a HAR cannot, the real header order on the wire and the TLS ClientHello fingerprint. When a request "looks fine but still gets blocked," this is usually where you find the mismatch.
{% endhint %}

## Using powhttp with the Claude Code / Codex plugin

powhttp ships an MCP server that lets our [Claude Code plugin](/ai-plugins/claude-code-plugin) (or [Codex plugin](/ai-plugins/codex-plugin)) read your captured traffic and diagnose blocks for you.

1. In powhttp, open **Settings → MCP Server** and start it (there's an **Auto-start on app launch** option). It listens at `http://localhost:8383/mcp`.
2. Route your failing script through the capture proxy as described above and run it.
3. Ask the plugin to debug, for example: *"Capture my script with powhttp and tell me why my request is blocked."*

## Finished!

You can now inspect exactly what your client put on the wire and compare it to a real Chrome request. If you'd rather analyze a saved capture instead, see [Recording HAR files](/request-based-basics/recording-har-files-for-harvey).

For the full workflow — recording browser sessions, searching a capture to trace where dynamic tokens come from, diffing your request against the browser's until they match, replay, and routing through an external proxy — see the [powhttp guide](https://hypersolutions.co/blog/how-to-use-powhttp-web-scraping).

Get in touch: [discord.gg/akamai](https://discord.gg/akamai)


# Recording HAR files

This page explains how to record HAR files from your code for debugging and support. We explain how to do this with the recommended tools: Charles Webproxy and powhttp.

{% hint style="warning" %}
**Harvey is deprecated.** Our [Claude Code plugin](/ai-plugins/claude-code-plugin) and [Codex plugin](/ai-plugins/codex-plugin) analyze HAR files directly in your editor with the same rule set, capture live traffic via powhttp, and can fix your code for you. The recording steps below still apply: the plugins and our support team both work from HAR files.
{% endhint %}

### Prerequisites

Before continuing, make sure you have installed and configured one of the supported proxy tools:

* [Installing Charles WebProxy](/request-based-basics/installing-charles-webproxy)
* [Installing powhttp](/request-based-basics/installing-powhttp)

### Routing your script traffic

In order to record a HAR file, you first need to route your script's HTTP traffic through the proxy. You can do this by setting the proxy address in your code to your local proxy port:

* **Charles WebProxy:** `http://127.0.0.1:8888`
* **powhttp:** `http://127.0.0.1:8080` (default port, check your powhttp settings)

{% hint style="info" %}
Make sure your proxy is running **before** you start your script, otherwise the requests will fail or bypass the proxy entirely.
{% endhint %}

### Recording with Charles WebProxy

1. Run your script while Charles is open. You should see the requests appear in the session list.
2. Select the requests you want to export. You can select multiple by holding `Ctrl` (or `Cmd` on macOS) and clicking each request, or `Ctrl+A` to select all.
3. Right-click on the selected requests and click **Export Session...**

<figure><img src="/files/UYwdlRayRg0BZ7f16GjK" alt=""><figcaption></figcaption></figure>

4. In the save dialog, select **HTTP Archive (.har)** from the **Files of type** dropdown at the bottom.
5. Choose a location and save the file.

### Recording with powhttp

1. Run your script while powhttp is open. You should see the requests appear in the session list.
2. Click the **Download** button (⬇) in the toolbar.

<figure><img src="/files/0DgZe2eX1nsuV7Z4Z2UL" alt=""><figcaption></figcaption></figure>

3. Select **HAR 1.3 (recommended)** from the format options.
4. Choose a location and save the file.

{% hint style="info" %}
For more on getting the most out of powhttp — searching a capture, diffing requests, replay, and external proxies — see the [powhttp guide](https://hypersolutions.co/blog/how-to-use-powhttp-web-scraping).
{% endhint %}

### Submitting your HAR file

Once you have your `.har` file saved, you can attach it to your support ticket or send it to us via Discord. The HAR file contains all the request and response data we need to diagnose your issue.

{% hint style="warning" %}
HAR files may contain sensitive information such as cookies, tokens, and authorization headers. If you are concerned about sharing this data, let us know and we can guide you on how to sanitize the file before sending it.
{% endhint %}


# Getting started

This page explains the flow of generating sensor data and obtaining valid cookies for websites protected by Akamai Bot Manager.

## Sensor Data

If you're already familiar with Akamai Bot Manager challenges, you can either install one of our [SDKs & Examples](/start-here/readme-1) for easy integration, or head over to our [API Reference](/api-reference/akamai) if you want to handle the implementation yourself. The [Akamai Bypass API](https://hypersolutions.co/products/akamai) overview covers which challenges the endpoints solve and how they are priced.

### Understanding Akamai Protection

Akamai Bot Manager protects websites by requiring clients to generate and submit sensor data that proves they're legitimate browsers. This protection manifests as:

* A dynamically generated script endpoint embedded in protected pages
* An `_abck` cookie that gets validated when performing protected actions
* Cookie validation that occurs when accessing protected endpoints (login, add to cart, checkout, etc.)

The `_abck` cookie becomes valid after successfully posting sensor data. A cookie containing `~0~` indicates you can stop posting additional sensors, though not all sites use this indicator.

### Solution Flow

#### Step 1: Initial Page Request

Make a GET request to the protected page. This is typically the page users would naturally visit before performing the protected action (e.g., product page before add-to-cart).

**Critical:** You must use a TLS client that mimics Chrome and match the exact header order of real browsers. See [Core Requirements](/start-here/core-requirements) for the full list.

#### Step 2: Parse Script Endpoint

Extract the Akamai script endpoint from the HTML response. The script tag is typically located near the end of the body and contains a dynamically generated path:

```html
<script type="text/javascript" src="/yMOlMy/yS/3T/NVx6/a7xTRI1O5hJJ8/EDi7z45Ou1bfXb/dzldXmhnIQk/CjdBHQkD/Hn0" defer></script>
```

**Important:** This path is unique and dynamic - it cannot be hardcoded and must be parsed from each response.

{% tabs %}
{% tab title="Golang" %}

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

// Parse script path from HTML reader
scriptPath, err := akamai.ParseScriptPath(htmlReader)
if err != nil {
    // Handle parsing error
}
// scriptPath will be like: /yMOlMy/yS/3T/NVx6/a7xTRI1O5hJJ8/...
```

{% endtab %}

{% tab title="Python" %}

```python
from hyper_sdk.akamai import parse_script_path

script_path = parse_script_path(html_content)
# script_path will be like: /yMOlMy/yS/3T/NVx6/a7xTRI1O5hJJ8/...
```

{% endtab %}

{% tab title="JS / TS" %}

```javascript
import { parseAkamaiPath } from "hyper-sdk-js";

const scriptPath = parseAkamaiPath(htmlContent);
// scriptPath will be like: /yMOlMy/yS/3T/NVx6/a7xTRI1O5hJJ8/...
```

{% endtab %}
{% endtabs %}

#### Step 3: Fetch Script Content

Request the script content from the parsed endpoint. Save the entire response body as you'll need it for sensor generation.

Remember to:

* Use the same TLS client
* Include appropriate referer
* Maintain consistent cookie jar

#### Step 4: Generate Sensor Data

Use the Hyper Solutions API to generate sensor data. The sensor data simulates complex browser behavior and environment fingerprinting:

{% tabs %}
{% tab title="Golang" %}

```go
sensorData, sensorContext, err := session.GenerateSensorData(ctx, &hyper.SensorInput{
    PageUrl:        "https://www.example.com/product/example-item",
    UserAgent:      userAgent,
    Abck:           currentAbckCookie,  // Current _abck cookie value
    Bmsz:           bmSzCookie,         // bm_sz cookie value
    Version:        "3",                // Akamai version (usually "3")
    Script:         scriptContent,      // Full script content (first request only)
    Context:        sensorContext,      // Previous context (empty on first request)
    AcceptLanguage: "en-US,en;q=0.9",
    IP:             clientIP,           // Required: client IP address
})
if err != nil {
    // Handle error
}
```

{% endtab %}

{% tab title="Python" %}

```python
from hyper_sdk import SensorInput

sensor_data, sensor_context = session.generate_sensor_data(SensorInput(
    page_url="https://www.example.com/product/example-item",
    user_agent=user_agent,
    abck=current_abck_cookie,  # Current _abck cookie value
    bmsz=bm_sz_cookie,         # bm_sz cookie value
    version="3",               # Akamai version
    script=script_content,     # Full script content (first request only)
    context=sensor_context,    # Previous context (empty on first request)
    accept_language="en-US,en;q=0.9",
    ip=client_ip              # Required: client IP address
))
```

{% endtab %}

{% tab title="JS / TS" %}

```javascript
import { SensorInput, generateSensorData } from "hyper-sdk-js";

const result = await generateSensorData(session, new SensorInput(
    "https://www.example.com/product/example-item",  // pageUrl
    userAgent,
    currentAbckCookie,    // Current _abck cookie value
    bmSzCookie,          // bm_sz cookie value
    "3",                 // Akamai version
    scriptContent,       // Full script content (first request only)
    sensorContext,       // Previous context (empty on first request)
    "en-US,en;q=0.9",   // acceptLanguage
    clientIP            // Required: client IP address
));

const sensorData = result.payload;
const newSensorContext = result.context;
```

{% endtab %}
{% endtabs %}

**Important notes about sensor generation:**

* The `script` parameter is only needed on the first sensor generation
* The `context` parameter should be empty on first request, then use the returned context for subsequent requests
* Save the returned `sensorContext` for use in the next sensor generation

#### Step 5: Submit Sensor Data

POST the generated sensor data to the same script endpoint. The payload should be JSON formatted with a single `sensor_data` field:

```json
{"sensor_data":"[generated_sensor_data_string]"}
```

The response will update your `_abck` cookie through Set-Cookie headers.

#### Step 6: Validate and Repeat

Check if the updated `_abck` cookie indicates completion:

{% tabs %}
{% tab title="Golang" %}

```go
// Check for the ~0~ pattern (when available)
if strings.Contains(abckCookieValue, "~0~") {
    // Can stop posting sensors
}

// Or use the validation helper
isValid := akamai.IsCookieValid(abckCookieValue, requestCount)
```

{% endtab %}

{% tab title="Python" %}

```python
# Check for the ~0~ pattern (when available)
if "~0~" in abck_cookie_value:
    # Can stop posting sensors

# Or use the validation helper
is_valid = is_cookie_valid(abck_cookie_value, request_count)
```

{% endtab %}

{% tab title="JS / TS" %}

```javascript
// Check for the ~0~ pattern (when available)
if (abckCookieValue.includes("~0~")) {
    // Can stop posting sensors
}

// Or use the validation helper
const isValid = isAkamaiCookieValid(abckCookieValue, requestCount);
```

{% endtab %}
{% endtabs %}

**Sensor posting strategy:**

* If the cookie contains `~0~`, you can proceed to the protected action
* If the site doesn't use the `~0~` indicator, post exactly 3 sensors before proceeding
* Each subsequent sensor should NOT include the script content (only needed on first request)
* Each subsequent sensor MUST use the context returned from the previous generation

#### Step 7: Perform Protected Action

Once you have a valid `_abck` cookie (either containing `~0~` or after posting 3 sensors), you can proceed with the protected action.

**Important:** After performing a protected action, the `_abck` cookie typically becomes invalidated. You might need to generate new sensor data before the next protected action.

### Critical Implementation Requirements

#### TLS Client Configuration

**You MUST use a TLS client that:**

* Supports modern TLS cipher suites
* Can maintain exact header order
* Properly handles HTTP/2 or HTTP/1.1 as the target site requires
* Maintains consistent TLS fingerprint throughout the session

Using standard HTTP clients without proper TLS configuration will result in detection and blocking.

#### Header Order

**Header order is critical.** Akamai's detection system analyzes the exact order of HTTP headers. You must:

* Match the header order of real browsers
* Maintain consistent header order throughout all requests
* Use a client that allows precise header order control

#### Session Consistency

Throughout the entire flow, maintain:

* **Same User-Agent** for all requests
* **Same TLS fingerprint** across all connections
* **Proper cookie forwarding** between requests
* **Consistent client IP address** for all operations

### Best Practices

1. **Parse Dynamic Paths**: Never hardcode script endpoints - they change regularly and are unique per session
2. **Context Preservation**: Always save and reuse the sensor context between generations
3. **Script Caching**: The script content only needs to be fetched once per session (use it only for the first sensor)
4. **Retry Limits**: Post a maximum of 3 sensors - if unsuccessful, the issue is likely with your TLS client or header configuration
5. **Cookie Monitoring**: Check for cookie invalidation after each protected action
6. **IP Consistency**: Use the same IP address throughout the entire session

### Troubleshooting

#### Sensors Not Generating Valid Cookies

* Verify your TLS client configuration matches browser fingerprints
* Ensure header order exactly matches browser patterns
* Confirm the IP address is consistent and not blacklisted
* Check that you're properly parsing the dynamic script path

#### Immediate Detection/Blocking

* Your TLS fingerprint is likely incorrect or inconsistent
* Header order doesn't match expected browser patterns
* The client IP may be from a datacenter or known proxy range

#### Cookie Invalidates Too Quickly

* This is normal after protected actions - regenerate before each protected request
* Some sites invalidate after a specific number of requests regardless of actions
* Ensure you're not reusing invalidated cookies

For more detailed API documentation, refer to our [API Reference](/api-reference/akamai) or check our [SDKs & Examples](/start-here/readme-1) for your preferred programming language.

### Complete Example

For a full working implementation with proper TLS client setup, header ordering, and cookie handling, see our examples repository:

{% embed url="<https://github.com/Hyper-Solutions/hypersolutions-examples>" %}


# SBSD Introduction

SBSD (State Based Scraping Detection) is Akamai's passive sensor check. This page covers identifying basic SBSD and posting sensors to keep access valid.

#### Understanding Basic SBSD Protection

State Based Scraping Detection (SBSD) is an advanced bot protection mechanism used by Akamai. While our other guides cover handling SBSD challenges and 429 blocks, many websites implement SBSD in a passive mode that simply requires posting sensors proactively to maintain access. Our [Akamai Bypass API](https://hypersolutions.co/products/akamai) generates these sensors from a single HTTP call.

#### Identifying Basic SBSD

When you request a page with basic SBSD protection, you'll receive normal page content along with an SBSD script reference:

```html
<script src="/6mGXhhKgo3Cn/HH/EB0WcpIr3K/X0iuwmY3aY/UkZyWg/Dy/J1CmB4HUQ?v=99b02ce6-f91f-0f49-40ae-6f8493e30214"></script>
```

Note the absence of the `t` parameter - this distinguishes basic SBSD from hard challenges. The script only contains:

* **Path**: The script URL path
* **v parameter**: A UUID value
* **No t parameter**: This only appears in challenge scenarios

#### Implementation Flow

Basic SBSD protection follows a simple sequence:

1. **Initial Page Request**: Client requests the protected page
2. **Parameter Extraction**: Extract path and UUID from the script tag
3. **Script Fetch**: Request the SBSD script
4. **Sensor Submission**: Post two SBSD sensors (index 0 and 1)
5. **Continue Normally**: Proceed with your intended requests

#### Implementation Guide

**Step 1: Extract SBSD Parameters**

Parse the HTML response to extract the SBSD script parameters:

```javascript
const regex = /([a-z\d/\-_\.]+)\?v=([^"'&]+)/i;
const matches = html.match(regex);
const path = matches[1];
const uuid = matches[2];
```

**Step 2: Fetch the SBSD Script**

Request the script using the extracted parameters:

```http
GET /[path]?v=[uuid] HTTP/2
Headers...
```

Save the script content for use in sensor generation.

**Step 3: Generate and Submit First Sensor**

Using our API service, generate the first SBSD payload with index 0:

```json
POST /sbsd HTTP/1.1
Content-Type: application/json

{
  "userAgent": "Mozilla/5.0...",
  "uuid": "99b02ce6-f91f-0f49-40ae-6f8493e30214",
  "pageUrl": "https://example.com/",
  "o": "cookie_value",
  "script": "script_content_from_step_2",
  "ip": "your_ip_address",
  "acceptLanguage": "en-US,en;q=0.9",
  "index": 0
}
```

Submit the generated payload to the SBSD endpoint:

```http
POST /[path] HTTP/2
Content-Type: application/json

{
  "body": "GENERATED_PAYLOAD_INDEX_0"
}
```

**Step 4: Generate and Submit Second Sensor**

Immediately follow with the second sensor using index 1:

```json
POST /sbsd HTTP/1.1
Content-Type: application/json

{
  "userAgent": "Mozilla/5.0...",
  "uuid": "99b02ce6-f91f-0f49-40ae-6f8493e30214",
  "pageUrl": "https://example.com/",
  "o": "cookie_value",
  "script": "script_content_from_step_2",
  "ip": "your_ip_address",
  "acceptLanguage": "en-US,en;q=0.9",
  "index": 1
}
```

Submit to the same endpoint:

```http
POST /[path] HTTP/2
Content-Type: application/json

{
  "body": "GENERATED_PAYLOAD_INDEX_1"
}
```

**Step 5: Proceed with Protected Requests**

After posting both sensors, you can make requests to protected endpoints without triggering SBSD challenges.

#### Generating the sensor with the SDK

Instead of calling `/sbsd` directly, the SDKs wrap it in a single call. Generate each sensor (index `0`, then `1`) with an `SbsdInput`:

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

```go
sbsd, err := session.GenerateSbsdData(ctx, &hyper.SbsdInput{
    Index:          index,        // 0, then 1
    UserAgent:      userAgent,
    Uuid:           uuid,
    PageUrl:        pageURL,
    OCookie:        oCookie,       // sbsd_o (or bm_so) cookie value
    Script:         scriptContent,
    AcceptLanguage: acceptLanguage,
    IP:             ip,
})
if err != nil {
    // Handle the error
}
// POST {"body": sbsd} to the SBSD script path
```

{% endcode %}
{% endtab %}

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

```python
sbsd = session.generate_sbsd_data(hyper_sdk.SbsdInput(
    index=index,               # 0, then 1
    user_agent=user_agent,
    uuid=uuid,
    page_url=page_url,
    o_cookie=o_cookie,         # sbsd_o (or bm_so) cookie value
    script=script_content,
    accept_language=accept_language,
    ip=ip,
))
# POST {"body": sbsd} to the SBSD script path
```

{% endcode %}
{% endtab %}

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

```javascript
import { SbsdInput, generateSbsdPayload } from "hyper-sdk-js";

// Constructor is positional: (index, uuid, oCookie, pageUrl, userAgent, script, ip, acceptLanguage)
const sbsd = await generateSbsdPayload(
    session,
    new SbsdInput(index, uuid, oCookie, pageUrl, userAgent, scriptContent, ip, acceptLanguage)
);
// POST { body: sbsd } to the SBSD script path
```

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

#### Implementation Example

Here's a complete flow in pseudocode:

```javascript
// Step 1: Initial request
const response = await fetch("https://example.com/");
const html = await response.text();

// Step 2: Extract SBSD parameters
const regex = /([a-z\d/\-_\.]+)\?v=([^"'&]+)/i;
const matches = html.match(regex);

if (!matches) {
  // No SBSD protection, continue normally
  return;
}

const path = matches[1];
const uuid = matches[2];

// Step 3: Fetch SBSD script
const scriptUrl = `https://example.com${path}?v=${uuid}`;
const scriptResponse = await fetch(scriptUrl);
const scriptContent = await scriptResponse.text();

// Step 4: Post both sensors
const postUrl = `https://example.com${path}`;

for (let index = 0; index < 2; index++) {
  // Generate payload using our API
  const payload = await generatePayload({
    userAgent: "YOUR_USER_AGENT",
    uuid: uuid,
    pageUrl: "https://example.com/",
    oCookie: getCookie("sbsd_o") || getCookie("bm_so"),
    script: scriptContent,
    ip: yourIp,
    acceptLanguage: "en-US,en;q=0.9",
    index: index
  });
  
  // Submit sensor
  await fetch(postUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ body: payload })
  });
}

// Step 5: Continue with protected requests
const apiResponse = await fetch("https://example.com/api/data");
```

#### Key Differences from Challenge-Based SBSD

Basic SBSD protection differs from challenge scenarios:

1. **No blocking page** - Normal content loads immediately
2. **No t parameter** - Script URL only contains the v parameter
3. **Two sensors required** - Always post index 0 and index 1
4. **Proactive protection** - Sensors prevent future blocks rather than solving existing ones

#### Important Notes

1. **Cookie Management**: Use the `sbsd_o` or `bm_so` cookie value for the `o` parameter.
2. **Script Reuse**: The SBSD script content can be cached and reused for multiple sensor posts within the same session.
3. **Header Consistency**: Maintain consistent headers across all requests, including User-Agent and Accept-Language.
4. **Sensor Order**: Always post sensors in order (index 0 first, then index 1).

For handling blocking challenges or 429 responses with challenge tokens, refer to:

* [SBSD Challenge Flow](/akamai-web/sbsd-challenge-flow)
* [Handling 429 Status Codes](/akamai-web/handling-429-status-codes-with-sbsd-challenges)


# SBSD Challenge Flow

Websites using Akamai's active SBSD mode serve a challenge that must be solved before content is released. This page walks through that challenge flow.

### Understanding SBSD Protection

State Based Scraping Detection (SBSD) is an advanced bot protection mechanism used by Akamai to protect websites from scrapers. Websites protected by SBSD present a challenge that must be solved before allowing access to the protected content. Our [Akamai Bypass API](https://hypersolutions.co/products/akamai) solves this challenge from a single HTTP call.

### Challenge Flow Overview

The SBSD challenge follows a specific sequence of requests that must be executed in order:

1. **Initial Page Request**: Client makes a request to the protected website
2. **Challenge Page Response**: Server returns a challenge page with a script reference
3. **Script Request**: Client fetches the referenced script
4. **Payload Construction & Submission**: Client generates and submits the required payload
5. **Access Grant**: Upon successful verification, access to the website is granted

### Implementation Guide

#### Step 1: Initial Page Request

Make a standard GET request to the target website. When the site is protected by SBSD, instead of receiving the actual content, you'll receive a challenge page.

<pre class="language-html"><code class="lang-html"><strong>&#x3C;html>
</strong>   &#x3C;body>
      &#x3C;script src="/6mGXhhKgo3Cn/HH/EB0WcpIr3K/X0iuwmY3aY/UkZyWg/Dy/J1CmB4HUQ?v=99b02ce6-f91f-0f49-40ae-6f8493e30211&#x26;t=183446611">&#x3C;/script>
      &#x3C;script>
         (function() {
             var chlgeId = '';
             var scripts = document.getElementsByTagName('script');
             for (var i = 0; i &#x3C; scripts.length; i++) {
                 if (scripts[i].src &#x26;&#x26; scripts[i].src.match(/t=([^&#x26;#]*)/)) {
                     chlgeId = scripts[i].src.match(/t=([^&#x26;#]*)/)[1];
                 }
             }
             var proxied = window.XMLHttpRequest.prototype.send;
             window.XMLHttpRequest.prototype.send = function() {
                 var pointer = this
                 var intervalId = window.setInterval(function() {
                     if (pointer.readyState === 4 &#x26;&#x26; pointer.responseURL &#x26;&#x26; pointer.responseURL.indexOf('t=' + chlgeId) > -1) {
                         location.reload(true);
                         clearInterval(intervalId);
                     }
                 }, 1);
                 return proxied.apply(this, [].slice.call(arguments));
             };
         })();
      &#x3C;/script>
   &#x3C;/body>
&#x3C;/html>
                                    
</code></pre>

#### Step 2: Identify Challenge Signature

The response will contain an HTML page with a script tag that has essential parameters for solving the challenge. You need to extract:

* **Path**: The script URL path
* **v parameter**: A UUID value
* **t parameter**: A challenge token

Example script tag:

```html
<script src="/6mGXhhKgo3Cn/HH/EB0WcpIr3K/X0iuwmY3aY/UkZyWg/Dy/J1CmB4HUQ?v=99b02ce6-f91f-0f49-40ae-6f8493e30214&t=183446612"></script>
```

From this example:

* **Path**: `/6mGXhhKgo3Cn/HH/EB0WcpIr3K/X0iuwmY3aY/UkZyWg/Dy/J1CmB4HUQ`
* **v parameter**: `99b02ce6-f91f-0f49-40ae-6f8493e30214`
* **t parameter**: `183446612`

Implement a regular expression to extract these values:

```javascript
const regex = /([a-z\d/\-_\.]+)\?v=(.*?)(?:&.*?t=(.*?))?["']/i;
const matches = html.match(regex);
const path = matches[1];
const v = matches[2];
const t = matches[3] || "";
```

#### Step 3: Fetch Challenge Script

Request the script using the extracted components:

```http
GET /[path]?v=[v_parameter]&t=[t_parameter] HTTP/2
Headers...
```

You'll need to save this script content for use in the next step.

#### Step 4: Generate and Submit Payload

Using our API service, generate the SBSD payload by providing:

1. The extracted UUID (v parameter)
2. The page URL
3. The script content
4. Your User-Agent
5. Any existing sbsd\_o cookie value, or the bm\_so cookie value if sbsd\_o is not present.

```
POST /sbsd HTTP/1.1
Content-Type: application/json

{
  "userAgent": "Mozilla/5.0...",
  "uuid": "99b02ce6-f91f-0f49-40ae-6f8493e30214",
  "pageUrl": "https://example.com/",
  "o": "existing_sbsd_o_cookie_value_if_any",
  "script": "script_content_from_step_3",
  "ip": "your ipv4 or ipv6 address",
  "acceptLanguage": "en-US,en;q=0.9"
}
```

Our API will return the properly formatted payload string.

#### Step 5: Submit the Solution

POST the generated payload to the challenge endpoint:

```http
POST /[path]?t=[t_parameter] HTTP/2
Headers...

{
  "body": "YOUR_GENERATED_PAYLOAD"
}
```

#### Step 6: Access Protected Content

If the payload is correct, you can now make requests to the protected website and receive the actual content instead of the challenge page.

```http
GET / HTTP/2
Headers...
```

### Implementation Example

Here's a pseudocode example showing the complete flow:

```javascript
// Step 1: Initial request
const initialResponse = fetch("https://example.com/");
const html = await initialResponse.text();

// Step 2: Extract challenge parameters
const regex = /([a-z\d/\-_\.]+)\?v=(.*?)(?:&.*?t=(.*?))?["']/i;
const matches = html.match(regex);
const path = matches[1];
const v = matches[2];
const t = matches[3] || "";

// Step 3: Fetch script
const scriptUrl = `https://example.com${path}?v=${v}&t=${t}`;
const scriptResponse = await fetch(scriptUrl);
const scriptContent = await scriptResponse.text();

// Step 4: Generate payload using our API
const payload = await generatePayload({
  userAgent: "YOUR_USER_AGENT",
  uuid: v,
  pageUrl: "https://example.com/",
  oCookie: getCookie("sbsd_o") || getCookie("bm_so"),
  script: scriptContent,
  ip: yourIp,
  acceptLanguage: yourAcceptLanguage
});

// Step 5: Submit payload
const submitUrl = `https://example.com${path}?t=${t}`;
await fetch(submitUrl, {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ body: payload })
});

// Step 6: Access protected content
const protectedContent = await fetch("https://example.com/");
```

### Important Notes

1. **Keep Headers Consistent**: Align your headers with Chrome using a TLS client.
2. **Header Order Matters**: Akamai verifies header ordering on all requests
3. **Cookie Management**: Properly store and reuse any cookies set by the server

### Integration with Our API

Our API simplifies the most complex part of this process - generating the correct payload. By providing the necessary parameters to our service, you receive a properly formatted payload ready for submission.

For detailed integration instructions and API endpoints, refer to our [API Reference Documentation](/api-reference/akamai).


# Handling 429 Status Codes with SBSD Challenges

A 429 Too Many Requests from an SBSD protected API is a challenge, not a rate limit. This page shows how to extract the challenge token and post a fresh SBSD payload.

### Understanding 429 SBSD Blocks

When interacting with APIs protected by SBSD, you may occasionally encounter a `429 Too Many Requests` status code. Instead of the expected API response, you'll receive a JSON response containing a challenge token:

```json
{
  "t": "183446612"
}
```

This indicates that the SBSD protection system has triggered a challenge that must be solved before you can continue making requests to the API.

### Solution Process

Solving a 429 SBSD block follows a similar process to the standard SBSD challenge flow explained in our [SBSD Challenge Flow Documentation](/akamai-web/sbsd-challenge-flow), but with a simplified approach:

#### Step 1: Extract the Challenge Token

From the 429 response, extract the `t` value (challenge token):

```javascript
const response = await fetch("https://example.com/api/resource");
if (response.status === 429) {
  const data = await response.json();
  const challengeToken = data.t;  // In our example: "183446612"
  
  // Proceed to solve the challenge
}
```

#### Step 2: Construct the Challenge URL Path

For a 429 response, you'll need to use the same script path and `v`parameter as in previous successful SBSD solves. You must store this information before making the API request. You can also reuse the script content you have requested previously.

#### Step 3: Generate a New Payload

Use our API to generate a fresh SBSD payload:

```javascript
const payload = await generatePayload({
  userAgent: "YOUR_USER_AGENT",
  uuid: "YOUR_STORED_UUID",  // Use the UUID from a previous challenge
  pageUrl: "https://example.com/",
  oCookie: getCookie("sbsd_o") || getCookie("bm_so"),
  script: scriptContent,
  ip: yourIp,
  acceptLanguage: yourAcceptLanguage
});
```

#### Step 4: Submit the Solution

POST the generated payload to the challenge endpoint:

```javascript
const submitUrl = `https://example.com${scriptPath}?t=${challengeToken}`;
await fetch(submitUrl, {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ body: payload })
});
```

#### Step 5: Retry Your Original API Request

Once the challenge is solved, retry your original API request. It should now proceed normally:

```javascript
const retryResponse = await fetch("https://example.com/api/resource");
// Process the successful response
```

### Complete Example

Here's a pseudocode example showing how to handle a 429 SBSD challenge in your API requests:

```javascript
async function fetchWithSbsdHandling(url, options = {}) {
  let response = await fetch(url, options);
  
  // Check if we received a 429 with a challenge token
  if (response.status === 429) {
    try {
      const data = await response.json();
      
      if (data.t) {
        // Extract the challenge token
        const challengeToken = data.t;

        // Generate the payload using our API
        const payload = await generatePayload({
          userAgent: options.headers['User-Agent'],
          uuid: getStoredUuid(),
          pageUrl: new URL(url).origin,
          oCookie: getCookie("sbsd_o") || getCookie("bm_so"),
          script: scriptContent,
          ip: yourIp,
          acceptLanguage: yourAcceptLanguage
        });
        
        // Submit the solution
        const submitUrl = `${new URL(url).origin}${scriptPath}?t=${challengeToken}`;
        await fetch(submitUrl, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "User-Agent": options.headers['User-Agent'],
            ... more headers
          },
          body: JSON.stringify({ body: payload })
        });
        
        // Retry the original request
        return fetch(url, options);
      }
    } catch (error) {
      console.error("Failed to solve SBSD challenge:", error);
    }
  }
  
  return response;
}
```

### Implementation Best Practices

1. **Store Challenge Information**: Save script paths and UUIDs from previous challenges.
2. **Automatic Retries**: Implement automatic SBSD challenge solving and request retries in your API client.
3. **Consistent Headers**: Maintain the same headers and match header order throughout the entire challenge solving process.
4. **Session Management**: Properly store and forward cookies between requests to maintain session state.

### Integrating with Our API

Our API service simplifies the payload generation process.

For detailed API integration instructions and complete documentation on our payload generation service, refer to our [API Reference Documentation](/api-reference/akamai).


# Handling 428 Status Code (SEC-CPT)

Akamai returns 428 Precondition Required when a SEC-CPT challenge is active. This page covers the crypto, behavioral and adaptive providers and how to solve each one.

### Understanding SEC-CPT Blocks

When interacting with APIs protected by Akamai, you may encounter a `428 Precondition Required` status code. This indicates that you have triggered a challenge that must be solved before you can continue making requests.

The challenge response contains a JSON payload with provider-specific information:

```json
{
  "sec-cp-challenge": "true",
  "provider": "crypto",
  ...
}
```

The `provider` field determines which challenge flow you need to follow. The three providers are:

* **crypto** - A proof-of-work challenge with a mandatory wait duration
* **behavioral** - A behavioral analysis challenge requiring normal sensor data
* **adaptive** - A combined challenge requiring both proof-of-work and sensor data submission

#### Key Cookie

The `sec_cpt` cookie is the primary indicator of challenge status. A successfully solved challenge will result in a `sec_cpt` cookie containing `~3~` in its value.

***

### Crypto Provider

The crypto provider implements a proof-of-work challenge with a **mandatory wait duration that cannot be bypassed**.

#### Challenge Response Structure

When you receive a crypto challenge, the response contains:

| Field                  | Description                                                                    |
| ---------------------- | ------------------------------------------------------------------------------ |
| `sec-cp-challenge`     | Always `"true"` indicating an active challenge                                 |
| `provider`             | `"crypto"` for this challenge type                                             |
| `branding_url_content` | Path to the challenge page (e.g., `/_sec/cp_challenge/crypto_message-4-3.htm`) |
| `chlg_duration`        | **Mandatory wait time in seconds**                                             |
| `token`                | Challenge token for payload generation                                         |
| `timestamp`            | Server timestamp                                                               |
| `nonce`                | Cryptographic nonce                                                            |
| `difficulty`           | Proof-of-work difficulty parameter                                             |
| `timeout`              | Challenge timeout value                                                        |

#### Solution Flow

**Step 1: Parse the Challenge**

Extract the challenge data from the 428 response. The response can come in two formats:

* **HTML format**: Contains an iframe with `challenge` attribute (base64-encoded JSON), `data-duration` attribute, and `src` attribute for the challenge path
* **JSON format**: Direct JSON response with all challenge parameters

**Step 2: Wait the Required Duration**

You **must** wait for the duration specified in `chlg_duration` (or `data-duration` in HTML format). This wait time is enforced server-side and cannot be bypassed or shortened.

**Step 3: Generate and Submit the Proof-of-Work Payload**

After waiting, generate the proof-of-work payload containing:

* The challenge `token`
* Computed `answers` based on the challenge parameters (nonce, timestamp, difficulty)

Submit this payload via POST to `/_sec/verify?provider=crypto` on the target domain.

**Step 4: Verify the Challenge**

Make a GET request to `/_sec/cp_challenge/verify` to complete the verification process.

**Step 5: Validate Success**

Check that the `sec_cpt` cookie now contains `~3~` in its value. If not, the challenge was not successfully solved.

***

### Behavioral Provider

The behavioral provider requires sensor data submission, similar to standard Akamai sensor flow but with a different endpoint structure.

#### Challenge Response Structure

```json
{
  "sec-cp-challenge": "true",
  "provider": "behavioral",
  "branding_type": "custom_branding",
  "branding_cust_url": "/challenge.html",
  "verify_url": "fwrjQWEM/6OcbaAS/TQkjRzC/-A/wXa1S2iYkY/IBwoXw/AD5PIWQF/GUEB"
}
```

| Field               | Description                                              |
| ------------------- | -------------------------------------------------------- |
| `sec-cp-challenge`  | Always `"true"` indicating an active challenge           |
| `provider`          | `"behavioral"` for this challenge type                   |
| `branding_type`     | Branding configuration type                              |
| `branding_cust_url` | Path to the challenge branding page                      |
| `verify_url`        | **Dynamic verification URL path** (unique per challenge) |

#### Solution Flow

**Step 1: Fetch the Branding Page**

Make a GET request to the `branding_cust_url` path (e.g., `/challenge.html`) on the target domain. This page contains the script endpoint needed for sensor submission.

**Step 2: Extract and Fetch the Script**

Parse the branding page response to locate the Akamai script endpoint. Make a GET request to fetch the script content - this is required for sensor generation.

**Step 3: Submit Sensor Data**

Generate and POST sensor data to the script endpoint. Key considerations:

* Use a **fresh sensor context** for this challenge (do not reuse context from previous requests)
* While one sensor POST is often sufficient, implement a loop of up to **3 sensor posts**
* Break out of the loop early if the `sec_cpt` cookie is set (indicates sufficient sensor data was received)
* Include the `_abck` cookie value in sensor generation

**Step 4: Verify the Challenge**

Make a GET request to the `verify_url` path returned in the original challenge response. Note that this is a **dynamic path** unique to each challenge, not the static `/_sec/cp_challenge/verify` endpoint used by the crypto provider.

**Step 5: Validate Success**

Confirm that the `sec_cpt` cookie contains `~3~` in its value.

***

### Adaptive Provider

The adaptive provider combines both proof-of-work and sensor data submission into a single sequential flow. It effectively merges the crypto and behavioral challenge types: you must first complete the proof-of-work step, then submit sensor data, and finally verify.

#### Challenge Response Structure

```json
{
  "sec-cp-challenge": "true",
  "provider": "adaptive",
  "chlg_duration": 30,
  "branding_type": "custom_branding",
  "branding_cust_url": "/challenge-assets/v7/captcha.html",
  "token": "<challenge_token>",
  "timestamp": 1772786267,
  "nonce": "0f7c9e91cbd8ab5f6008",
  "difficulty": 15000,
  "count": 1,
  "timeout": 1000,
  "verify_url": "<dynamic_url>"
}
```

| Field               | Description                                                                 |
| ------------------- | --------------------------------------------------------------------------- |
| `sec-cp-challenge`  | Always `"true"` indicating an active challenge                              |
| `provider`          | `"adaptive"` for this challenge type                                        |
| `chlg_duration`     | **Mandatory wait time in seconds**                                          |
| `branding_type`     | Branding configuration type                                                 |
| `branding_cust_url` | Path to the challenge branding page                                         |
| `token`             | Challenge token for payload generation                                      |
| `timestamp`         | Server timestamp                                                            |
| `nonce`             | Cryptographic nonce                                                         |
| `difficulty`        | Proof-of-work difficulty parameter                                          |
| `count`             | Number of proof-of-work answers required                                    |
| `timeout`           | Challenge timeout value                                                     |
| `verify_url`        | Present in response but **not used**; verification uses the static endpoint |

#### Solution Flow

**Step 1: Parse the Challenge**

Extract the challenge data from the 428 response. Note that adaptive challenges include fields from both crypto (token, nonce, difficulty, count) and behavioral (branding\_cust\_url) providers.

**Step 2: Wait the Required Duration**

You **must** wait for the duration specified in `chlg_duration`. As with the crypto provider, this wait time is enforced server-side and cannot be bypassed or shortened.

**Step 3: Generate and Submit the Proof-of-Work Payload**

After waiting, generate the proof-of-work payload containing:

* The challenge `token`
* Computed `answers` based on the challenge parameters (nonce, timestamp, difficulty)
* The number of answers must match the `count` field (e.g., if `count` is 1, provide one answer)

Submit this payload via POST to `/_sec/verify?provider=adaptive` on the target domain.

Example payload:

```json
{
  "token": "<challenge_token>",
  "answers": ["0.66463d05840cd"]
}
```

**Step 4: Submit Sensor Data**

After completing the proof-of-work step, proceed with the sensor data submission flow:

1. Fetch the branding page at the `branding_cust_url` path
2. Extract and fetch the Akamai script endpoint from the branding page
3. Generate and POST sensor data to the script endpoint in a loop of up to **3 sensor posts**
4. Break out of the loop early if the `sec_cpt` cookie is set

This follows the same sensor submission process as the behavioral provider: use a fresh sensor context and include the `_abck` cookie value in sensor generation.

**Step 5: Verify the Challenge**

Make a GET request to `/_sec/cp_challenge/verify` (the static verification endpoint). Note that unlike the behavioral provider, the adaptive provider uses the **static** verify endpoint rather than the dynamic `verify_url` from the challenge response.

A successful response will contain:

```json
{"success": "true"}
```

**Step 6: Validate Success**

Confirm that the `sec_cpt` cookie contains `~3~` in its value.

***

### Implementation Best Practices

#### Header Ordering

Maintaining correct header order is critical for all challenge types. Akamai's protection systems analyze header ordering as part of their fingerprinting. Always ensure your HTTP client preserves the exact header order you specify.

#### Cookie Management

* **`sec_cpt`**: Primary challenge status cookie - monitor for `~3~` to confirm success
* **`bm_sz`**: Akamai bot manager cookie - required for sensor generation
* **`_abck`**: Akamai bot manager cookie - required for sensor generation

Properly store and forward cookies between all requests to maintain session state.

#### Session Consistency

* Use the same User-Agent throughout the entire challenge flow
* Maintain consistent client hints (`sec-ch-ua`, `sec-ch-ua-mobile`, `sec-ch-ua-platform`)
* Keep TLS fingerprint consistent across all requests

#### Handling Challenges in API Flows

SEC-CPT challenges can appear in two contexts:

1. **Initial page load**: The challenge appears when first accessing a protected page
2. **During API calls**: A previously valid session may receive a 428 response, requiring challenge resolution before retrying the original request

Implement automatic challenge detection and solving in your API client to handle both scenarios seamlessly.

#### Error Handling

* If the `sec_cpt` cookie does not contain `~3~` after completing the flow, the challenge was not successfully solved
* For behavioral challenges, if sensor posts don't result in a `sec_cpt` cookie after 3 attempts, the implementation is most likely incorrect
* For adaptive challenges, ensure both the proof-of-work submission and sensor posts complete successfully, failure in either phase will prevent verification
* For crypto challenges, ensure you wait the **full duration** before submitting - premature submission will fail

***

### Quick Reference

| Aspect            | Crypto Provider                      | Behavioral Provider                 | Adaptive Provider                                  |
| ----------------- | ------------------------------------ | ----------------------------------- | -------------------------------------------------- |
| Wait Required     | Yes (mandatory)                      | No                                  | Yes (mandatory)                                    |
| Proof-of-Work     | Yes                                  | No                                  | Yes                                                |
| Sensor Posts      | No                                   | Yes (1-3 posts)                     | Yes (1-3 posts, after proof-of-work)               |
| POST Endpoint     | `/_sec/verify?provider=crypto`       | Script endpoint from branding page  | `/_sec/verify?provider=adaptive` + script endpoint |
| Verify Endpoint   | `/_sec/cp_challenge/verify` (static) | Dynamic `verify_url` from challenge | `/_sec/cp_challenge/verify` (static)               |
| Success Indicator | `sec_cpt` contains `~3~`             | `sec_cpt` contains `~3~`            | `sec_cpt` contains `~3~`                           |
| Typical Flow      | Wait → POST PoW → Verify             | Fetch branding → Sensors → Verify   | Wait → POST PoW → Sensors → Verify                 |


# Getting started

Incapsula (now Imperva) guards enterprise sites with the reese84 sensor and the \_\_\_utmvc cookie. This page explains how the pieces fit together.

Incapsula, now **Imperva**, guards login, checkout, and API endpoints across enterprise sites. It fingerprints the runtime with **reese84**, gates access behind the **`___utmvc`** cookie, and can escalate to a captcha when trust drops. Our [Incapsula & Imperva Bypass API](https://hypersolutions.co/products/incapsula) reproduces the reese84 and `___utmvc` payloads from a single HTTP call, so you don't have to run the obfuscated challenge scripts in a browser.

### reese84 vs UTMVC

These are two separate layers. Some sites use one, some use both:

* **reese84** is the core sensor: a signed JavaScript fingerprint payload posted back before the site issues a valid `reese84` token. It comes in two forms:
  * **Static** (background sensor, no challenge page), see [Reese84](/incapsula/reese84).
  * **Dynamic** (a "Pardon Our Interruption" page, often with a Proof of Work step), see [Reese84 Dynamic](/incapsula/reese84-dynamic).
* **UTMVC** is a separate challenge layer: a rotating, obfuscated script that, when executed correctly, issues the `___utmvc` cookie that authorizes the session. See [UTMVC](/incapsula/utmvc).

If trust is low, Incapsula can fall back to an **hCaptcha or GeeTest** challenge inside an iframe. Hyper Solutions does not solve those, see [Incapsula Captcha Block](/incapsula/incapsula-captcha-block).

### Which protection does my site use?

**UTMVC**: the page loads a script that looks like this:

```
/_Incapsula_Resource?SWJIYLWA=...
```

**reese84**: the browser holds a cookie named `reese84`, or you see an `x-d-token` header on requests. If the site serves a **"Pardon Our Interruption"** page, it's the [dynamic](/incapsula/reese84-dynamic) variant.

### FAQ

**My requests still get blocked even though the payload looks valid.** The usual culprits are an **IP mismatch** (the `ip` you pass must match your egress IP, use a sticky proxy), a **rotated User-Agent**, or an **unhandled UTMVC or captcha layer**. See [Core Requirements](/start-here/core-requirements).

**Do I need to solve the captcha?** Only if the site escalates to one. Hyper Solutions handles reese84 and `___utmvc`; captcha blocks require a third-party solver, see [Incapsula Captcha Block](/incapsula/incapsula-captcha-block).

**How long is a token good for?** The reese84 response includes `renewInSec`. Renew before it expires rather than re-solving the whole flow each time.

### Complete Example

For a full working implementation with proper TLS client setup, header ordering, and cookie handling, see our examples repository:

{% embed url="<https://github.com/Hyper-Solutions/hypersolutions-examples>" %}


# Reese84

Reese84 is Imperva / Incapsula's core bot check: a hidden JavaScript sensor that expects a signed payload before it issues a valid reese84 token.

Reese84 is the core Incapsula / Imperva check. A hidden JavaScript sensor collects device, canvas, and timing entropy and expects a **signed reese84 payload** posted back before it issues a valid `reese84` token. Our [Incapsula & Imperva Bypass API](https://hypersolutions.co/products/incapsula) generates that payload for you from a single HTTP call.

A site uses reese84 if the browser holds a cookie named **`reese84`**, or if you see an **`x-d-token`** header on its requests.

{% hint style="info" %}
This page covers the **static** reese84 sensor that runs in the background with no challenge page. If the site instead shows a **"Pardon Our Interruption"** page, it uses the dynamic variant with an extra Proof of Work step, see [Reese84 Dynamic](/incapsula/reese84-dynamic).
{% endhint %}

#### Locating the Script Path

The reese84 script is served from an obscure path unique to each site. Find it by inspecting network requests in your browser's developer tools and looking for POST requests whose URL contains `?d=`, these are sensor submissions. The corresponding script path is **static per site**, so you only need to find it once.

#### Implementation Steps

**Step 1: Fetch the Script Content**

Make a GET request to the script URL (the path you located above, including its query parameters). Store the full response body, our API requires the script content to generate a valid sensor.

```http
GET /path/to/reese84/script?s=... HTTP/2
Chrome: Headers
Accept: */*
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
```

**Step 2: Generate the Sensor**

Pass the script content and your session inputs to our API to generate the sensor. The SDKs wrap this in a single call:

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

```go
sensor, err := session.GenerateReese84Sensor(ctx, &hyper.ReeseInput{
    UserAgent:      userAgent,
    AcceptLanguage: "en-US,en;q=0.9",
    IP:             ip,          // must match your egress IP
    ScriptUrl:      scriptUrl,   // full script URL, including ?s=...
    PageUrl:        pageUrl,
    Script:         scriptBody,  // full body from Step 1
    // Pow is only used by the dynamic variant, leave empty here
})
if err != nil {
    // Handle the error
}
```

{% endcode %}
{% endtab %}

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

```python
sensor = session.generate_reese84_sensor(hyper_sdk.ReeseInput(
    user_agent=user_agent,
    accept_language="en-US,en;q=0.9",
    ip=ip,                  # must match your egress IP
    script_url=script_url,  # full script URL, including ?s=...
    pageUrl=page_url,       # note: this kwarg is camelCase in the Python SDK
    script=script_body,     # full body from Step 1
))
```

{% endcode %}
{% endtab %}

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

```javascript
import { Reese84Input, generateReese84Sensor } from "hyper-sdk-js";

// Constructor is positional: (userAgent, ip, acceptLanguage, pageUrl, script, scriptUrl, pow?)
const sensor = await generateReese84Sensor(
    session,
    new Reese84Input(userAgent, ip, "en-US,en;q=0.9", pageUrl, scriptBody, scriptUrl)
);
```

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

{% hint style="warning" %}
The input class is named **`ReeseInput`** in Go and Python, but **`Reese84Input`** in JS/TS.
{% endhint %}

The input fields:

| Field (Go)       | Python            | JS ctor arg      | Description                                                                                     |
| ---------------- | ----------------- | ---------------- | ----------------------------------------------------------------------------------------------- |
| `UserAgent`      | `user_agent`      | `userAgent`      | Your browser's User-Agent (consistent across all requests)                                      |
| `AcceptLanguage` | `accept_language` | `acceptLanguage` | Your `Accept-Language` header value                                                             |
| `IP`             | `ip`              | `ip`             | Your public IP, which **must** match the egress IP the target sees                              |
| `ScriptUrl`      | `script_url`      | `scriptUrl`      | The full script URL from Step 1 (with `?s=...`)                                                 |
| `PageUrl`        | `pageUrl`         | `pageUrl`        | The URL of the protected page (note: camelCase in the Python SDK)                               |
| `Script`         | `script`          | `script`         | The full script body from Step 1                                                                |
| `Pow`            | `pow`             | `pow`            | Proof of Work value, empty unless the [dynamic](/incapsula/reese84-dynamic) variant requires it |

If you'd rather implement the API call yourself, see the [API Reference](/api-reference/incapsula).

**Step 3: Submit the Sensor**

Submit the generated sensor to the script path with `?d=yourdomain.com` appended:

```http
POST /path/to/reese84/script?d=www.example.com HTTP/2
Chrome: Headers
Content-Type: text/plain; charset=utf-8

[YOUR_GENERATED_SENSOR]
```

**Step 4: Store the Cookie**

Parse the token from the response and save it as a cookie named `reese84`:

```json
{
  "token": "3:abc123...",
  "renewInSec": 896,
  "cookieDomain": "www.example.com"
}
```

Set the cookie with the domain from the response, then use it in subsequent requests to the protected site.

#### Notes

* The script path is static per site, locate it once and reuse it.
* The token expires after `renewInSec` seconds. Implement renewal before expiration.
* Use consistent headers, User-Agent, and TLS fingerprint across all requests.


# Reese84 Dynamic

Reese84 Dynamic is used by sites that show a "Pardon Our Interruption" challenge page. Clients must solve the challenge and submit a valid payload to access the protected content.

Our [Incapsula & Imperva Bypass API](https://hypersolutions.co/products/incapsula) solves the dynamic challenge, including the Proof of Work step, from a single HTTP call.

#### Challenge Flow Overview

The Reese challenge follows this sequence:

1. **Initial Request**: Client requests a protected page and receives a challenge page
2. **Extract Script URL**: Parse the challenge page HTML to find the Reese84 script path
3. **Fetch Script Content**: GET the script URL and store the full response body (required by the API)
4. **PoW Request** (if required): Client retrieves the Proof of Work value
5. **Payload Generation**: Client uses our API to generate the correct payload
6. **Payload Submission**: Client submits the payload to the challenge endpoint
7. **Access Granted**: Upon verification, client can access the protected content

#### Implementation Guide

**Step 1: Initial Request & Challenge Detection**

When you make a request to a protected resource, you'll receive a "Pardon Our Interruption" page instead of the expected content:

```http
GET / HTTP/2
Chrome: Headers
```

The response will contain HTML with a script tag that includes essential parameters:

```html
<script>
  if (!isSpa) {
    var scriptElement = document.createElement('script');
    scriptElement.type = "text/javascript";
    scriptElement.src = "/onalbaine-legeance-what-come-Womany-Malcome-to-o/14167535692918208311?s=xcUvM9nI";
    scriptElement.async = true;
    scriptElement.defer = true;
    document.head.appendChild(scriptElement);
  }
</script>
```

**Step 2: Extract Script URL**

Extract the **script path** and **full URL** from the challenge page. You need two values:

* **Script path** (without query params), used later for the PoW and payload submission endpoints
* **Full script path** (with query params), used to fetch the script content

{% hint style="info" %}
The SDKs do both in a single call: `ParseDynamicReeseScript(html, url)` (Go) / `parse_dynamic_reese_script(html, url)` (Python) / `parseDynamicReeseScript(html, urlStr)` (JS/TS). It returns `(sensorPath, scriptPath)`, where `sensorPath` is already suffixed with `?d=<hostname>`. The manual regexes below are only needed if you parse it yourself.
{% endhint %}

```javascript
// Extract the script path (without query params) for the POST endpoints
const pathRegex = /src\s*=\s*"(\/[^/]+\/[^?]+)\?.*"/;
const pathMatches = pathRegex.exec(htmlContent);
const scriptPath = pathMatches[1];
// e.g., "/onalbaine-legeance-what-come-Womany-Malcome-to-o/14167535692918208311"

// Extract the full script path (with query params) for fetching
const fullPathRegex = /scriptElement\.src\s*=\s*"(.*?)"/;
const fullMatches = fullPathRegex.exec(htmlContent);
const fullScriptPath = fullMatches[1];
// e.g., "/onalbaine-legeance-what-come-Womany-Malcome-to-o/14167535692918208311?s=xcUvM9nI"

const scriptUrl = `https://www.example.com${fullScriptPath}`;
```

**Step 3: Fetch & Store the Script Content**

**This step is critical.** Make a GET request to the full script URL and save the entire response body. The script content is required by our API to generate a valid payload.

```http
GET /onalbaine-legeance-what-come-Womany-Malcome-to-o/14167535692918208311?s=xcUvM9nI HTTP/2
Chrome: Headers
Accept: */*
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
Referer: https://www.example.com/
```

Store the full response body as a string, you will pass it to the API in Step 5.

**Step 4: Retrieve Proof of Work (If Required)**

Some sites require an additional Proof of Work (PoW) challenge. To determine if a site requires PoW, observe the network requests in your browser's developer tools. If you see a POST request to the Reese84 script endpoint with the body `{"f":"gpc"}`, the site uses PoW.

If PoW is required, make a POST request to the script path with `?d=yourdomain.com` appended:

```http
POST /onalbaine-legeance-what-come-Womany-Malcome-to-o/14167535692918208311?d=www.example.com HTTP/2
Chrome: Headers
Content-Type: text/plain; charset=utf-8

{"f":"gpc"}
```

The server will respond with a PoW string value:

```json
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
```

Save this value for the next step.

**Step 5: Generate the Payload**

Use our API to generate the Reese payload. The API requires the following inputs:

| Parameter        | Description                                                                     |
| ---------------- | ------------------------------------------------------------------------------- |
| `UserAgent`      | Your browser's User-Agent string (must be consistent across all requests)       |
| `AcceptLanguage` | Your Accept-Language header value (e.g., `en-US,en;q=0.9`)                      |
| `IP`             | Your client's public IP address                                                 |
| `ScriptUrl`      | The full script URL from Step 2 (e.g., `https://www.example.com/path/id?s=...`) |
| `PageUrl`        | The URL of the protected page you're trying to access                           |
| `Script`         | The full script content fetched in Step 3                                       |
| `Pow`            | The PoW value from Step 4 (empty string if PoW is not required)                 |

Our API will return the properly formatted payload string needed for the next step.

{% hint style="info" %}
This is the same SDK call as the static sensor, `GenerateReese84Sensor` / `generate_reese84_sensor` / `generateReese84Sensor` with a `ReeseInput` (`Reese84Input` in JS/TS), the only difference being that you set the `Pow` field to the value from Step 4. See [Reese84](/incapsula/reese84) for the per-language code and the full field table.
{% endhint %}

For detailed API specifications, see our [API Reference Documentation](/api-reference/incapsula).

**Step 6: Submit the Payload**

Post the generated payload to the challenge endpoint. Note: this uses the **script path** (without query params) with `?d=yourdomain.com` appended:

```http
POST /onalbaine-legeance-what-come-Womany-Malcome-to-o/14167535692918208311?d=www.example.com HTTP/2
Chrome: Headers
Content-Type: text/plain; charset=utf-8
Accept: application/json; charset=utf-8
Origin: https://www.example.com
Referer: https://www.example.com/

[YOUR_GENERATED_PAYLOAD]
```

The server will respond with a token in JSON format:

```json
{
  "token": "3:2wlemniq+CXN97167oNjyw==:EraPjamz...",
  "renewInSec": 896,
  "cookieDomain": "www.example.com"
}
```

**Step 7: Store the Token & Access Protected Content**

Save the token as a cookie named `reese84` with the domain from the response. Now make your request to the previously protected resource:

```http
GET / HTTP/2
Chrome: Headers
Cookie: reese84=3:2wlemniq+CXN97167oNjyw==:EraPjamz...
```

Verify the response no longer contains the "Pardon Our Interruption" challenge page. If the challenge page still appears, the token may be invalid or the IP may be blocked.

#### Implementation Best Practices

1. **Consistent Identity**: Use the same User-Agent, Accept-Language, and header order across all requests. Mismatches between these values will cause detection.
2. **Header Order**: Maintain consistent header ordering between requests. Incapsula checks for header order consistency as part of its fingerprinting.
3. **TLS Fingerprint**: Use a TLS client whose profile matches the Chrome version in your User-Agent. If your User-Agent claims a given Chrome version, your TLS fingerprint should match that same version (a slightly older profile is acceptable, see [User Agents](/api-reference/user-agents)).
4. **Token Renewal**: The token has an expiration time (`renewInSec`). Implement a renewal mechanism to avoid re-solving the full challenge.
5. **Public IP**: Your public IP must be obtained through the same proxy/network path you use for all other requests.

#### API Integration Notes

Our API simplifies the complex process of generating valid Reese payloads.

For detailed API specifications, endpoint documentation, and usage examples, please refer to our [API Reference Documentation](/api-reference/incapsula).


# Incapsula Captcha Block

Walkthrough of the Incapsula captcha block flow: detecting the challenge page, extracting the \_Incapsula\_Resource URL and submitting the token to regain access.

### Challenge Flow Overview

The Incapsula captcha challenge follows this sequence:

1. **Initial Request**: Client requests a protected page
2. **Captcha Block Page**: Server returns a captcha challenge page
3. **Resource Request**: Client requests the embedded resource URL
4. **Token Submission**: Client submits the captcha token to the challenge endpoint
5. **Access Granted**: Upon verification, client can access the protected content

### Implementation Guide

#### Step 1: Initial Request & Challenge Detection

When you make a request to a protected resource, you'll receive a captcha challenge page instead of the expected content:

```http
GET / HTTP/2
Chrome: Headers
```

The response will contain HTML:

```html
<html style="height:100%">
<head>
  <META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
  <meta name="format-detection" content="telephone=no">
  <meta name="viewport" content="initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <script type="text/javascript" src="/_Incapsula_Resource?SWJIYLWA=719d34d31c8e3a6e6fffd425f7e032f3"></script>
  <script src="/nions-to-vnse-the-Bewarfish-so-like-here-hoa-Mon" async></script>
</head>
<body style="margin:0px;height:100%">
  <iframe id="main-iframe" src="/_Incapsula_Resource?SWUDNSAI=31&xinfo=51-29384756-0%20NNNY%20RT%281744568237689%2043%29%20q%280%20-1%20-1%200%29%20r%280%20-1%29%20B12%2814%2c0%2c0%29%20U18&incident_id=1687000240661649450-106846605907789363&edet=12&cinfo=0e0000000e25&rpinfo=0&cts=XlV9lAHtmM6iAKQa1hQ6Yvp2jH9v9NdRImAO%2fBAIXo7Yl3vQMpnD%2bTzrDt%2f%2bPS32&mth=GET" frameborder=0 width="100%" height="100%" marginheight="0px" marginwidth="0px">
    Request unsuccessful. Incapsula incident ID: ...
  </iframe>
</body>
</html>
```

#### Step 2: Extract Resource URL

Extract the iframe source URL from the challenge page to build this URL:

```
https://www.example.com/_Incapsula_Resource?SWUDNSAI=31&xinfo=51-19749804-0%20NNNY%20RT%281744182960404%2043%29%20q%280%20-1%20-1%200%29%20r%280%20-1%29%20B12%2814%2c0%2c0%29%20U18&incident_id=1687000240661649450-106846605907789363&edet=12&cinfo=0e0000000e25&rpinfo=0&cts=XlV9lAHtmM6iAKQa1hQ6Yvp2jH9v9NdRImAO%2fBAIXo7Yl3vQMpnD%2bTzrDt%2f%2bPS32&mth=GET
```

#### Step 3: Request the Resource

Make a GET request to the extracted resource URL:

```http
GET /_Incapsula_Resource?SWUDNSAI=31&xinfo=51-29384756-0%20NNNY%20RT%281744568237689%2043%29%20q%280%20-1%20-1%200%29%20r%280%20-1%29%20B12%2814%2c0%2c0%29%20U18&incident_id=1687000240661649450-106846605907789363&edet=12&cinfo=0e0000000e25&rpinfo=0&cts=XlV9lAHtmM6iAKQa1hQ6Yvp2jH9v9NdRImAO%2fBAIXo7Yl3vQMpnD%2bTzrDt%2f%2bPS32&mth=GET HTTP/2
Chrome: Headers
```

The response will be an HTML page containing the captcha challenge and most importantly, the POST URL for submitting the token. Parse this response to extract the POST URL:

```javascript
xhr.open("POST", "/_Incapsula_Resource?SWCGHOEL=v2&dai=106846605907789363&cts=XlV9lAHtmM6iAKQa1hQ6Yvp2jH9v9NdRImAO%2fBAIXo7Yl3vQMpnD%2bTzrDt%2f%2bPS32", true);
```

#### Step 4: Obtain Captcha Token

The page contains an hCaptcha or Geetest challenge that you must solve to get a token.

{% hint style="warning" %}
Hyper Solutions does **not** solve this captcha for you, there is no API endpoint for this step. Use a third-party captcha-solving service (for example one that supports hCaptcha or Geetest), extract the site key and any challenge parameters from the resource page in Step 3, and request a token from that service. The token you get back is what you submit in Step 5.
{% endhint %}

Identify which captcha is in use by inspecting the resource page from Step 3 (look for an hCaptcha `sitekey` or a Geetest `gt`/`challenge` parameter), then pass those values to your solver.

#### Step 5: Submit the Captcha Token

Post the captcha token to the extracted POST URL:

```http
POST /_Incapsula_Resource?SWCGHOEL=v2&dai=106846605907789363&cts=XlV9lAHtmM6iAKQa1hQ6Yvp2jH9v9NdRImAO%2fBAIXo7Yl3vQMpnD%2bTzrDt%2f%2bPS32 HTTP/2
Chrome: Headers

g-recaptcha-response=tokenhere
```

The server will respond with a Set-Cookie header containing an `incap_sh_*` cookie:

```
HTTP/2 200 OK
Date: Wed, 09 Apr 2025 12:34:56 GMT
Content-Type: text/html; charset=utf-8
Set-Cookie	incap_sh_1979199=sh72ZwAAAAA6PmkpBgAIsr3YvwY8ht+b3wLU6h2+Aq+WkW+w; HttpOnly; Path=/; SameSite=None; Secure; Max-Age=3600
```

#### Step 6: Access the Protected Content

With the `incap_sh_*` cookie set, make your original request again to access the previously protected resource:

```http
GET / HTTP/2
Chrome: Headers
Cookie: incap_sh_1979199=sh72ZwAAAAA6PmkpBgAIsr3YvwY8ht+b3wLU6h2+Aq+WkW+w
```

The server should now return the protected content instead of the captcha challenge.


# UTMVC

UTMVC is an Incapsula / Imperva protection that runs a script and sets a \_\_\_utmvc cookie (three underscores) before granting access to the site.

A site is using UTMVC when its HTML loads an `/_Incapsula_Resource?SWJIYLWA=...` script. The flow is: fetch that script, generate a cookie from it via our API, set the `___utmvc` cookie, then hit a submit path to activate it.

{% hint style="info" %}
The cookie name is **`___utmvc`**, three underscores.
{% endhint %}

#### Implementation Steps

**Step 1: Parse the script path**

Extract the UTMVC script path from the page HTML. You can use the regex below, or the SDK helper (`ParseUtmvcScriptPath` in Go, `parse_utmvc_script_path` in Python, `parseUtmvcScriptPath` in JS/TS):

```regex
src="(/_Incapsula_Resource\?[^"]*)"
```

**Step 2: Fetch the script content**

Make a GET request to the script path from Step 1 and store the full response body. Our API needs the script contents to generate a valid cookie.

**Step 3: Generate & set the cookie**

Generate the cookie via our API (`GenerateUtmvcCookie` / `generate_utmvc_cookie` / `generateUtmvcCookie`), then set the returned value as the `___utmvc` cookie in your jar. The input requires:

* **UserAgent**: the same Chrome User-Agent you use for every request.
* **SessionIds**: the value of **each** cookie whose name starts with `incap_ses_`.
* **Script**: the script body from Step 2.

{% hint style="success" %}
The request body is large, compress it (`gzip`, `br`, or `deflate`) and set the `content-encoding` header. The SDKs auto-compress bodies over 1000 bytes.
{% endhint %}

**Step 4: Submit to activate the cookie**

Make a GET request to the submit path to activate the cookie:

```
/_Incapsula_Resource?SWKMTFSR=1&e=<random>
```

`e` is a random 64-bit float (for example `0.14896897949050825`). The SDK helpers build this path for you: `GetUtmvcSubmitPath()` / `get_utmvc_submit_path()` / `generateUtmvcScriptPath()`. If the `___utmvc` cookie is valid, the server responds by setting the cookie to the value `"a"` with a max-age of 0.

**Step 5: Make your real requests**

With the `___utmvc` cookie in place, retry the request that was blocked.

#### Notes

* The `___utmvc` cookie name has three underscores, a common source of bugs.
* Use consistent headers, User-Agent, and TLS fingerprint across all requests.
* See the [API Reference](/api-reference/incapsula) if you want to call `/utmvc` directly instead of using an SDK.


# Getting started

DataDome serves two challenge types, interstitial and slider. This page helps you identify which one you're facing and points you to the right guide.

If you're already familiar with solving DataDome challenges, you can either install one of our [SDKs](/start-here/readme-1) for easy integration, or head over to our [API Reference](/api-reference/datadome) if you want to handle the implementation yourself. The [DataDome Bypass API](https://hypersolutions.co/products/datadome) overview covers which challenges the endpoints solve and how they are priced.

{% hint style="info" %}
As with any other antibot, make sure you use a working TLS client that mimics the latest version of Google Chrome, match the headers and header-order 1:1 with the browser, and make sure you are using an up-to-date User-Agent. See [Core Requirements](/start-here/core-requirements) for the full list.
{% endhint %}

### Identifying the challenge

Both challenges are served with a **403 status code** and a nearly identical HTML block page containing a `dd` object. Two fields tell them apart: the `rt` value inside the `dd` object, and the script URL at the bottom of the page.

#### Interstitial

A device-check page the API solves without a captcha.

**Identifying characteristics:**

* The `dd` object has `'rt':'i'`
* The block page references `https://ct.captcha-delivery.com/i.js` (note the `i.js`)

[Read the detailed guide →](/datadome/interstitial)

#### Slider (captcha)

A slider captcha where the API solves the puzzle from the challenge images.

**Identifying characteristics:**

* The `dd` object has `'rt':'c'`
* The block page references `https://ct.captcha-delivery.com/c.js` (note the `c.js`)

{% hint style="warning" %}
If the `dd` object has `t` set to `bv`, your proxy is hard blocked, solving the challenge will have no effect.
{% endhint %}

[Read the detailed guide →](/datadome/slider-captcha)

### Challenges returned from an API request

DataDome doesn't only block full page loads. When your code calls a JSON or XHR endpoint, DataDome may return the challenge **inline as a JSON body** instead of the 403 HTML block page:

{% code overflow="wrap" %}

```json
{ "url": "https://geo.captcha-delivery.com/interstitial/?initialCid=..." }
```

{% endcode %}

Here the `url` is a **ready-made device link**: DataDome has already built it, so there is no `dd` object to parse. Detect this case when the response body is JSON with a `url` field pointing at `captcha-delivery.com`.

Route by the URL **path**, then solve exactly as you would a block-page challenge, passing the `url` straight through as the device link:

* `.../interstitial/...` → the [interstitial](/datadome/interstitial) flow
* `.../captcha/...` → the [slider](/datadome/slider-captcha) flow

Because you already hold the device link, **skip the HTML-parsing step** (`ParseInterstitialDeviceCheckLink` / `ParseSliderDeviceCheckLink`). Fetch the device link, then call the interstitial or slider generation with that `url` as the `deviceLink`.

{% hint style="info" %}
The interstitial solve can hand off to the slider the same way. Its response looks like `{ "cookie": "...", "view": "captcha", "url": "..." }`, and when `view` is `captcha` you solve the slider using that `url`.
{% endhint %}

### Tags

Separately from the challenge flows above, DataDome expects the browser to post telemetry ("tags") that raise your session's trust score. See [Tags](/datadome/tags) for when and how to send these.

{% hint style="info" %}
**Further reading on our blog:** [Recording a clean DataDome capture](https://hypersolutions.co/blog/datadome-browser-sessions) explains why polluted browser captures still get blocked even when header order looks right.
{% endhint %}

### Complete Example

For a full working implementation with proper TLS client setup, header ordering, and cookie handling, see our examples repository:

{% embed url="<https://github.com/Hyper-Solutions/hypersolutions-examples>" %}


# Tags

DataDome tags are extra telemetry posted to /js that raise a session's trust score and reduce blocks. This page shows how to generate tags payloads with our SDKs.

Unlike slider and interstitial, tags will never be served in a challenge or block page. This type is used to send extra telemetry data to DataDome that will increase trust score of the session (resulting in less blocks).\
\
In browser you will see multiple requests to a `/js`endpoint, this is where browser sends the tags data. This is how you can generate this data using our SDKS:

{% tabs %}
{% tab title="Golang" %}

```go
payload, err := session.GenerateDataDomeTags(ctx, &hyper.DataDomeTagsInput{
    UserAgent: "", // Your chrome useragent
    Cid: "", // Your current datadome cookie
    Ddk: "", // sitekey, static for each site. parse it from the /js/ payload request from browser
    Referer: "", // The referer visible as the referer header in the payload POST
    Type: "", // First time 'ch', second time 'le'
    Language: "", // The first language of your accept-language header, defaults to "en-US"
    IP: "", // The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 address of your pc.
})
if err != nil {
// Handle the error
}
// Use the payload to POST to /js
```

{% endtab %}

{% tab title="Python" %}

```python
payload = hyper_session.generate_tags_payload(hyper_sdk.DataDomeTagsInput(
    user_agent=USER_AGENT, # Your chrome UserAgent
    cid=cid, # Your current datadome cookie
    ddk=ddk, # sitekey, static for each site. parse it from the /js/ payload request from browser
    referer=referer, # The referer visible as the referer header in the payload POST
    tags_type=tags_type, # First time 'ch', second time 'le'
    ip=ip, # The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 address of your pc.
    accept_language=accept_language# The accept language header that you use
))
# Use the payload to POST to /js
```

{% endtab %}

{% tab title="JS / TS" %}

```javascript
const payload = await generateTagsPayload(session, {
    userAgent: "", // Your chrome UserAgent
    cid: cid, // Your current datadome cookie
    ddk: ddk, // sitekey, static for each site. parse it from the /js/ payload request from browser
    referer: referer, // The referer visible as the referer header in the payload POST
    type: type, // First time 'ch', second time 'le'
    ip: ip, // The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 address of your pc.
    acceptLanguage: language, // The accept-language header value
});
```

{% endtab %}
{% endtabs %}

You need to POST this payload to the `/js`endpoint same way browser will do it. The endpoint will return a response like this:

```json
{
	"status": 200,
	"cookie": "datadome=L7HH_UaWyA17TZFa7FNKxtIE9cReX~6bpf~E5A5IetWsibg0KwHgedPMPHee40cm4VqY9r3Yr6ZOCuWL17WB71PDE92lXdBIyyl3M2SZyhOl~7rmkK_XxE0O19hB4q0o; Max-Age=31536000; Domain=.vinted.fr; Path=/; Secure; SameSite=Lax"
}
```

You should update your `datadome`cookie in your cookiejar manually, with the value returned in the response.\
\
Posting tags should be done twice, always retrieving the `datadome`cookie from the first tags POST request. First with type `ch`and the second time with type `le`.


# Slider (captcha)

This page explains the flow of solving the slider challenge of DataDome.

If you already familiar with solving DataDome challenges, you can either install one of our [SDKs](/start-here/readme-1) for easy integration, or head over to our [API Reference](/api-reference/datadome) if you want to handle the implementation yourself. The [DataDome Bypass API](https://hypersolutions.co/products/datadome) overview covers which challenges the endpoints solve and how they are priced.

## Slider

This challenge is served using a 403 status code and a response body that looks as follows:

{% code overflow="wrap" %}

```html
<html>
   <head>
      <title>example.com</title>
      <style>#cmsg{animation: A 1.5s;}@keyframes A{0%{opacity:0;}99%{opacity:0;}100%{opacity:1;}}</style>
   </head>
   <body style="margin:0">
      <p id="cmsg">Please enable JS and disable any ad blocker</p>
      <script data-cfasync="false">var dd={'rt':'c','cid':'AHrlqAAAAAMA6gifcHcCX3IATaDxmw==','hsh':'EC3A9FB6F2A31D3AF16C270E6531D2','t':'fe','s':43337,'e':'5e8c40553ff322bdcba3d8e59224b6a2858cf1080c5e0f3923cc0fcd3d4217d5','host':'geo.captcha-delivery.com'}</script><script data-cfasync="false" src="https://ct.captcha-delivery.com/c.js"></script>
   </body>
</html>
```

{% endcode %}

{% hint style="warning" %}
If in the response, `t` is set to `bv`, it means your proxy is hard blocked, solving the challenge will not have any effect.
{% endhint %}

This is almost the same response you would receive with a Interstitial challenge, however the reference to this URL: `https://ct.captcha-delivery.com/c.js` is unique to slider.

### Parsing the HTML

Before we explain how you can manually parse the required values from the HTML posted above, first are shown code snippets of how it can be done easily with our SDKs:

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

```go
// reader is an `io.Reader` which holds the response body of the HTML.

// datadomeCookie is the cookie value of the cookie with name "datadome",
// this cookie is set by the 403 block page.

// referer is the URL that served the 403 block page.

deviceLink, err := datadome.ParseSliderDeviceCheckLink(reader, datadomeCookie, referer)
if err != nil {
    // Handle the error
}
// deviceLink will look like: https://geo.captcha-delivery.com/captcha/?...
```

{% endcode %}
{% endtab %}

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

```python
from hyper_sdk.datadome import parse_slider_device_check_link

device_link = parse_slider_device_check_link(html_content, datadome_cookie, referer)
# device_link will look like: https://geo.captcha-delivery.com/captcha/?...
```

{% endcode %}
{% endtab %}

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

```javascript
import {parseSliderDeviceCheckUrl, generateSliderPayload} from "hyper-sdk-js/datadome/slider.js";

const result = parseSliderDeviceCheckUrl(
    "", // Block page body
    "", // Value of `datadome` cookie
    "" // Referer, e.g. URL you are trying to access
);
if (result.isIpBanned) {
    // IP address is banned.
    // Note: result.url is null if this is true.
    return;
}
```

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

If you would rather parse the response yourself, you will need to extract the following fields from the `dd` object that can be found in the response body: `cid`, `hsh`, `t`, `s`, `e`.\
\
You also need to store the URL that received the block, it is used as the `referer`, and the `datadome` cookie that is set on this blocked request.\
\
You can now build the URL as follows:

{% code overflow="wrap" %}

```
https://geo.captcha-delivery.com/captcha/?initialCid={cid}&hash={hsh}&cid={datadomeCookie}&t={t}&referer={referer}&s={s}&e={e}&dm=cd
```

{% endcode %}

### Fetching the slider script

After having parsed the deviceLink in the previous step, you need to make a GET request to it. Make sure that you are sending the same headers and in the same order as your browser. Read and save the response body of this request as we need it to submit to the Hyper Solutions API.

### Fetching the slider puzzle

In order for the Hyper Solutions API to solve the slider challenge, it needs access to the images involved. The HTML/JavaScript that we have retrieved in the previous step contains data like this:

{% code overflow="wrap" %}

```javascript
captchaChallengeSeed: '17af5b20aafd238256f5a5d11cf475da',
captchaChallengePath: 'https://dd.prod.captcha-delivery.com/image/2026-01-19/17af5b20aafd238256f5a5d11cf475da.jpg',
```

{% endcode %}

#### Parsing the Image URLs

You need to extract the `captchaChallengePath` value, which gives you the puzzle image URL directly. To get the piece image URL, simply replace `.jpg` with `.frag.png`.

#### Finding the puzzle link (jpg)

You can parse `captchaChallengePath` using this regex:

```regexp
captchaChallengePath:\s*['"]([^'"]+\.jpg)['"]
```

Or if you already have the path extracted:

```regexp
(https:\/\/dd\.prod\.captcha-delivery\.com\/image\/.*?\.jpg)
```

#### Deriving the piece link (frag.png)

Once you have the puzzle URL, derive the piece URL by replacing the extension:

```javascript
const pieceUrl = puzzleUrl.replace('.jpg', '.frag.png');
```

For example:

* **Puzzle URL:** `https://dd.prod.captcha-delivery.com/image/2026-01-19/17af5b20aafd238256f5a5d11cf475da.jpg`
* **Piece URL:** `https://dd.prod.captcha-delivery.com/image/2026-01-19/17af5b20aafd238256f5a5d11cf475da.frag.png`

### Fetching the Images

You need to make a GET request to both URLs and store both responses (base64 encoded):

* The `.jpg` response should be stored as `puzzle`
* The `.frag.png` response should be stored as `piece`

### Fetching the payload from API

Again we have handy SDK functions to help you with these API calls, if you want to implement the API calls yourself, head over to the [API Reference](/api-reference/datadome).\
\
Alongside your usual session values (User-Agent, IP, Accept-Language, and the page URL as `parentUrl`), you need four challenge-specific values:

* deviceLink: The link parsed in the previous sections.
* html: The full response body of the GET request you made to the deviceLink URL.
* puzzle: The base64-encoded bytes of the `.jpg` image.
* piece: The base64-encoded bytes of the `.frag.png` image.

You can then generate the payload with the SDK as follows:

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

```go
checkUrl, headers, err := session.GenerateDataDomeSlider(ctx, &hyper.DataDomeSliderInput{
    UserAgent:      userAgent,
    DeviceLink:     deviceLink,
    Html:           string(html),
    Puzzle:         base64Puzzle,   // base64 of the .jpg
    Piece:          base64Piece,    // base64 of the .frag.png
    ParentUrl:      pageUrl,
    AcceptLanguage: acceptLanguage,
    IP:             ip,
})
if err != nil {
    // Handle the error
}
// GET the checkUrl; replay the returned headers on subsequent requests
```

{% endcode %}
{% endtab %}

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

```python
result = hyper_session.generate_slider_payload(hyper_sdk.DataDomeSliderInput(
    user_agent=user_agent,
    device_link=device_link,
    html=html,
    puzzle=base64_puzzle,
    piece=base64_piece,
    parent_url=page_url,
    accept_language=accept_language,
    ip=ip,
))
# GET result["payload"] (the check URL); replay result["headers"] when applicable
```

{% endcode %}
{% endtab %}

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

```javascript
const result = await generateSliderPayload(session, {
    userAgent: userAgent,
    deviceLink: deviceLink,
    html: deviceCheckBody,
    puzzle: base64Puzzle,       // base64 of the .jpg
    piece: base64Piece,         // base64 of the .frag.png
    parentUrl: pageUrl,
    acceptLanguage: acceptLanguage,
    ip: ip,
});
// GET result.payload (the check URL); replay result.headers when applicable
```

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

### Posting payload, solving challenge

Our API returns a simple URL as the result of solving the slider challenge, all that is required is to make a GET request to this URL which looks as follows:

```
https://geo.captcha-delivery.com/captcha/check?cid=...
```

The response of this GET request will be as follows:

{% code overflow="wrap" %}

```json
{
	"cookie": "datadome=cookievalue; Max-Age=31536000; Domain=.example.com; Path=/; Secure; SameSite=Lax"
}
```

{% endcode %}

You can parse the JSON response, and update your cookieJar with the `"cookie"` you received from DataDome.\
\
You have now successfully solved DataDome's slider challenge, retrying the request that served this block should not give you a challenge anymore.


# Interstitial

This page explains the flow of solving the interstitial challenge of DataDome.

If you already familiar with solving DataDome challenges, you can either install one of our [SDKs](/start-here/readme-1) for easy integration, or head over to our [API Reference](/api-reference/datadome) if you want to handle the implementation yourself. The [DataDome Bypass API](https://hypersolutions.co/products/datadome) overview covers which challenges the endpoints solve and how they are priced.

## Interstitial

This challenge is served using a 403 status code and a response body that looks as follows:

{% code overflow="wrap" %}

```html
<html>
   <head>
      <title>example.com</title>
      <style>#cmsg{animation: A 1.5s;}@keyframes A{0%{opacity:0;}99%{opacity:0;}100%{opacity:1;}}</style>
   </head>
   <body style="margin:0">
      <p id="cmsg">Please enable JS and disable any ad blocker</p>
      <script data-cfasync="false">var dd={'rt':'i','cid':'AHrlqAAAAAMACAOLE2sBBRMATaDxmw==','hsh':'13C44BAB3C9D728ABD66E2A9F0233C','b':1501854,'s':48047,'host':'geo.captcha-delivery.com'}</script><script data-cfasync="false" src="https://ct.captcha-delivery.com/i.js"></script>
   </body>
</html>
```

{% endcode %}

This is almost the same response you would receive with a Slider challenge, however the reference to this URL: `https://ct.captcha-delivery.com/i.js` is unique to interstitial.

### Parsing the HTML

Before we explain how you can manually parse the required values from the HTML posted above, first are shown code snippets of how it can be done easily with our SDKs:

{% tabs %}
{% tab title="Golang" %}

<pre class="language-go"><code class="lang-go">// reader is an `io.Reader` which holds the response body of the HTML.

<strong>// datadomeCookie is the cookie value of the cookie with name "datadome",
</strong>// this cookie is set by the 403 block page.

// referer is the URL that served the 403 block page.

deviceLink, err := datadome.ParseInterstitialDeviceCheckLink(reader, datadomeCookie, referer)
if err != nil {
// Handle the error
}
// deviceLink will look like: https://geo.captcha-delivery.com/interstitial/?...
</code></pre>

{% endtab %}

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

```python
from hyper_sdk.datadome import parse_interstitial_device_check_link

device_link = parse_interstitial_device_check_link(html_content, datadome_cookie, referer)
# device_link will look like: https://geo.captcha-delivery.com/interstitial/?...
```

{% endcode %}
{% endtab %}

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

```javascript
import parseInterstitialDeviceCheckUrl from "hyper-sdk-js/datadome/interstitial.js";

const deviceCheckUrl = parseInterstitialDeviceCheckUrl(
    "", // Block page body
    "", // Value of `datadome` cookie
    "" // Referer, e.g. URL you are trying to access
);
if (deviceCheckUrl === null) {
    // deviceCheckUrl will be null if parseInterstitialDeviceCheckUrl failed to parse it.
}
```

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

If you would rather parse the response yourself, you will need to extract the following fields from the `dd` object that can be found in the response body: `cid`, `hsh`, `s`, `b`.\
\
You also need to store the URL that received the block, it is used as the `referer`, and the `datadome` cookie that is set on this blocked request.\
\
You can now build the URL as follows:

{% code overflow="wrap" %}

```
https://geo.captcha-delivery.com/interstitial/?initialCid={cid}&hash={hsh}&cid={datadomeCookie}&referer={referer}&s={s}&b={b}&dm=cd
```

{% endcode %}

We will call this URL the deviceLink from now on.

### Fetching the interstitial script

After having parsed the deviceLink in the previous step, you need to make a GET request to it. Make sure that you are sending the same headers and in the same order as your browser. Read and save the response body of this request as we need it to submit to the Hyper Solutions API.

### Fetching the payload from API

Again we have handy SDK functions to help you with these API calls, if you want to implement the API calls yourself, head over to the [API Reference](/api-reference/datadome).\
\
Alongside your usual session values (User-Agent, IP, Accept-Language), you need two challenge-specific values:

* deviceLink: The link parsed in one of the previous sections.
* html: The full response body of the GET request you made to the deviceLink URL.

You can then generate the payload with the SDK as follows:

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

```go
payload, headers, err := session.GenerateDataDomeInterstitial(ctx, &hyper.DataDomeInterstitialInput{
    UserAgent:      userAgent,
    DeviceLink:     deviceLink,
    Html:           string(html),
    AcceptLanguage: acceptLanguage,
    IP:             ip,
})
if err != nil {
    // Handle the error
}
// POST the payload; replay the returned headers on subsequent requests
```

{% endcode %}
{% endtab %}

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

```python
result = hyper_session.generate_interstitial_payload(hyper_sdk.DataDomeInterstitialInput(
    user_agent=user_agent,
    device_link=device_check_link,
    html=html_content,
    accept_language=accept_language,
    ip=ip,
))
# POST result["payload"] to https://geo.captcha-delivery.com/interstitial/
# Replay result["headers"] on subsequent requests when required
```

{% endcode %}
{% endtab %}

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

```javascript
const result = await generateInterstitialPayload(session, {
    userAgent: userAgent,
    deviceLink: deviceCheckUrl,
    html: deviceCheckBody,
    ip: ip,
    acceptLanguage: acceptLanguage,
});
// POST result.payload to https://geo.captcha-delivery.com/interstitial/
// Replay result.headers on subsequent requests when required
```

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

### Posting payload, solving challenge

The payload returned by the HyperSolutions API in the previous step is an already concatenated Form Data string, you can POST this to the following URL:

```
https://geo.captcha-delivery.com/interstitial/
```

And make sure you are matching the headers in the order that your browser used.\
\
The response of this POST request will be as follows:

{% code overflow="wrap" %}

```json
{
	"cookie": "datadome=cookievalue; Max-Age=31536000; Domain=.example.com; Path=/; Secure; SameSite=Lax",
	"view": "redirect",
	"url": "https://www.example.com/path"
}
```

{% endcode %}

You can parse the JSON response, and update your cookieJar with the `"cookie"` you received from DataDome.\
\
You have now successfully solved DataDome's interstitial challenge, retrying the request that served this block should not give you a challenge anymore.


# Getting started

This page covers the different Kasada implementation flows you may encounter.

If you're already familiar with Kasada and wish to implement the API handling yourself, you can skip this and head directly to the [API Reference](/api-reference/kasada). The [Kasada Bypass API](https://hypersolutions.co/products/kasada) overview covers which challenges the endpoints solve and how they are priced.

{% hint style="info" %}
As with any other antibot, make sure you use a working TLS client that mimics the latest version of Google Chrome, match the headers and header-order 1:1 with the browser, and make sure you are using an up-to-date User-Agent. See [Core Requirements](/start-here/core-requirements) for the full list.
{% endhint %}

### Understanding Kasada Flows

Kasada can be implemented in two different ways depending on the website. It's important to identify which flow you're dealing with:

#### Flow 1: Initial Block Page (429 on Homepage)

Some sites, like Hyatt.com, serve a Kasada challenge immediately when you first access the website. You'll receive a **429 status code** with an HTML block page containing a reference to the `ips.js` script.

**Identifying characteristics:**

* First GET request to the website returns 429 status code
* Response body contains: `<script src="/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?..."></script>`
* You must solve the challenge before accessing any content on the site

**When to use this flow:**

* Site blocks you immediately on homepage access
* You see 429 status code with Kasada script reference
* Site reloads after posting `/tl`

[Read the detailed guide →](/k4sada/flow-1-initial-block-page)

#### Flow 2: Fingerprint Endpoint (/fp)

Most sites implement Kasada by having the browser make a request to the `/fp` (fingerprint) endpoint in the background. This is the standard Kasada implementation.

**Identifying characteristics:**

* Browser makes GET request to `/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/fp`
* This request returns 429 with the `ips.js` script reference
* You can often access the site initially, but need to solve Kasada for protected endpoints
* May require `x-kpsdk-cd` header on subsequent requests

**When to use this flow:**

* You can access the homepage but get challenged on specific endpoints
* Browser makes background request to `/fp` endpoint
* You need to maintain `x-kpsdk-ct` token for ongoing requests

[Read the detailed guide →](/k4sada/flow-2-fingerprint-endpoint)

### Next Steps

Choose the appropriate flow based on what you observe:

* **Getting 429 immediately on homepage?** → [Flow 1: Initial Block Page](/k4sada/flow-1-initial-block-page)
* **Browser making /fp requests?** → [Flow 2: Fingerprint Endpoint](/k4sada/flow-2-fingerprint-endpoint)

Both flows share the same core process of fetching the script, generating a payload, and posting to `/tl`. The main difference is when and where the challenge is triggered.

### Related

* [Vercel BotID](/k4sada/vercel-botid): a separate Kasada-powered detection layer that adds an `x-is-human` header, generated on its own endpoint.
* [Supported User Agents](/k4sada/supported-user-agents): the desktop and mobile-app User-Agents Kasada accepts.

### Complete Example

For a full working implementation with proper TLS client setup, header ordering, and cookie handling, see our examples repository:

{% embed url="<https://github.com/Hyper-Solutions/hypersolutions-examples>" %}


# Flow 1: Initial Block Page

This flow applies to websites like Hyatt.com where Kasada blocks you immediately when you first access the site with a 429 status code.

### Overview

When you make your first GET request to the website, you'll receive a **429 status code** with an HTML response containing a Kasada script reference. You must solve this challenge before you can access any content on the site.

### Initial Request

The response will be a 429 status code with HTML that looks like this:

{% code overflow="wrap" %}

```html
<!DOCTYPE html>
<html>
<head></head>
<body>
<script>window.KPSDK={};KPSDK.now=typeof performance!=='undefined'&&performance.now?performance.now.bind(performance):Date.now.bind(Date);KPSDK.start=KPSDK.now();</script>
<script src="/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?tkrm_alpekz_s1.3=0ZhprgzXdlDhhn0esTCQPfWjA2AeaGW50gpHSJVGSjRUPSrKJRQmsSZjTK8HhAmopVcLq2dfwum0SJmpM0Kz5j2DupTTI4OB1PLl7dhhJIVFAKsCsEoeL4hVm2tQjyFkyPUu42RgZ0dutvGd2xxDbpRLCWjV9MlMysNPzGvUTyg8CBX&x-kpsdk-im=AAIHh6ySRFXhFWAJcYSdsr-BStey6j5sKkK9HXfcJJ2BnB2_eCdWiiJjVu0OEOBEhsIFyZ4CgRIcu6EDyMf-WS88HRSC8PKJm2lZpq0ZTummEHy855H_HBuLSiiUmGQSiPUbJ74rXDFbWw"></script>
</body>
</html>
```

{% endcode %}

### Step 1: Parse the Script Path

You need to extract the script path from the HTML response. The script URL will look like:

```
/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?...
```

You can parse this using our SDKs:

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

```go
scriptPath, err := kasada.ParseScriptPath(reader)
if err != nil {
    // Handle the error
}
// scriptPath will look like: /149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?...
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}

```python
from hyper_sdk.kasada import parse_script_path

script_path = parse_script_path(html_content)
# script_path will look like: /149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?...
```

{% endtab %}

{% tab title="JS / TS" %}

```javascript
import { parseKasadaPath } from 'hyper-sdk-js';

const scriptPath = parseKasadaPath(blockedPageHtml);
```

{% endtab %}
{% endtabs %}

### Step 2: Fetch the ips.js Script

Make a GET request to the script path you parsed. Make sure to:

* Use the full URL: `https://www.example.com{scriptPath}`
* Match browser headers exactly
* Maintain the same header order as Chrome

Save the JavaScript response body as you'll need it for the next step.

### Step 3: Generate Payload via API

Now you'll use the Hyper Solutions API to generate the payload and headers needed for the `/tl` request.

Refer to the [Kasada](/api-reference/kasada) and the SDK documentation for accurate fields.

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

```go
payload, headers, err := session.GenerateKasadaPayload(ctx, &hyper.KasadaPayloadInput{
    // Kasada payload configuration
})
if err != nil {
    // Handle the error
}
// payload and headers are ready for the /tl request
```

{% endcode %}
{% endtab %}

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

```python
from hyper_sdk import KasadaPayloadInput

payload, headers = session.generate_kasada_payload(KasadaPayloadInput(
    # kasada payload input fields
))
```

{% endcode %}
{% endtab %}

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

```javascript
import { KasadaPayloadInput, generateKasadaPayload } from 'hyper-sdk-js';

const result = await generateKasadaPayload(session, new KasadaPayloadInput(
    // kasada payload input fields
));

const payload = result.payload;
const headers = result.headers;
```

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

{% hint style="warning" %}
The payload returned by the API is base64-encoded. You must decode it before posting to `/tl`.
{% endhint %}

### Step 4: POST to /tl Endpoint

POST the decoded payload to the `/tl` endpoint:

```
https://www.example.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/tl
```

**Critical requirements:**

* Content-Type must be `application/octet-stream`
* Include all headers returned by the API (`x-kpsdk-im`, `x-kpsdk-ct`, `x-kpsdk-dt`)
* Match browser header order exactly
* POST the decoded (binary) payload

### Step 5: Parse /tl Response

A successful response will return **200 status code** with:

**Response body:**

```json
{
    "reload": true
}
```

**Critical response headers to save:**

* `x-kpsdk-ct`: Token for subsequent requests (also in cookies)
* `x-kpsdk-st`: Timestamp value needed for generating POW (`x-kpsdk-cd`) headers
* `set-cookie`: Kasada cookies (e.g., `tkrm_alpekz_s1.3`, `tkrm_alpekz_s1.3-ssn`)

Example response headers:

{% code overflow="wrap" %}

```
x-kpsdk-ct: 02Rrkf95YyBbq2lGyws6SFVp...
x-kpsdk-st: 1759149934586
set-cookie: tkrm_alpekz_s1.3=02Rrkf95YyBbq2lGyws6SFVp...; Max-Age=86400; Path=/; HttpOnly
set-cookie: tkrm_alpekz_s1.3-ssn=02Rrkf95YyBbq2lGyws6SFVp...; Max-Age=86400; Path=/; HttpOnly; Secure; SameSite=None
```

{% endcode %}

{% hint style="info" %}
Store these values in your session:

* Update your cookie jar with the Set-Cookie headers
* Save `x-kpsdk-st` for future POW generation
* Save `x-kpsdk-ct` if you need to include it in request headers (check if browser does)
  {% endhint %}

### Step 6: Retry Original Request

Now retry your original request to the website with the Kasada cookies. The site should no longer serve you a 429 block page.

**Make sure to:**

* Include all Kasada cookies in your request
* Maintain proper headers and header order

### Summary

The complete flow:

1. ✅ Initial GET → Receive 429 with block page
2. ✅ Parse script path from HTML
3. ✅ GET request to ips.js script
4. ✅ Generate payload via Hyper Solutions API
5. ✅ POST decoded payload to /tl endpoint
6. ✅ Parse response headers and cookies
7. ✅ Retry original request with cookies

You have now successfully bypassed Kasada's initial block page challenge!


# Flow 2: Fingerprint Endpoint

This is the standard Kasada implementation where the browser makes a background request to the /fp (fingerprint) endpoint. This flow applies to most Kasada-protected websites.

### Overview

Unlike Flow 1, you may be able to access the homepage initially. Kasada is triggered when the browser makes a GET request to:

```
/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/fp
```

This request returns a 429 status code with the Kasada challenge. After solving it, you'll receive tokens and cookies that must be included in subsequent requests to protected endpoints.

### Step 1: Request the /fp Endpoint

Make a GET request to the fingerprint endpoint with query parameter `x-kpsdk-v`:

{% code overflow="wrap" %}

```
https://www.example.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/fp?x-kpsdk-v=j-xxx
```

{% endcode %}

The response will be a **429 status code** with HTML that looks like this:

{% code overflow="wrap" %}

```html
<!DOCTYPE html>
<html>
<head></head>
<body>
<script>window.KPSDK={};KPSDK.now=typeof performance!=='undefined'&&performance.now?performance.now.bind(performance):Date.now.bind(Date);KPSDK.start=KPSDK.now();</script>
<script src="/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?tkrm_alpekz_s1.3=0ZhprgzXdlDhhn0esTCQPfWjA2AeaGW50gpHSJVGSjRUPSrKJRQmsSZjTK8HhAmopVcLq2dfwum0SJmpM0Kz5j2DupTTI4OB1PLl7lkhhJIVFAKsCsEoeL4hVm2tQjyFkyPUu42RgZ0dutvGd2xxDbpRLCWjV9MlMysNPzGvUTyg8CBX&x-kpsdk-im=AAIHh6ySRFXhFWAJcYSdsr-BStey6j5sKkK9HXfcJJ2BnB2_eCdWiiJjVu0OEOBEhsIFyZ4CgRIcu6EDyMf-WS88HRSC8PKJm2lZpq0ZTummEHy855H_HBuLSiiUmGQSiPUbJ74rXDFbWw"></script>
</body>
</html>
```

{% endcode %}

### Step 2: Parse the Script Path

Extract the script path from the HTML response. The script URL will look like:

```
/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?...
```

You can parse this using our SDKs:

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

```go
scriptPath, err := kasada.ParseScriptPath(reader)
if err != nil {
    // Handle the error
}
// scriptPath will look like: /149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js?...
```

{% endcode %}
{% endtab %}

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

```python
from hyper_sdk.kasada import parse_script_path

script_path = parse_script_path(blocked_page_html)
# Returns: /ips.js?.
```

{% endcode %}
{% endtab %}

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

```javascript
import { parseKasadaPath } from 'hyper-sdk-js';

const scriptPath = parseKasadaPath(blockedPageHtml);
```

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

### Step 3: Fetch the ips.js Script

Make a GET request to the script path you parsed. Make sure to:

* Use the full URL: `https://www.example.com{scriptPath}`
* Match browser headers exactly
* Maintain the same header order as Chrome
* Set referer to the `/fp` URL

Save the JavaScript response body as you'll need it for the next step.

### Step 4: Generate Payload via API

Use the Hyper Solutions API to generate the payload and headers needed for the `/tl` request.\
\
Refer to the [Kasada](/api-reference/kasada) and the SDK documentation for accurate fields.

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

```go
payload, headers, err := session.GenerateKasadaPayload(ctx, &hyper.KasadaPayloadInput{
    // Fields
})
if err != nil {
    // Handle the error
}
// payload and headers are ready for the /tl request
```

{% endcode %}
{% endtab %}

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

```python
from hyper_sdk import KasadaPayloadInput

payload, headers = session.generate_kasada_payload(KasadaPayloadInput(
    # kasada payload input fields
))
```

{% endcode %}
{% endtab %}

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

```javascript
import { KasadaPayloadInput, generateKasadaPayload } from 'hyper-sdk-js';

const result = await generateKasadaPayload(session, new KasadaPayloadInput(
    // kasada payload input fields
));

const payload = result.payload;
const headers = result.headers;
```

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

{% hint style="warning" %}
The payload returned by the API is base64-encoded. You must decode it before posting to `/tl`.
{% endhint %}

### Step 5: POST to /tl Endpoint

POST the decoded payload to the `/tl` endpoint:

```
https://www.example.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/tl
```

**Critical requirements:**

* Content-Type must be `application/octet-stream`
* Include all headers returned by the API (`x-kpsdk-im`, `x-kpsdk-ct`, `x-kpsdk-dt`)
* Match browser header order exactly
* POST the decoded (binary) payload
* Set referer to the `/fp` URL

### Step 6: Parse /tl Response

A successful response will return **200 status code** with:

**Response body:**

```json
{
    "reload": true
}
```

**Critical response headers to save:**

* `x-kpsdk-ct`: Token that must be included in subsequent requests to protected endpoints
* `x-kpsdk-st`: Timestamp value needed for generating POW (`x-kpsdk-cd`) headers
* `set-cookie`: Kasada cookies (e.g., `tkrm_alpekz_s1.3`, `tkrm_alpekz_s1.3-ssn`)

Example response headers:

{% code overflow="wrap" %}

```
x-kpsdk-ct: 02Rrkf95YyBbq2lGyws6SFVp...
x-kpsdk-st: 1759149934586
set-cookie: tkrm_alpekz_s1.3=02Rrkf95YyBbq2lGyws6SFVp...; Max-Age=86400; Path=/; HttpOnly
set-cookie: tkrm_alpekz_s1.3-ssn=02Rrkf95YyBbq2lGyws6SFVp...; Max-Age=86400; Path=/; HttpOnly; Secure; SameSite=None
```

{% endcode %}

{% hint style="info" %}
Store these values in your session:

* Update your cookie jar with the Set-Cookie headers
* Save `x-kpsdk-st` for future POW generation
* Save `x-kpsdk-ct` as you'll need to include it in request headers to protected endpoints
  {% endhint %}

### Step 7: Making Requests to Protected Endpoints

Now you can make requests to protected endpoints with the Kasada tokens. **Observe what headers the browser includes** and match them exactly.

#### Headers to include:

1. **Kasada cookies** (always required):
   * Include all cookies from the `/tl` response in your Cookie header
2. **x-kpsdk-ct header** (if browser includes it):
   * Use the value from the `/tl` response headers, or the one that was last returned from a protected endpoint.
   * Some sites require this in the header, others only use cookies
3. **x-kpsdk-cd header** (if browser includes it):
   * This is a POW (Proof of Work) that must be freshly generated for **each request**
   * See the section below for how to generate it

### Fetch Client Configuration (Optional - /mfc)

Some Kasada implementations require an additional step to fetch client configuration. If you observe the browser making a GET request to the `/mfc` endpoint, you'll need to include this step.

#### When to use /mfc

Check your browser's network logs. If you see a request to:

```
/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/mfc
```

Then you need to perform this step after solving the initial challenge and before making requests to protected endpoints.

#### Requesting /mfc

Make a GET request to the `/mfc` endpoint:

```
https://www.example.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/mfc
```

**Important:**

* Include your Kasada cookies from the `/tl` response
* Match browser headers and header order
* The request should return a 200 status code

#### Parse /mfc Response Headers

The response will include these critical headers:

* **x-kpsdk-fc**: Feature configuration value needed for POW generation on sites using `/mfc`
* **x-kpsdk-h**: Header value that may be required on subsequent requests to protected endpoints

Example response headers:

```
x-kpsdk-fc: AH4kT...
x-kpsdk-h: 1-BwVlRFs
```

{% hint style="info" %}
Store both values:

* Save `x-kpsdk-fc` to use when generating POW (`x-kpsdk-cd`) headers
* Save `x-kpsdk-h` and include it in requests to protected endpoints if the browser does
  {% endhint %}

{% hint style="warning" %}
If your site doesn't use `/mfc` (you don't see it in browser logs), you can skip this step entirely and omit the `fc` parameter when generating POW.
{% endhint %}

### Generating x-kpsdk-cd for Each Request

If the website requires the `x-kpsdk-cd` header on requests (check browser behavior), you **must** generate a fresh POW for **each and every request**. Never reuse POW values.

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

```go
powPayload, err := session.GenerateKasadaPow(ctx, &hyper.KasadaPowInput{
    // POW challenge parameters
})
if err != nil {
    // Handle error
}
```

{% endcode %}
{% endtab %}

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

```python
from hyper_sdk import KasadaPowInput

pow_payload = session.generate_kasada_pow(KasadaPowInput(
    # kasada pow input fields
))
```

{% endcode %}
{% endtab %}

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

```javascript
import { KasadaPowInput, generateKasadaPow } from 'hyper-sdk-js';

const powPayload = await generateKasadaPow(session, new KasadaPowInput(
    // kasada pow input fields
));
```

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

{% hint style="danger" %}
**CRITICAL**: The `x-kpsdk-cd` header must be regenerated for every single request. Reusing POW values will cause your requests to fail. Generate a new POW immediately before making each request.
{% endhint %}

{% hint style="warning" %}
**Use the latest `x-kpsdk-ct`, not just the one from `/tl`.** POW generation takes a `ct` value, and that token is refreshed as you go: responses (including from protected endpoints) can return a new `x-kpsdk-ct` header. Each time one does, update your stored `ct` and pass that latest value when generating the next POW. Only the most recent `ct` works, a stale value such as the original `/tl` token will make `/cd` fail. The `st` timestamp stays as returned by `/tl`.
{% endhint %}

### When to Re-solve the Challenge

You may need to re-solve the Kasada challenge (repeat the entire flow) if:

* Your Kasada cookies expire
* You receive a 429 response on protected endpoints

To maintain long-running sessions:

* Proactively refresh tokens before they expire
* Handle 429 responses by triggering a new challenge solve

### Summary

The complete flow:

1. ✅ GET request to `/fp` endpoint → Receive 429 with block page
2. ✅ Parse script path from HTML
3. ✅ GET request to ips.js script
4. ✅ Generate payload via Hyper Solutions API
5. ✅ POST decoded payload to /tl endpoint
6. ✅ Parse response headers and cookies
7. ✅ Make requests to protected endpoints with:
   * Kasada cookies (always)
   * `x-kpsdk-ct` header (if browser uses it)
   * `x-kpsdk-cd` header (if browser uses it - generate fresh for each request)

You have now successfully integrated Kasada's flow!


# Vercel BotID

This flow applies when a site is protected by Kasada through Vercel BotID.  You can identify this by the presence of the \`x-is-human\` header on requests  to protected endpoints.

#### Overview

Vercel BotID is a bot detection system that can be used alongside Kasada protection. When enabled, protected endpoints require an `x-is-human` header containing a generated token. You can identify sites using Vercel BotID by observing the `x-is-human` header in browser requests to protected endpoints.

#### Step 1: Fetch the c.js Script

Make a GET request to the BotID script. The script path follows this pattern:

{% code overflow="wrap" %}

```
https://www.example.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/a-4-a/c.js?i=0&v=3&h=www.example.com
```

{% endcode %}

**Critical requirements:**

* Match browser headers exactly
* Maintain the same header order as Chrome

Save the JavaScript response body as you'll need it for the next step.

#### Step 2: Generate the x-is-human Header via API

Use the Hyper Solutions API to generate the `x-is-human` header value. See the [API Reference](/api-reference/kasada) for the full field list.

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

```go
header, err := session.GenerateBotIDHeader(ctx, &hyper.BotIDHeaderInput{
    Script:         scriptBody,
    UserAgent:      "your-user-agent",
    IP:             "your-proxy-ip",
    AcceptLanguage: "en-US,en;q=0.9",
})
if err != nil {
    // Handle the error
}
// header is ready to use as x-is-human value
```

{% endcode %}
{% endtab %}

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

```python
from hyper_sdk import BotIDHeaderInput

header = session.generate_botid_header(BotIDHeaderInput(
    script=script_body,
    user_agent="your-user-agent",
    ip="your-proxy-ip",
    accept_language="en-US,en;q=0.9",
))
```

{% endcode %}
{% endtab %}

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

```javascript
import { BotIDHeaderInput, generateBotIDHeader } from 'hyper-sdk-js';

const header = await generateBotIDHeader(session, new BotIDHeaderInput({
    script: scriptBody,
    userAgent: "your-user-agent",
    ip: "your-proxy-ip",
    acceptLanguage: "en-US,en;q=0.9",
}));
```

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

#### Step 3: Making Requests to Protected Endpoints

Include the generated token in the `x-is-human` header on requests to protected endpoints. **Observe what headers the browser includes** and match them exactly.

#### When to Re-generate the Header

You may need to generate a new `x-is-human` header if:

* You receive a 429 response on protected endpoints
* Your proxy IP address changes

#### Summary

The complete flow:

1. ✅ GET request to `c.js` script endpoint
2. ✅ Generate `x-is-human` header via Hyper Solutions API
3. ✅ Make requests to protected endpoints with the `x-is-human` header

You have now successfully integrated Vercel BotID!


# Supported User Agents

This page documents all user agent configurations supported by the Hyper Solutions Kasada API.

### Desktop User Agents

#### Windows Chrome (Recommended)

```
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{REPLACETHIS}.0.0.0 Safari/537.36
```

Windows remains our recommended default for desktop configurations.

#### macOS Chrome

```
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{REPLACETHIS}.0.0.0 Safari/537.36
```

macOS support is available for specific use cases where Windows user agents may face temporary restrictions.

### Mobile User Agents

The Kasada API also supports the following mobile app user agents:

```
FootLocker/X.X.X iOS/X.X
ChampsSports/X.X.X iOS/X.X
KidsFootLocker/X.X.X iOS/X.X
FLCA/CFNetwork/Darwin
FLEU/CFNetwork/Darwin
Whatnot vXX.XX.0 (XX), iOS X.X, iPhoneXX,X
SNKRS/X.X.X (prod; XXXXXXXXXX; iOS X.X; iPhoneX,X)
NikeApp/XX.XX.X (prod; XXXXXXXXXX; iOS X.X; iPhoneX,X)
Sephora X.X, iOS XX.X.X, iPhoneXX,X
```

**Note:** The app version (e.g., `X.X.X`) and iOS version (e.g., `iOS X,X`) will change over time. Ensure you are using current versions for the best results.

#### X-Kpsdk-Dv header value for mobile

This value is hardcoded and we can't return it to you for the mobile endpoints. You can use the following strings:

```
QkZWEmcDRUBEDloaAg8GABpSDxVEX1JfXBRZRQF5VRoJFFozUQtXBABQGwEPHA==
QkZWEmcDRUBEDloaAg8GABpRDxVEX1JfXBRZRQF5VRoJFFozUQtXBABRGwEPHA==
```


# Authentication

Authentication can be done in two ways.

## API Key

This is the easiest way to authenticate, simply add the `x-api-key` header with your API Key to your requests.

## API Key + JWT Signing

Using JWT adds a degree of complexity but it is strongly recommended to use when you are going to call this API in client-side applications.\
\
The JWT Token offers an additional layer of security since it will remain in your source code and will not be sent with requests.\
\
You will still need to add the `x-api-key` to your requests and this time also `x-signature`. It can be generated as follows:

{% tabs %}
{% tab title="Go" %}

```go
import (
	"github.com/golang-jwt/jwt/v5"
)

func GenerateSignature(apiKey, jwtKey string) (string, error) {
	claims := jwt.MapClaims{}
	claims["key"] = apiKey
	claims["exp"] = time.Now().Add(time.Second * 15).Unix() // this prevents replay attacks

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString([]byte(jwtKey))
	if err != nil {
		return "", err
	}

	return tokenString, nil
}
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const jwt = require('jsonwebtoken');

function generateSignature(apiKey, jwtKey) {
  const claims = {
    key: apiKey,
    // Set expiration to 15 seconds from now to prevent replay attacks
    exp: Math.floor(Date.now() / 1000) + 15,
  };

  try {
    const tokenString = jwt.sign(claims, jwtKey, { algorithm: 'HS256' });
    return tokenString;
  } catch (err) {
    throw err;
  }
}

```

{% endtab %}

{% tab title="Python" %}
The following function requires `PyJWT` to be installed.

```
pip install PyJWT
```

```python
import jwt
import time

def generate_signature(api_key, jwt_key):
    claims = {
        'key': api_key,
        # Set expiration to 15 seconds from now to prevent replay attacks
        'exp': int(time.time()) + 15,
    }

    try:
        token_string = jwt.encode(claims, jwt_key, algorithm='HS256')
        return token_string
    except Exception as error:
        raise error

```

{% endtab %}
{% endtabs %}

### Organizations

Organization owners can authenticate API requests on behalf of their users by using their App Key and App Secret. These credentials are available in your organization dashboard.

Add the following headers to your requests:

| Header            | Description                                        |
| ----------------- | -------------------------------------------------- |
| `x-api-key`       | The user's API Key                                 |
| `x-app-key`       | Your organization's App Key                        |
| `x-app-signature` | A signed JWT token generated using your App Secret |

The signature is generated the same way as the standard JWT signing method:

{% tabs %}
{% tab title="Go" %}

```go
import (
	"time"
	"github.com/golang-jwt/jwt/v5"
)

func generateSignature(appKey string, appSecret []byte) (string, error) {
	claims := jwt.MapClaims{
		"key": appKey,
		"exp": time.Now().Add(time.Minute).Unix(),
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(appSecret)
}
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const jwt = require('jsonwebtoken');

function generateSignature(appKey, appSecret) {
  const claims = {
    key: appKey,
    exp: Math.floor(Date.now() / 1000) + 60,
  };
  return jwt.sign(claims, appSecret, { algorithm: 'HS256' });
}
```

{% endtab %}

{% tab title="Python" %}

```python
import jwt
import time

def generate_signature(app_key: str, app_secret: str) -> str:
    claims = {
        'key': app_key,
        'exp': int(time.time()) + 60,
    }
    return jwt.encode(claims, app_secret, algorithm='HS256')
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
The `x-app-key` and `x-app-signature` headers are only for organization owners. Individual users should continue using the standard `x-api-key` authentication.
{% endhint %}


# Akamai

API reference for the Hyper Solutions Akamai API: generate sensor data, SBSD payloads and SEC-CPT challenge responses for sites behind Akamai Bot Manager.

{% hint style="info" %}
Tip: Use one of the [SDKs](/start-here/readme-1) instead of directly calling the API.
{% endhint %}

This API generates human-like data based on the most recent chrome browser version on Windows. It is recommended to match the latest TLS and UserAgent of the newest chrome version when using this API on a site.\
\
The latest chrome useragents can be found [here](https://versionhistory.googleapis.com/v1/chrome/platforms/win/channels/stable/versions/). It is perfectly fine to use only one useragent for all your requests.\
\
Note that all request payloads to this API should be sent as `application/json`.\
\
It is advised to use one of the sdks that were written for this API. Feel free to contact me about them.

## Generate sensor data

## Generate sensor data

> Generates sensor data that should be posted in order to acquire valid "\_abck" cookies

```json
{"openapi":"3.0.0","info":{"title":"AKM Sensor Data and Pixel Payload API","version":"1.0.0"},"servers":[{"url":"https://akm.hypersolutions.co","description":"Main server for sensor and pixel endpoints"}],"paths":{"/v2/sensor":{"post":{"summary":"Generate sensor data","description":"Generates sensor data that should be posted in order to acquire valid \"_abck\" cookies","operationId":"generateSensorData","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["abck","bmsz","pageUrl","version","userAgent","script","scriptUrl","ip","acceptLanguage","context"],"properties":{"abck":{"type":"string","description":"The \"_abck\" cookie to generate sensor data with. This should be obtained from your cookie jar. The cookie for the first request should be from doing a GET request to the script endpoint."},"bmsz":{"type":"string","description":"The \"bm_sz\" cookie to generate sensor data with."},"pageUrl":{"type":"string","description":"The page URL to insert into the generated sensor data. This should be the URL of the page you're currently on, like a product page, login page or home page."},"version":{"type":"string","description":"The Akamai version that the site is on. Supported values are \"2\" and \"3\". When using v3 dynamic, set this value to \"3\"."},"userAgent":{"type":"string","description":"The userAgent that you're using for the entire session"},"script":{"type":"string","description":"Script contents as string. Mutually exclusive with context. Should only be sent on first sensor request."},"scriptUrl":{"type":"string","description":"The script URL that you are posting sensor data to."},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."},"acceptLanguage":{"type":"string","description":"Your accept-language header."},"context":{"type":"string","description":"Leave empty for first sensor, update with context string from response for later sensors."}}}}}},"responses":{"200":{"description":"OK Sensor data successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The generated sensor data"},"context":{"type":"string","description":"The context value to use for generating the next sensor"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

## Sbsd

Sbsd is a new challenge meant to block scrapings from accessing HTML. You will find it with a blocking page that looks like this:

```html
<html>
   <head>
      <script type="text/javascript" src="https://example.com/assets/33eb14c569f53cae79b60dac7ccf7fcaf0012483407" async ></script><script src="/.well-known/sbsd?v=7ac10a5c-7a4e-fddd-611f-39dcf23f1722&amp;t=99543528"></script>
      <script>
         (function() {
             var proxied = window.XMLHttpRequest.prototype.send;
             window.XMLHttpRequest.prototype.send = function() {
             var pointer = this
             var intervalId = window.setInterval(function(){
                 if (pointer.readyState != 4){
                 return;
                 }
                 location.reload(true)
                 clearInterval(intervalId);
             }, 1);
             return proxied.apply(this, [].slice.call(arguments));
             };
         })();
      </script>
   </head>
   <body>
   </body>
</html>
```

It is important to parse the UUID, this can be done with the following regex:

```regex
v=(.*?)&
```

After parsing the UUID you can make a request to my API with the following API Reference:

## Generate sbsd payload

> Generates sbsd payload that should be posted in order to get around sbsd page

```json
{"openapi":"3.0.0","info":{"title":"AKM Sensor Data and Pixel Payload API","version":"1.0.0"},"servers":[{"url":"https://akm.hypersolutions.co","description":"Main server for sensor and pixel endpoints"}],"paths":{"/sbsd":{"post":{"summary":"Generate sbsd payload","description":"Generates sbsd payload that should be posted in order to get around sbsd page","operationId":"generateSbsdPayload","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["index","pageUrl","uuid","userAgent","o","script","ip","acceptLanguage"],"properties":{"index":{"type":"number","description":"Always set to 0 when solving sbsd challenge with `t` parameter. Otherwise start from 0 and keep incrementing. On some sites, posting 2 sbsd sensors gives much better success rates."},"pageUrl":{"type":"string","description":"This should be the URL of the page you're currently on, like a product page, login page or home page. It's also the referer on the sbsd post request in browser."},"uuid":{"type":"string","description":"The uuid of the sbsd challenge (https://example.com/.well-known/sbsd?v=dcc78710-14fe-3835-cc6e-b9b5ea3b6010). uuid is dcc78710-14fe-3835-cc6e-b9b5ea3b6010 on this url."},"userAgent":{"type":"string","description":"The userAgent that you're using for the entire session"},"o":{"type":"string","description":"The \"sbsd_o\" cookie value, if \"sbsd_o\" is not present, use the cookie value from \"bm_so\"."},"script":{"type":"string","description":"The script body as a string."},"acceptLanguage":{"type":"string","description":"Your accept-language header."},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."}}}}}},"responses":{"200":{"description":"OK Sbsd payload successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The generated sbsd payload"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

You can then post the payload string to the following endpoint:

```
https://example.com/.well-known/sbsd
```

Making a new GET request to the same page (make sure that cookies are included in this request) it will show you the full HTML.

## Pixel

Pixel is not required by most sites. Please discuss with support first if you think the site requires it. Your site having the pixel script does not mean it has pixel enforced.

## Generate pixel payload

## Generate pixel payload

> Generates a pixel payload to use to obtain a "ak\_bmsc" cookie. Note that the payload returned is already url-encoded.

```json
{"openapi":"3.0.0","info":{"title":"AKM Sensor Data and Pixel Payload API","version":"1.0.0"},"servers":[{"url":"https://akm.hypersolutions.co","description":"Main server for sensor and pixel endpoints"}],"paths":{"/pixel":{"post":{"summary":"Generate pixel payload","description":"Generates a pixel payload to use to obtain a \"ak_bmsc\" cookie. Note that the payload returned is already url-encoded.","operationId":"generatePixelPayload","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["htmlVar","scriptVar","userAgent","ip","acceptLanguage"],"properties":{"htmlVar":{"type":"string","description":"The bazadebezolkohpepadr value."},"scriptVar":{"type":"string","description":"The u value that gets sent in the payload. It's hidden somewhere in the first array in the pixel script."},"userAgent":{"type":"string","description":"The userAgent that you're using for your whole session."},"acceptLanguage":{"type":"string","description":"Your accept-language header."},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."}}}}}},"responses":{"200":{"description":"OK Pixel payload successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The generated pixel payload (url-encoded)"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```


# Incapsula

API reference for the Hyper Solutions Incapsula API: generate reese84 sensor payloads and utmvc cookies for sites behind Imperva / Incapsula.

{% hint style="info" %}
Tip: Use one of the [SDKs](/start-here/readme-1) instead of directly calling the API.
{% endhint %}

This API generates human-like data based on the most recent chrome browser version on Windows. It is recommended to match the latest TLS fingerprint and UserAgent of the newest chrome version when using this API on a site.\
\
The latest chrome useragents can be found [here](https://versionhistory.googleapis.com/v1/chrome/platforms/win/channels/stable/versions/). It is perfectly fine to use only one useragent for all your requests.\
\
Note that all request payloads to this API should be sent as `application/json`.

## Generate reese84 sensor

## Generate reese84 sensor

> Generates sensor data that should be posted in order to acquire valid "reese84" cookies

```json
{"openapi":"3.0.0","info":{"title":"Incapsula Reese84 Sensor and UTMVC Cookie API","version":"1.0.0"},"servers":[{"url":"https://incapsula.hypersolutions.co","description":"Server for reese84 and utmvc endpoints"}],"paths":{"/reese84":{"post":{"summary":"Generate reese84 sensor","description":"Generates sensor data that should be posted in order to acquire valid \"reese84\" cookies","operationId":"generateReese84Sensor","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userAgent","pageUrl","script","scriptUrl","acceptLanguage","ip"],"properties":{"userAgent":{"type":"string","description":"The userAgent that you're using for the entire session"},"pageUrl":{"type":"string","description":"Your page URL."},"script":{"type":"string","description":"The script content as a string."},"scriptUrl":{"type":"string","description":"The script url where you got the script contents from."},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."},"acceptLanguage":{"type":"string","description":"Your accept-language header."},"pow":{"type":"string","description":"The pow string fetched from incapsula resource."}}}}}},"responses":{"200":{"description":"OK Sensor data successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The generated sensor data"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

## Generate utmvc cookie

{% hint style="success" %}
Since the request body of the requests will be large, it is highly recommended to apply compression to your request body first. We support the following compression methods: "gzip", "br", "deflate", and "zstd".<br>

Don't forget to set the `content-encoding` header.
{% endhint %}

## Generate utmvc cookie

> Generates a utmvc cookie

```json
{"openapi":"3.0.0","info":{"title":"Incapsula Reese84 Sensor and UTMVC Cookie API","version":"1.0.0"},"servers":[{"url":"https://incapsula.hypersolutions.co","description":"Server for reese84 and utmvc endpoints"}],"paths":{"/utmvc":{"post":{"summary":"Generate utmvc cookie","description":"Generates a utmvc cookie","operationId":"generateUtmvcCookie","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"},{"in":"header","name":"content-encoding","schema":{"type":"string"},"description":"In case you want to improve latency to our APIs, you can apply encoding to your request payload and set the content-encoding header accordingly."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["script","sessionIds","userAgent"],"properties":{"script":{"type":"string","description":"The JavaScript obtained from doing a GET request to the script path."},"sessionIds":{"type":"array","items":{"type":"string"},"description":"The value of each cookie that has a name that starts with incap_ses_."},"userAgent":{"type":"string","description":"The userAgent that you're using for your whole session."}}}}}},"responses":{"200":{"description":"OK Cookie was generated","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The generated cookie value"},"swhanedl":{"type":"string","description":"Parameters for /_Incapsula_Resource?SWHANEDL= request"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```


# DataDome

This page shows the API Reference for our DataDome API.

{% hint style="info" %}
Tip: Use one of the [SDKs](/start-here/readme-1) instead of directly calling the API.
{% endhint %}

{% hint style="success" %}
Compress your request body, some payloads are large. We support "gzip", "br", "deflate", and "zstd"; set the `content-encoding` header. More info: [Compression](/api-reference/compression).
{% endhint %}

## Interstitial

Generates the payload for the DataDome **interstitial** challenge (the device-check page solved without a captcha). See the [interstitial guide](/datadome/interstitial).

## Solve interstitial

> Solves the interstitial challenge. After getting this payload, make a POST request to \`<https://geo.captcha-delivery.com/interstitial/\\`> with it in the payload and you will receive the datadome cookie in the response. Set this cookie in your cookiejar, you have now successfully solved DataDome interstitial.

```json
{"openapi":"3.0.0","info":{"title":"DataDome Captcha Solving API","version":"1.0.0"},"servers":[{"url":"https://datadome.hypersolutions.co","description":"Server for captcha solving endpoint"}],"paths":{"/interstitial":{"post":{"summary":"Solve interstitial","description":"Solves the interstitial challenge. After getting this payload, make a POST request to `https://geo.captcha-delivery.com/interstitial/` with it in the payload and you will receive the datadome cookie in the response. Set this cookie in your cookiejar, you have now successfully solved DataDome interstitial.","operationId":"solveInterstitial","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userAgent","deviceLink","html","ip","acceptLanguage"],"properties":{"userAgent":{"type":"string","description":"Your chrome UserAgent"},"deviceLink":{"type":"string","description":"The deviceLink you constructed"},"html":{"type":"string","description":"The response body of the GET request to the deviceLink"},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."},"acceptLanguage":{"type":"string","description":"Your accept-language header"}}}}}},"responses":{"200":{"description":"OK Interstitial solved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The payload for POST request to /interstitial/"},"headers":{"type":"object","properties":{"sec-ch-device-memory":{"type":"string"},"sec-ch-ua-mobile":{"type":"string"},"sec-ch-ua-arch":{"type":"string"},"sec-ch-ua-platform":{"type":"string"},"sec-ch-ua-model":{"type":"string"},"sec-ch-ua-full-version-list":{"type":"string"}}}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

## Slider

Solves the DataDome **slider** captcha from the challenge images and returns the check URL. See the [slider guide](/datadome/slider-captcha).

## Solve captcha

> Solves the captcha challenge. After getting this URL in the payload, make a GET request to it and you will receive the datadome cookie in the response. Set this cookie in your cookiejar, you have now successfully solved DataDome slider.

```json
{"openapi":"3.0.0","info":{"title":"DataDome Captcha Solving API","version":"1.0.0"},"servers":[{"url":"https://datadome.hypersolutions.co","description":"Server for captcha solving endpoint"}],"paths":{"/slider":{"post":{"summary":"Solve captcha","description":"Solves the captcha challenge. After getting this URL in the payload, make a GET request to it and you will receive the datadome cookie in the response. Set this cookie in your cookiejar, you have now successfully solved DataDome slider.","operationId":"solveCaptcha","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userAgent","deviceLink","html","puzzle","piece","parentUrl","ip","acceptLanguage"],"properties":{"userAgent":{"type":"string","description":"Your chrome UserAgent"},"deviceLink":{"type":"string","description":"The deviceLink you constructed"},"parentUrl":{"type":"string","description":"The parentUrl visible in the form fields"},"html":{"type":"string","description":"The response body of the GET request to the deviceLink"},"puzzle":{"type":"string","description":"The response body from the GET request to the `.jpg` image, base64 encoded."},"piece":{"type":"string","description":"The response body from the GET request to the `.frag.png` image, base64 encoded."},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."},"acceptLanguage":{"type":"string","description":"Your accept-language header"}}}}}},"responses":{"200":{"description":"OK Captcha solved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The URL for captcha verification"},"headers":{"type":"object","properties":{"sec-ch-device-memory":{"type":"string"},"sec-ch-ua-mobile":{"type":"string"},"sec-ch-ua-arch":{"type":"string"},"sec-ch-ua-platform":{"type":"string"},"sec-ch-ua-model":{"type":"string"},"sec-ch-ua-full-version-list":{"type":"string"}}}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

## Tags

Generates the telemetry ("tags") payload you post to DataDome's collector to raise your session's trust score. See [Tags](/datadome/tags).

## Solve tags

> Solves the tags challenge.

```json
{"openapi":"3.0.0","info":{"title":"DataDome Captcha Solving API","version":"1.0.0"},"servers":[{"url":"https://datadome.hypersolutions.co","description":"Server for captcha solving endpoint"}],"paths":{"/tags":{"post":{"summary":"Solve tags","description":"Solves the tags challenge.","operationId":"solveTags","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userAgent","ddk","referer","type","ip","acceptLanguage","version"],"properties":{"userAgent":{"type":"string","description":"Your chrome UserAgent"},"ddk":{"type":"string","description":"sitekey, static for each site. parse it from the /js/ payload request from browser"},"cid":{"type":"string","description":"Your current datadome cookie"},"referer":{"type":"string","description":"The referer visible as the referer header in the payload POST"},"type":{"type":"string","description":"First time 'ch', second time 'le'"},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."},"acceptLanguage":{"type":"string","description":"Your accept-language header"},"version":{"type":"string","description":"The value you will find in the ddv form field"}}}}}},"responses":{"200":{"description":"OK Interstitial solved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The payload for POST request"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```


# Kasada

API reference for the Hyper Solutions Kasada API: generate x-kpsdk-ct challenge tokens, x-kpsdk-cd challenge data and Vercel BotID x-is-human headers.

{% hint style="info" %}
Tip: Use one of the [SDKs](/start-here/readme-1) instead of directly calling the API.
{% endhint %}

{% hint style="success" %}
Since the request body of the requests will be large, it is highly recommended to apply compression to your request body first. We support the following compression methods: "gzip", "br", "deflate", and "zstd".<br>

Don't forget to set the `content-encoding` header. More info here: [Compression](/api-reference/compression)
{% endhint %}

## Generate challenge token (ct)

## Generate Kasada payload

> Generates a payload to be used in the \`/tl\` POST request

```json
{"openapi":"3.0.0","info":{"title":"Kasada Payload API","version":"1.0.0"},"servers":[{"url":"https://kasada.hypersolutions.co","description":"Kasada payload generation server"}],"paths":{"/payload":{"post":{"summary":"Generate Kasada payload","description":"Generates a payload to be used in the `/tl` POST request","operationId":"generateKasadaPayload","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userAgent","script","ipsLink","ip","acceptLanguage"],"properties":{"userAgent":{"type":"string","description":"The User-Agent string of the browser"},"script":{"type":"string","description":"The script content as a string."},"ipsLink":{"type":"string","description":"The IPS link obtained from the Kasada block page"},"acceptLanguage":{"type":"string","description":"Your accept-language header."},"ip":{"type":"string","description":"The IP that is used to post the sensor data to the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."}}}}}},"responses":{"200":{"description":"OK Payload successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"headers":{"type":"object","properties":{}},"payload":{"type":"string","description":"The generated Kasada protection payload"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

## Generate challenge data (cd)

## Get challenge POW

> Retrieves a challenge POW (\`x-kpsdk-cd\`)

```json
{"openapi":"3.0.0","info":{"title":"Kasada Payload API","version":"1.0.0"},"servers":[{"url":"https://kasada.hypersolutions.co","description":"Kasada payload generation server"}],"paths":{"/cd":{"post":{"summary":"Get challenge POW","description":"Retrieves a challenge POW (`x-kpsdk-cd`)","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["st","ct","domain"],"properties":{"st":{"type":"integer","description":"Timestamp retrieved from the `x-kpsdk-st` response header of the `/tl` request"},"ct":{"type":"string","description":"The latest `x-kpsdk-ct` value. It starts as the `x-kpsdk-ct` from the `/tl` response but is refreshed whenever a later response returns a new `x-kpsdk-ct` header. Always send the most recent value, a stale `ct` will fail."},"workTime":{"type":"integer","description":"Custom workTime value if you are generating POWs in advance"},"fc":{"type":"string","description":"Only used on specific sites. Inquire if your site makes a GET request to /mfc."},"domain":{"type":"string","description":"The domain of the p.js url"}}}}}},"responses":{"200":{"description":"OK POW successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The cd value"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

## Generate Vercel BotID (x-is-human)

## Generate x-is-human header

> Generates a x-is-human header to be used on endpoints protected by Vercel BotID

```json
{"openapi":"3.0.0","info":{"title":"Kasada Payload API","version":"1.0.0"},"servers":[{"url":"https://kasada.hypersolutions.co","description":"Kasada payload generation server"}],"paths":{"/botid":{"post":{"summary":"Generate x-is-human header","description":"Generates a x-is-human header to be used on endpoints protected by Vercel BotID","parameters":[{"in":"header","name":"Content-Type","required":true,"schema":{"type":"string","enum":["application/json"]},"description":"The Content-Type of the request body"},{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userAgent","script","ip","acceptLanguage"],"properties":{"userAgent":{"type":"string","description":"The User-Agent string of the browser"},"script":{"type":"string","description":"The script content as a string."},"acceptLanguage":{"type":"string","description":"Your accept-language header."},"ip":{"type":"string","description":"The IP that is used to interact with the target site. You can use /ip to get the IP from a connection. If you are not using proxies, this will be the IPv4 or IPv6 address of your pc."}}}}}},"responses":{"200":{"description":"OK Payload successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"string","description":"The generated x-is-human header value"}}}}}},"400":{"description":"Bad Request Error occurred","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden Authentication error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```


# Usage & Status Codes

Check your remaining quota with the Usage API, and understand the HTTP status codes the Hyper Solutions API returns.

### Checking your usage

Use the Usage API to monitor your remaining quota and consumption:

## Get usage statistics

> Retrieves detailed usage statistics for all products and plans associated with the API key

```json
{"openapi":"3.0.0","info":{"title":"JustHyped Usage Statistics API","version":"1.0.0"},"servers":[{"url":"https://api.hypersolutions.co","description":"Main server for usage statistics"}],"paths":{"/usage":{"get":{"summary":"Get usage statistics","description":"Retrieves detailed usage statistics for all products and plans associated with the API key","operationId":"getUsageStatistics","parameters":[{"in":"header","name":"x-api-key","required":true,"schema":{"type":"string"},"description":"Your API key for authentication"}],"responses":{"200":{"description":"OK Usage statistics successfully retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"balanceEuro":{"type":"number","format":"float","description":"Current balance in Euros"},"plans":{"type":"array","description":"List of plans with their usage statistics","items":{"type":"object","properties":{"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 date when this plan expires"},"requestsUsed":{"type":"integer","description":"Number of requests used from this plan"},"requestsQuota":{"type":"integer","description":"Total number of requests allocated to this plan"},"requestsRemaining":{"type":"integer","description":"Number of requests remaining (quota - used)"},"product":{"type":"string","description":"Name of the product (e.g., \"akamai\", \"datadome\", \"incapsula\")"},"numRequests":{"type":"integer","description":"Total number of requests for this plan"}}}}}}}}},"401":{"description":"Unauthorized - API key is missing or invalid","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}},"403":{"description":"Forbidden - API key is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}}}}}}}}}}}
```

### Status codes

These are the responses the Hyper Solutions API itself returns:

| Status | Means                          | Fix                                                                                                                                                        |
| ------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | Payload generated successfully | Replay the returned payload and `headers` object on the target                                                                                             |
| `401`  | Missing or invalid API key     | Check the `x-api-key` header and your key at [hypersolutions.co/keys](https://hypersolutions.co/keys). See [Authentication](/api-reference/authentication) |

{% hint style="info" %}
Compress your request bodies, some sensor payloads are large. The SDKs auto-compress bodies over 1000 bytes. See [Compression](/api-reference/compression).
{% endhint %}


# Compression

Our API supports multiple compression algorithms to improve performance and reduce bandwidth usage.

### Supported Algorithms

* **gzip** - Widely supported, good general-purpose compression
* **deflate** - Lightweight compression with broad compatibility
* **brotli** - Modern algorithm with excellent compression ratio
* **zstd** - Recommended for best overall performance

### Using Compression

#### Request Compression

Set the `Content-Encoding` header with your chosen algorithm:

```
Content-Encoding: zstd
```

#### Response Compression

Set the `Accept-Encoding` header in your request:

```
Accept-Encoding: zstd, br, gzip
```

### Recommendations

* **Best Performance**: zstd provides the optimal balance of compression ratio and speed
* **Highest Compatibility**: gzip is supported by virtually all HTTP clients

### SDK Support

* **Go SDK (v1.7.2+)**: Uses zstd compression by default
* **Python/JavaScript SDKs**: Use gzip compression by default

### Performance Comparison

| Algorithm | Speed  | Compression Ratio | Use Case                     |
| --------- | ------ | ----------------- | ---------------------------- |
| zstd      | Fast   | Very good         | Recommended for most cases   |
| brotli    | Slow   | Excellent         | When bandwidth is limited    |
| gzip      | Medium | Good              | When compatibility is needed |
| deflate   | Medium | Fair              | Lightweight option           |

For implementation help, contact our support team.


# User Agents

This page explains the supported user agent configurations and how to maintain them for optimal performance across all Hyper Solutions APIs.

### Overview

Hyper Solutions APIs support two user agent variants for all bot protection bypass services (Akamai, DataDome, Incapsula, and Kasada).

### Supported User Agents

{% hint style="info" %}
Read "Chrome Version Management" to understand what to replace XXX with in these useragents
{% endhint %}

#### Windows User Agent (Recommended)

```
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/XXX.0.0.0 Safari/537.36
```

**Windows remains our recommended default.**

#### macOS User Agent

```
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/XXX.0.0.0 Safari/537.36
```

macOS user agent support is available for specific use cases where Windows user agents may face temporary restrictions or when targeting sites with macOS-specific requirements.

### Chrome Version Management

#### Current Version

At the time of writing, Chrome stable version is **149** (6/17/2026). Your user agent string must reflect the current stable Chrome version to avoid detection.

#### Monitoring Chrome Releases

Track Chrome stable releases at: <https://chromiumdash.appspot.com/schedule>

Chrome follows a roughly 4-week release cycle for stable versions. Monitor this schedule to stay informed about upcoming releases.

#### Update Timeline

**Recommended update strategy:**

1. Monitor the Chrome release schedule for new stable versions
2. Wait 3-7 days after the stable release date
3. Update your user agent string to the new version
4. Update all related headers accordingly

**Why wait 3-7 days?**

* Allows time for real users to naturally update their browsers
* Avoids being among the very first to use a new version
* Ensures stability and widespread adoption of the release

#### Required Updates

When updating to a new Chrome version, you must update:

1. **User Agent String**: Update the version number (e.g., `148.0.0.0` → `149.0.0.0`)
2. **sec-ch-ua Header**: Update to match the new version

   ```
   "Google Chrome";v="149", "Chromium";v="149", "Not)A;Brand";v="24"
   ```
3. **sec-ch-ua-platform Header**:
   * Windows: `"Windows"`
   * macOS: `"macOS"`

**Critical:** All version numbers must be consistent across your entire request configuration. Mismatched versions are a strong indicator of automated traffic.

### Best Practices

#### Version Consistency

* **Keep header versions consistent**: All Chrome version references in your headers (User Agent, sec-ch-ua, etc.) must match each other. Your TLS fingerprint should also match when possible. See Troubleshooting for the narrow case where a slightly older TLS profile is acceptable.
* **Update completely**: When upgrading, update all headers simultaneously
* **Test after updates**: Verify your configuration works after version changes

#### Platform Consistency

* **Stick to one platform**: Don't switch between Windows and macOS mid-session
* **Match all platform indicators**: Ensure all platform-specific headers align
* **Maintain throughout session**: Use the same configuration for all requests in a session

#### Monitoring and Maintenance

* **Set version alerts**: Create reminders for Chrome release dates
* **Test in advance**: Prepare updated configurations before deploying
* **Monitor success rates**: Track performance after version updates
* **Document your configuration**: Keep records of what works for your use cases

### Troubleshooting

#### Detection Issues After Chrome Update

If you experience increased detection after a Chrome release:

* Verify you've updated all version-dependent headers
* Ensure version numbers are consistent across all headers
* Check that you're not updating too early (wait 3-7 days)
* Confirm your TLS fingerprint matches the Chrome version

#### Platform-Specific Blocks

If one platform gets blocked:

* Consider switching to the alternate platform temporarily
* Contact support for guidance on your specific situation
* Monitor if the block is temporary or permanent
* Ensure all platform-specific headers are correctly configured

#### **TLS Client Missing a Profile for the Latest Chrome**

If your TLS client library doesn't yet ship a fingerprint profile for the current Chrome stable version, you can usually fall back to the most recent available profile (e.g., use a Chrome 147 or 148 TLS profile while sending a Chrome 149 user agent). Chrome's TLS ClientHello does not change on every release, so older profiles often remain viable across several versions.

**This is a fallback, not a recommended steady state.** Use it only when:

* Your TLS library has not yet released a matching profile, and
* You've verified the configuration still passes against your target

**Caveats:**

* Some Chrome releases *do* introduce meaningful TLS changes. After releases like these, an older profile will be detected.
* Test your success rate before and after applying this fallback. If you see a drop, your target is fingerprinting at a level the older profile no longer matches.
* Update to a matching profile as soon as your TLS library publishes one. Don't treat this as a long-term configuration.


# Claude Code plugin

The official Hyper Solutions plugin for Claude Code: integrate the Akamai, Incapsula, DataDome, and Kasada APIs and debug blocked requests with live traffic capture or HAR analysis.

The Hyper Solutions plugin teaches Claude Code how our API actually works, so your agent can integrate the SDKs, generate sensors, cookies, and tokens for all four vendors, and find the fingerprint mistake behind a blocked request, without you copy-pasting documentation into the chat.

### Installation

Run these inside Claude Code:

{% code overflow="wrap" %}

```
/plugin marketplace add Hyper-Solutions/hypersolutions-claude-code
/plugin install hypersolutions@hypersolutions
```

{% endcode %}

### What's included

* **The `hypersolutions` skill**: integration knowledge for Akamai (sensor, sec-cpt, SBSD, pixel), Incapsula (reese84, utmvc), DataDome (interstitial, slider, tags), and Kasada (payload, POW, Vercel BotID), across the Go, Python, and JS/TS SDKs or raw REST. It also knows the non-negotiables: browser-grade TLS clients, exact header order, session consistency, and sticky proxies.
* **The powhttp MCP server**: reads live captures from [powhttp](/request-based-basics/installing-powhttp): true header order, TLS fingerprint, and HTTP/2 stream details from the requests your code really sent. Install powhttp and start it under **Settings → MCP Server**. For a full walkthrough of capturing and debugging with powhttp, see the [powhttp guide](https://hypersolutions.co/blog/how-to-use-powhttp-web-scraping).
* **The har-analyzer MCP server**: analyzes an exported HAR against our maintained rule set: header order and casing, `sec-ch-ua` and Chrome version mismatches, duplicate cookies, pseudo-header order, and per-vendor mistakes.

### Authentication

* The **har-analyzer** signs in with your Hyper Solutions account via OAuth. Run `/mcp` in Claude Code and authenticate in the browser on first use.
* Actually calling the Hyper Solutions API requires an [API key](https://hypersolutions.co/keys). See [Authentication](/api-reference/authentication).

{% hint style="info" %}
To record a HAR file for analysis, see [Recording HAR files](/request-based-basics/recording-har-files-for-harvey).
{% endhint %}

### Example prompts

* "Help me set up the Akamai sensor flow in Python with tls-client."
* "My DataDome slider solve returns 403. Capture my script with powhttp and tell me what's wrong."
* "Analyze this HAR and find why I'm getting blocked."
* "Why is my `_abck` cookie never becoming valid?"

{% hint style="info" %}
Watch the plugin build a full request-based scraper from a single prompt in [One prompt, a full airline scraper](https://hypersolutions.co/blog/claude-code-plugin-ai-scraping).
{% endhint %}

{% embed url="<https://github.com/Hyper-Solutions/hypersolutions-claude-code>" %}


# Codex plugin

The official Hyper Solutions plugin for OpenAI Codex: integrate the Akamai, Incapsula, DataDome, and Kasada APIs and debug blocked requests with live traffic capture or HAR analysis.

The Hyper Solutions plugin teaches Codex how our API actually works, so your agent can integrate the SDKs, generate sensors, cookies, and tokens for all four vendors, and find the fingerprint mistake behind a blocked request, without you copy-pasting documentation into the chat.

### Installation

With the [Codex CLI](https://github.com/openai/codex) installed:

{% code overflow="wrap" %}

```
codex plugin marketplace add Hyper-Solutions/hypersolutions-codex
codex plugin add hypersolutions@hypersolutions
codex mcp login har-analyzer
```

{% endcode %}

The last command signs the HAR analyzer in against your Hyper Solutions account (also available under **Settings → MCP servers → From plugins → Authenticate**).

### What's included

* **The `hypersolutions` skill**: integration knowledge for Akamai (sensor, sec-cpt, SBSD, pixel), Incapsula (reese84, utmvc), DataDome (interstitial, slider, tags), and Kasada (payload, POW, Vercel BotID), across the Go, Python, and JS/TS SDKs or raw REST. It also knows the non-negotiables: browser-grade TLS clients, exact header order, session consistency, and sticky proxies.
* **The powhttp MCP server**: reads live captures from [powhttp](/request-based-basics/installing-powhttp): true header order, TLS fingerprint, and HTTP/2 stream details from the requests your code really sent. Install powhttp and start it under **Settings → MCP Server**. For a full walkthrough of capturing and debugging with powhttp, see the [powhttp guide](https://hypersolutions.co/blog/how-to-use-powhttp-web-scraping).
* **The har-analyzer MCP server**: analyzes an exported HAR against our maintained rule set: header order and casing, `sec-ch-ua` and Chrome version mismatches, duplicate cookies, pseudo-header order, and per-vendor mistakes.

### Authentication

* The **har-analyzer** signs in with your Hyper Solutions account via OAuth (`codex mcp login har-analyzer`).
* Actually calling the Hyper Solutions API requires an [API key](https://hypersolutions.co/keys). See [Authentication](/api-reference/authentication).

{% hint style="info" %}
To record a HAR file for analysis, see [Recording HAR files](/request-based-basics/recording-har-files-for-harvey).
{% endhint %}

### Example prompts

* "Help me set up the Akamai sensor flow in Python with tls-client."
* "My DataDome slider solve returns 403. Capture my script with powhttp and tell me what's wrong."
* "Analyze this HAR and find why I'm getting blocked."
* "Why is my `_abck` cookie never becoming valid?"

{% embed url="<https://github.com/Hyper-Solutions/hypersolutions-codex>" %}


