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 — see How to integrate for which to pick:
- HTTP clients — no browser. Your code makes the requests (axios, undici, fetch, requests, …), hands the captured challenge to the solver, and sends the solved submission itself. Cheapest and fastest.
- Playwright / browser — when the site needs a real browser anyway. The solver attaches to your page and lets Chrome own the submission, so it carries a real TLS fingerprint and the browser's own cookie jar.
Read next:
- How to integrate — picking an approach
- Using HTTP clients — axios, undici, fetch
- Playwright / browser — browser-driven solving
- SDK — typed request/response definitions
- Akamai API reference · DataDome API reference
- Deployment guide
- OpenAPI spec (browse interactively)
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>" }/dd/solve returns a prepared submission for you to send — DataDome binds the clearance cookie to the IP that submits it, so the request has to come from you. See why you send the submission.
See the DataDome API reference for the full challenge shape and interstitial handling.
Next steps
- How to integrate — pick between HTTP clients and a browser
- Using HTTP clients — the full four-request flow in axios, undici, and fetch
- Playwright / browser — including the Akamai WebSocket session
- SDK — typed request/response definitions for TypeScript