What is Akamai BMP and how to recognise it
Akamai BMP is the Android SDK sensor flow used by a mobile application. Its generated data travels with the app request in the x-acf-sensor-data header. The task takes SDK configuration instead of a page URL, and it returns a request header instead of an _abck cookie.
Check the application's protected API request and SDK configuration. If the request carries x-acf-sensor-data, collect the BMP SDK version and the app details before creating a task. The app's version and the BMP SDK version are separate values.
For browser _abck cookies or SBSD challenges, use the Akamai web guide instead. A BMP header is not a replacement for those cookies.
Which SDK and app details to send
Read the BMP SDK version from the app's SDK integration, not its application version. The packaged generator currently supports 4.2.1. Supply the Android package name, app version name and integer version code from the same application build.
Use the API server URL configured in the BMP SDK. It identifies the protected app endpoint; it is not a URL to a sensor script. If you supply the optional signing certificate SHA-1, send 40 hexadecimal characters without separators.
deviceId is an optional UUID. On the first call you can omit it, then store the UUID returned by the solve. Supply it on later calls to keep the same catalog device and derived Android ID. The returned UUID is lowercase, and uppercase input identifies the same device.
Optional locale, touch-event, sensor-event and server-challenge inputs let the payload reflect the integration's requirements. The API reference lists their accepted names and limits.
Where the sensor header goes
Copy solution.header into the x-acf-sensor-data request header without decoding, trimming or wrapping the value. Send it with the protected app request, not as a cookie or a browser form response. The generated header does not create a logged-in app session: the request keeps its own authentication, body and required headers.
The solution also reports version, deviceId, deviceManufacturer, deviceModel and osVersion. These describe the payload's device. Reuse the UUID for the same installation; each generation still creates a fresh payload and session keys.
Common mistakes
Do not confuse the BMP SDK version with the application's version. An app update does not tell you which BMP generator to select. If you receive an unsupported-version error, contact support with the SDK version and app package name to request support.
Do not send a short hexadecimal Android ID in deviceId: this input is a UUID. Do not create a fresh UUID for every request when you intend to continue the same installation.
A sensor header is one part of an app request. Replaying it as an _abck cookie, omitting the app's own authentication, or mixing details from different application builds does not reproduce the intended request.
Solve it with SurSolver
Create an AkamaiBmpTask with the SDK version, app package, app version, integer version code and configured server URL. Poll getTaskResult until the task is ready, then send the returned header as x-acf-sensor-data.
Keep the returned device UUID for later calls from the same installation. The API reference contains the request example, optional inputs and full solution shape. A task is charged when it is accepted; a task that finishes as failed returns its original charge once.
The Python script below generates the header, stores the returned device UUID for the next run, and sends it as x-acf-sensor-data on the app's API request. The Akamai BMP Python guide walks through the full flow.
# akamai-bmp-python.py - generate the Akamai BMP header and send it with the app API request.
# Install: pip install requests
# Run: python akamai-bmp-python.py
import os
import time
import requests
# Your SurSolver API key.
CLIENT_KEY = "YOUR_API_KEY"
# The SDK version packaged by SurSolver (currently 4.2.1).
BMP_VERSION = "4.2.1"
# The Android package name of the app.
APP_PACKAGE = "com.example.app"
# The versionName of the app.
APP_VERSION = "1.2.3"
# The versionCode of the app.
APP_VERSION_CODE = 123
# The protected API URL configured in the Akamai SDK.
SDK_SERVER_URL = "https://api.example.com"
# The app's protected API request URL.
APP_API_URL = "https://example.com/app-api"
# Its HTTP method; GET when unset.
APP_API_METHOD = None
# File that keeps the device id between runs; device-id.txt when unset.
DEVICE_ID_FILE = None
# 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 without_empty(fields):
# Optional task fields are sent only when they have a value.
return {name: value for name, value in fields.items() if value not in (None, "")}
def main():
device_file = DEVICE_ID_FILE or "device-id.txt"
# One installation keeps one device id; reuse the id a previous run saved.
device_id = None
if os.path.exists(device_file):
with open(device_file, encoding="utf-8") as handle:
device_id = handle.read().strip()
solution = solve({
"type": "AkamaiBmpTask",
"version": BMP_VERSION,
"appPackage": APP_PACKAGE,
"appVersion": APP_VERSION,
"appVersionCode": APP_VERSION_CODE,
"serverURL": SDK_SERVER_URL,
**without_empty({"deviceId": device_id}),
})
with open(device_file, "w", encoding="utf-8") as handle:
handle.write(solution["deviceId"])
headers = {"x-acf-sensor-data": solution["header"]}
# Add the headers and body the app sends with this request.
response = requests.request(APP_API_METHOD or "GET", APP_API_URL, headers=headers, 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()