Akamai Bot Manager has no widget to click, so there is nothing to point a solver at until you know what is blocking you. This post does the diagnosis first: you will be able to confirm that a site runs Akamai, name the protection layer that is blocking you, send an AkamaiCookie task through SurSolver, and replay the returned cookies so the edge accepts them.
Check whether the site runs Akamai
Akamai Bot Manager sits at the CDN edge and scores each request. A heavily obfuscated script collects browser and device telemetry and posts it back as {"sensor_data": "..."}; the edge then allows, rate-limits, challenges or blocks the traffic. Nothing is drawn on the page, so you recognise it from what the responses carry.
| Signal | What to look for |
|---|---|
| Cookies | _abck (the validation token), bm_sz (a seed set on the first response), ak_bmsc (HTTP-only, lives for hours), bm_sv, bm_mi. bm_s / bm_sc point to the SBSD layer; sec_cpt belongs to the 428 challenge. |
| Script URL | A same-origin script with a random-looking path that changes between page loads, serving obfuscated JavaScript. |
| Response headers | X-Akamai-* headers such as X-Akamai-Transformed, plus akamai-grn and x-akamai-request-id. |
| Response behaviour | 403 with an "Access Denied" or "Pardon Our Interruption" page, a 429 after a single request, a fake ERR_HTTP2_PROTOCOL_ERROR that looks like downtime, HTTP 428 for the sec-cpt layer. |
| Sensor endpoints | A POST of sensor_data to the script URL itself; /_sec/cp_challenge/verify and /_sec/verify?provider= for the 428 layer. A successful sensor POST answers 201. |
Block-page wording differs from site to site, so lean on the cookie cluster and the script rather than the page text. _abck plus bm_sz plus an obfuscated same-origin script is the strongest combination.
Read the tail of the _abck cookie
The end of the _abck value tells you how far the handshake got. Treat it as a convention, not a contract: some sites need a fixed number of sensor posts, and some never show the final state.
| Tail | Reads as |
|---|---|
~-1~ |
A challenge is outstanding; the edge has not accepted any sensor yet. |
~0~-1~ |
A sensor arrived but was not enough. |
~0~ |
The sensor was accepted. |
Do not judge a cookie by its one-year Expires date. The cookie itself lives for about a year, but the validated state lapses within minutes to hours, so a cookie that looks fresh can already be worthless.
With webSensor: true (see the parameters below), a ready _abck can keep the ~-1~ tail: the task can also finish after at least two POSTs to the sensor path return 200/201 with "success": true. Check the returned cookies against your protected application request.
Capture session cookies after loading the page's scripts. _abck may first be set by the sensor script response, rather than the initial page response.
Check which mode the site uses
- Open the protected page in a new incognito window so the session starts without old cookies.
- Open F12 → Console and run this command to read the
_abckstatus:
document.cookie.match(/(?:^|;\s*)_abck=([^;]+)/)?.[1]?.split('~')[1]
- Move the mouse around the page for 10–15 seconds, run the command again, and check Network for POSTs to the sensor script's path.
| What you observe | What to do |
|---|---|
The status changes from -1 to 0 |
Use normal mode; omit webSensor (defaults to false). |
The status stays at -1 and sensor POSTs return 201 |
Use webSensor: true. |
The status stays at -1 and sensor POSTs are missing or fail |
Check whether the sensor loaded and whether the IP is blocked before choosing a mode. |
If the command returns undefined, the page has no script-visible _abck cookie yet. Check the page response and sensor loading first; -1 alone is not enough to select web sensor mode.
Work out which layer blocks you
Akamai stacks several protections, and clearing one while another is active still gets you blocked. Match what you see to the row below.
| What you observe | Layer | With SurSolver |
|---|---|---|
_abck and bm_sz are set, the page loads, but login, cart or checkout calls get challenged |
Web sensor (_abck) |
AkamaiCookie |
HTTP 428 with JSON containing sec-cp-challenge: true and a provider field |
sec-cpt, a proof-of-work layer beside _abck |
Not a separate task type |
Blocked before any real HTML; bm_s, bm_sc, bm_so cookies or an SBSD script URL |
SBSD, which sits in front of the content and often needs _abck as well |
AkamaiSbsdCookie |
| Calls from a native Android app are rejected and the app embeds Akamai's mobile library | BMP, the mobile counterpart of the web sensor | AkamaiBmpTask |
| You already control TLS, IP and headers and only want sensor payloads computed | Sensor payloads | Not offered; SurSolver returns cookies and headers, not payloads |
The public catalog (GET /v1/task-types) lists AkamaiCookie (also AkamaiCookieProxyLess) for the _abck sensor, with no caller proxy, and AkamaiSbsdCookie for the IP-bound SBSD interstitial, with a required proxy. The web tasks return _abck and cookies, an object that maps every returned cookie name to its value (_abck included), without a userAgent result key. sec-cpt has no task of its own: if your target answers HTTP 428, run a single task first and check the result against the site before you build anything around it.
Collect the task parameters
| Field | Required | What to send |
|---|---|---|
websiteURL |
Yes | The page whose first load serves the sensor, usually login, checkout or a product page rather than the homepage. |
scriptUrls |
Yes | Non-empty list of full script URLs copied from the page. Normal mode: send the pixel and sensor URLs, with no sensor-first requirement. With webSensor: true: put the sensor first, then the pixel and any async script. These other scripts load after the first sensor POST. Use an array even for one script. |
webSensor |
No | false by default. Use the browser check above to decide whether to send true. |
cookies |
With webSensor: true |
Cookies from the page response, including _abck and any bm_sz or ak_bmsc cookies it set, as a Cookie header value. |
verify |
No | Only with webSensor: true: a protected path on the same site, such as /api/account, that must answer 2xx within four seconds after the solve. |
pageData |
No | Captured page HTML (up to 256 KiB), supplied alongside scriptUrls; it does not replace them. |
AkamaiCookie uses no caller proxy: the solve runs on SurSolver's egress, and you replay the result from your own connection. Send the replay browser's userAgent in the task and keep it unchanged when replaying.
Python: solve and replay the _abck session
The script below is the whole flow: create the task with the page URL and the sensor scripts, poll until it is ready, then replay the returned cookies. Set TARGET_URL, SCRIPT_URLS and a SUCCESS_TEXT that only appears in a response the site accepted, so the script can prove the session works instead of trusting a status code.
# akamai-python.py - solve Akamai _abck and request the protected page with the returned
# cookies.
# Install: pip install requests
# Run: python akamai-python.py
import time
import requests
# Your SurSolver API key.
CLIENT_KEY = "sur_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# The protected page whose first load serves the Akamai sensor.
TARGET_URL = "https://example.com/page"
# The /akam/ pixel script and the sensor script, in the order they load.
SCRIPT_URLS = ["https://example.com/script-1.js", "https://example.com/script-2.js"]
# The User-Agent the solve runs with; send the result with this same user agent.
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36"
# Text that appears only in a response the site accepted.
SUCCESS_TEXT = "PASTE_ACCEPTED_MARKER"
API = "https://api.sursolver.com"
POLL_ATTEMPTS = 60
def call(path, body):
# API errors are HTTP 200 with errorId 1; anything else is a transport failure.
response = requests.post(f"{API}{path}", json=body, timeout=30)
response.raise_for_status()
data = response.json()
if data["errorId"] != 0:
raise RuntimeError(f'{data["errorCode"]}: {data["errorDescription"]}')
return data
def solve(task):
created = call("/createTask", {"clientKey": CLIENT_KEY, "task": task})
for _ in range(POLL_ATTEMPTS):
result = call("/getTaskResult", {"clientKey": CLIENT_KEY, "taskId": created["taskId"]})
if result["status"] == "ready":
return result["solution"]
time.sleep(3)
raise TimeoutError(f'task {created["taskId"]} was not ready in {POLL_ATTEMPTS} polls')
def main():
solution = solve({
"type": "AkamaiCookieProxyLess",
"websiteURL": TARGET_URL,
"scriptUrls": SCRIPT_URLS,
"userAgent": USER_AGENT,
})
if not solution.get("cookies"):
raise SystemExit("The solve returned no cookies.")
# Replay with the user agent the solve ran with.
headers = {"User-Agent": USER_AGENT}
response = requests.get(
TARGET_URL,
headers=headers,
cookies=solution["cookies"],
timeout=30,
)
if not response.ok or SUCCESS_TEXT not in response.text:
raise SystemExit(f"The site did not accept the result (HTTP {response.status_code}).")
print("The site accepted the result.")
if __name__ == "__main__":
main()
A ready result carries the validated _abck under its own name and every returned cookie in cookies. userAgent is supplied in the task, not returned. A real solve, cut short, with the site's host replaced by example.com:
{
"errorId": 0,
"status": "ready",
"solution": {
"_abck": "27BE0762A028D5ECC19E445E149B898D~0~YAAQwsQUAiTtaTuhAQAAT69vSxBo…",
"cookies": {
"_abck": "27BE0762A028D5ECC19E445E149B898D~0~YAAQwsQUAiTtaTuhAQAAT69vSxBo…",
"bm_sz": "076AE0D2C46D5B96C3C73CA5059ED703~YAAQ…",
"ak_bmsc": "…"
}
}
}
cookies is an object that maps each cookie name to its value, and _abck is one of them. Set every entry on the protected site's domain with path /. Replay with the same userAgent you sent in the task; it is a required input, not a result key.
A verified run of this exact flow in the default mode returned ready with the _abck segment flipped to ~0~, the accepted-sensor state from the table above. With webSensor: true the tail can stay ~-1~; the task is ready once Akamai has accepted the sensor posts. That state is what the task delivers; whether the next protected request passes also depends on the replay client and the session around it, so confirm it on your own target.
If your own deadline expires, the task is still charged and still readable for the retention window (30 minutes from creation by default), so you can fetch it later with the same taskId.
Plain requests presents Python's TLS fingerprint, not Chrome's. The edge compares the cookie against the live connection, including TLS and HTTP/2 characteristics and header order, so a replay can fail even with the supplied user agent. If it does, replay from a client that mirrors Chrome's TLS, or let a real browser such as Playwright carry the session. Browser headers and user agents covers what has to match.
Node.js
node akamai-node.js — Node 18 or newer, no packages — runs the same flow from constants at the top: the cookies replays as the Cookie header with the task's userAgent, and the success text proves the session. The TLS note above is not Python-specific: any plain HTTP client presents its own fingerprint, so move a rejected replay into a real browser before suspecting the solve.
// akamai-node.js - solve Akamai _abck and request the protected page with the returned cookies.
// Install: Node.js 18 or newer (no packages).
// Run: node akamai-node.js
const { setTimeout: sleep } = require('node:timers/promises');
// Your SurSolver API key.
const CLIENT_KEY = 'sur_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// The protected page whose first load serves the Akamai sensor.
const TARGET_URL = 'https://example.com/page';
// The /akam/ pixel script and the sensor script, in the order they load.
const SCRIPT_URLS = ['https://example.com/script-1.js', 'https://example.com/script-2.js'];
// The User-Agent the solve runs with; send the result with this same user agent.
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36';
// Text that appears only in a response the site accepted.
const SUCCESS_TEXT = 'PASTE_ACCEPTED_MARKER';
const API = 'https://api.sursolver.com';
const POLL_ATTEMPTS = 60;
// API errors are HTTP 200 with errorId 1; anything else is a transport failure.
async function call(path, body) {
const res = await fetch(`${API}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (data.errorId !== 0) throw new Error(`${data.errorCode}: ${data.errorDescription}`);
return data;
}
async function solve(task) {
const created = await call('/createTask', { clientKey: CLIENT_KEY, task });
for (let attempt = 0; attempt < POLL_ATTEMPTS; attempt += 1) {
const result = await call('/getTaskResult', {
clientKey: CLIENT_KEY,
taskId: created.taskId,
});
if (result.status === 'ready') return result.solution;
await sleep(3000);
}
throw new Error(`task ${created.taskId} was not ready in ${POLL_ATTEMPTS} polls`);
}
// Optional task fields are sent only when they have a value.
function withoutEmpty(fields) {
return Object.fromEntries(
Object.entries(fields).filter(([, value]) => value !== undefined && value !== ''),
);
}
function cookieHeader(cookies) {
return Object.entries(cookies)
.map(([name, value]) => `${name}=${value}`)
.join('; ');
}
async function main() {
const solution = await solve({
type: 'AkamaiCookieProxyLess',
websiteURL: TARGET_URL,
scriptUrls: SCRIPT_URLS,
userAgent: USER_AGENT,
});
if (!solution.cookies) throw new Error('The solve returned no cookies.');
// Replay with the user agent the solve ran with.
const headers = {
Cookie: cookieHeader(solution.cookies),
'User-Agent': USER_AGENT,
};
const response = await fetch(TARGET_URL, { headers });
const text = await response.text();
if (!response.ok || !text.includes(SUCCESS_TEXT)) {
throw new Error(`The site did not accept the result (HTTP ${response.status}).`);
}
console.log('The site accepted the result.');
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
SBSD and BMP, briefly
SBSD is a short interstitial that Akamai serves before the real page, recognisable by the bm_s, bm_sc and bm_so cookies. It has its own task, AkamaiSbsdCookie: the interstitial page URL and its scriptUrls, an http:// or socks5:// proxy that the solve and the replay must share, and optionally the interstitial's own cookies and a verify path to check the result against. The Akamai SBSD solver page carries the contract and the replay rules.
BMP is the mobile counterpart: an Android app embeds Akamai's library and attaches sensor proof to the app's API calls. The AkamaiBmpTask takes the app's identity (version, appPackage, appVersion, appVersionCode, serverURL) and returns the module header plus device fields to apply to those calls. There is no page and no cookies in this flow. See the Akamai BMP solver page and the BMP Python integration.
Common failures
- A different user agent or TLS fingerprint on replay. Keep the browser identity supplied in the task. SBSD also binds cookies to the exit IP: keep one sticky proxy session for the solve and replay; the
_abcktask needs no caller proxy. - Reusing an cookies. Trust lasts from a few protected requests to a few hours, and protected actions typically consume it. Solve again after a challenge instead of caching the cookies.
- The wrong URL. Sending the homepage when the sensor is served on the login page gives the solver nothing to work with.
- A datacenter proxy for SBSD. It can be blocked before the sensor runs; see Proxies for captcha solving.
- A layer that was never cleared. A persistent 428, or a block before any HTML, points to sec-cpt or SBSD rather than a bad
_abck; for SBSD useAkamaiSbsdCookie. - Solving blind. Confirm the Akamai signals above before you create a task. A task is charged when it is accepted, and a task that finishes as failed automatically returns its original charge once.
Create an AkamaiCookie task with websiteURL and the sensor scriptUrls, replay the cookies from your own connection with the task's userAgent, and check the protected call for the success text you defined. The Akamai solver page carries the short version, and identify any captcha and extract its parameters helps when you are unsure what a site uses.