Getting Started
xhr.dev is an on-prem anti-bot challenge solver. You run it as a Docker container inside your own infrastructure — your traffic, proxies, and target sites never touch xhr.dev's servers. We ship you a container image and a signed licence file; everything else runs on your hardware.
It currently solves challenges from:
- Akamai Bot Manager — sensor challenges (
_abck/bm-sz) and SBSD - DataDome — captcha and interstitial challenges
Don't see the provider you need? Message us.
How it fits into your stack
There are two ways to integrate, depending on your architecture:
- Direct HTTP — you give the solver a target URL (and, for DataDome, the captured challenge payload); it fetches whatever it needs, runs the challenge in its sandbox, and optionally submits the result for you. See
POST /akamai/solveandPOST /dd/solve. - MITM proxy hand-off — if you already run a man-in-the-middle proxy in front of your bots (to intercept 403/challenge responses), hand the captured challenge straight to the solver instead of re-fetching it. See
POST /akamai/mitm-solveandPOST /dd/mitm-solve.
Akamai additionally exposes a stateful WebSocket session (/akamai/session) for browser-automation setups (Playwright, Puppeteer) where you want to relay each sensor submission through the bot's own browser context rather than have the solver make the request itself.
Full request/response contracts:
Quickstart
You'll need a licence.json / licence.sig pair — contact us or book a call to get one issued.
mkdir -p licence
# place licence.json and licence.sig from xhr.dev into ./licence
docker pull ghcr.io/xhrdev/xhrdev:latest
docker run -d \
--name xhrdev \
--restart unless-stopped \
-p 3000:3000 \
-v ./licence:/run/licence:ro \
ghcr.io/xhrdev/xhrdev:latest
curl http://localhost:3000/hc
# {"status":"ok"}If the licence is missing, expired, or tampered with, the container exits immediately with {"event":"launcher_failed"} and a non-zero exit code — it fails closed, not open. See the deployment guide for volume mount vs. environment-variable licence delivery, Docker Compose, and the full environment variable reference.
Solving an Akamai challenge (example)
curl -X POST http://localhost:3000/akamai/solve \
-H 'Content-Type: application/json' \
-d '{
"url": "https://target.example.com/login",
"profile": {
"id": "chrome-146-macos",
"chromeFullVersion": "146.0.7680.81",
"os": "macos",
"timezone": "America/New_York",
"timezoneOffsetMinutes": -300,
"tlsClientHello": "chrome_146",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
"httpHeaderTemplates": { "form": [], "iframe": [], "image": [], "xhr": [] }
},
"js_profile": {
"os": "macos",
"chromeVersion": "146",
"hardware": { "webgl": { "vendor": "Google Inc. (Apple)", "renderer": "ANGLE (Apple, ANGLE Metal Renderer: Apple M2, Unspecified Version)" } },
"screen": { "outerHeight": 1080, "innerHeight": 960 }
}
}'{
"success": true,
"accepted": true,
"cookie_header": "_abck=...",
"cookies": { "_abck": "..." },
"mode": "abck",
"outcome": "accepted",
"sensors_sent": 2
}The full field reference, capture-only mode, and error shapes are in the Akamai API reference.
Solving a DataDome challenge (example)
curl -X POST http://localhost:3000/dd/solve \
-H 'Content-Type: application/json' \
-d '{
"url": "https://target.example.com/",
"dd": { "cid": "...", "hsh": "...", "rt": "c", "s": 1 },
"ddCookie": "<datadome cookie value>",
"profile": { "id": "chrome-146-macos", "...": "..." },
"js_profile": { "...": "..." }
}'{ "cookie": "<new datadome cookie value>" }See the DataDome API reference for the full challenge shape, interstitial handling, and submit=false capture mode.
Client integration flow (browser-automation / WebSocket)
For bots driven by a real browser (Playwright, Puppeteer, etc.), the WebSocket flow lets you keep the browser's own cookie jar and TLS fingerprint in the loop:
- Launch a browser and navigate to the target URL.
- Capture the challenge — intercept the Akamai sensor script source, grab the page HTML, and collect current cookies.
- Connect to
ws://host:3000/akamai/session. - Send
initwith the captured script, HTML, cookies, and URL. - Relay each
submissionas a real XHR/fetch in the browser and reply withsubmission_response(status, body, updated cookies). - Watch for
cookie_updatewithaccepted: true— the_abckcookie is now valid. - Close the socket and continue browsing with the accepted cookies.
Full message schemas are in the Akamai API reference.