Akamai Bot Manager is the one anti-bot product where the cookie is the least interesting part of the story. Everyone fixates on _abck. What actually decides whether your next request passes is the sequence of updates _abck went through — and the headers that came back with it.
This post is about three things that break Akamai automation in practice: treating _abck as a one-time token, discarding bm_sz and ak_bmsc because _abck looks like the important one, and ignoring solution.headers because no other solver returns any.
Scope: everything below is for QA automation on properties you are authorised to test.
Three cookies, three jobs
Akamai hands out cookies in layers, and each one answers a different question.
-
ak_bmsc— the anonymous browser session. It is issued before any sensor data runs, it is short-lived, and on its own it gets you a page, not an API. -
bm_sz— the session marker that pairs with_abck. They are set together and they are checked together. A request carrying a healthy_abckbut no matchingbm_szis a very common way to get re-challenged while your logs say "the cookie is fine". -
_abck— the state carrier. It holds the sensor payload and the verdict on that payload.
If you only keep one, keep _abck — but understand that on its own it is half a conversation.
Reading the verdict inside _abck
A _abck value is a ~-separated blob. Two segments matter more than the rest:
- A
~-1~segment means a sensor post is still expected, or the one that was sent was not accepted. - A
~0~segment means the sensor data was accepted and the browser is validated.
A long value can contain both markers in different positions, because the value accumulates state. So the check is not "does the cookie contain ~0~": it is which marker comes last, and whether the segment count went up or down since the previous response.
This is also why status codes lie on Akamai more than on any other product. A 200 OK with a challenge document in the body and a ~-1~ cookie is a block. A 403 is almost the easy case: it is the one failure the status code actually reports.
The rewrite: why replaying the solve value fails
Akamai does not issue _abck once. Most responses carry a Set-Cookie that replaces it with a longer value and a different marker. That is the point of the design: the cookie is a record of the session's behaviour, so the server extends it as the session continues.
Concretely, if you solve once, store the cookie, and replay it for the next two hundred requests, you are replaying state one of a conversation the server has already advanced past. It works — until it does not, usually a few requests in, and usually with no signal other than a challenge page.
The fix is mechanical: keep a per-session cookie jar, and update it from every response, not just from the solve. Then verify the marker after each update rather than trusting the status code.
Headers are part of the credential
This is where Akamai differs from every other solver integration I maintain. Its solve result includes solution.headers — browser headers to reuse in subsequent requests — and the field is explicitly documented as possibly null.
When it is present, replay it. When it is null, fall back to solution.useragent and keep your own header set consistent with it: same Accept-Language, same sec-ch-ua family, same header order. A UA that says Chrome 131 with a sec-ch-ua for Chrome 108 is exactly the sort of mismatch the sensor data is built to catch.
When the marker never flips: task.ApiURL
Some Akamai-protected applications only validate the session after a specific XHR or POST — the price lookup, the search call, the cart estimate. Until that request happens inside the session, _abck stays at ~-1~ no matter how clean the browser is.
For that case the task takes an ApiURL field: the POST target to request within the browser session. The response body is not returned as part of the solve result — you are not scraping that call, you are using it to make the session real. If you are getting valid-looking cookies that are still rejected, a missing ApiURL is the first thing I would check.
The loop, correctly shaped
import os, requests, time BASE = os.environ["SOLVEHUB_URL"].rstrip("/") H = {"Authorization": f"Bearer {os.environ['SOLVEHUB_API_KEY']}"} payload = { "type": "akamai", "task": { "websiteURL": "https://example.com/product/42", "ApiURL": "https://example.com/api/price?sku=42", # optional but often decisive "proxyType": "http", "proxyAddress": "203.0.113.10", "proxyPort": "8080", }, } task_id = requests.post(f"{BASE}/v1/tasks", headers=H, json=payload, timeout=30).json()["taskId"] while True: t = requests.get(f"{BASE}/v1/tasks/{task_id}", headers=H, timeout=30).json() if t["status"] == "completed": sol = t["solution"] break if t["status"] in ("failed", "cancelled"): raise RuntimeError(t.get("errorCode")) time.sleep(2) jar = sol["cookies"] # _abck, bm_sz, ak_bmsc extra = sol.get("headers") or {} # documented as possibly null session = requests.Session() session.cookies.update(jar) session.headers["User-Agent"] = sol["useragent"] session.headers.update(extra) session.proxies = {"http": "http://203.0.113.10:8080", "https": "http://203.0.113.10:8080"} def marker(cookie_value): segs = cookie_value.split("~") return [s for s in segs if s in ("0", "-1")] r = session.get("https://example.com/product/42", timeout=30) session.cookies.update(r.cookies) # keep the rewrite, not just the seed if marker(session.cookies.get("_abck", ""))[-1:] == ["-1"]: raise RuntimeError("challenge in disguise: 200 with a pending sensor marker") Two details in that snippet are the ones people skip. session.cookies.update(r.cookies) after every response — that is the rewrite. And the marker check reading the last marker segment rather than testing for the substring ~0~, because a value that contains both markers is normal and is not a pass.
The five-item check
- Am I replaying the seed cookie, or the updated one from the last response?
- Do I carry
bm_szandak_bmscalongside_abck? - Does the last marker segment read
0, not just exist? - If
solution.headerswas not null, did I actually send them? - Does the page need an
ApiURLPOST before the session is considered real?
The rule
On Akamai the cookie is not a key you are handed, it is a ledger the server keeps updating. Read the last marker, store every rewrite, send back the headers that came with it, and let the body — not the status code — tell you whether you passed.
I maintain SolveHub, a CAPTCHA and anti-bot solving API for authorised testing. The Akamai endpoint, its request fields and the ApiURL behaviour are documented at https://www.tiam.codes/docs/akamai.``
