hCaptcha Enterprise rqdata: what it is and how to solve it.
Enterprise hCaptcha binds every challenge to a fresh rqdata blob and the
session it was issued in. A token is only valid if it was minted against that exact
rqdata, which is why solving the image grid alone is not enough. This guide
shows where rqdata lives on a page, how to capture it fresh, the mistakes
that get enterprise tokens refused, and how to get a token enterprise sitekeys accept.
What rqdata actually is
On a regular hCaptcha sitekey, the widget renders, the visitor (or an invisible check)
passes, and hCaptcha injects a P1_ token into the
h-captcha-response field. The token mostly answers one question: was a
challenge passed for this sitekey?
Enterprise sitekeys add a second input. The page passes a signed, per-challenge blob
called rqdata into hcaptcha.render() or
hcaptcha.execute(). That blob encodes session and request context, and it
changes on every challenge. The token hCaptcha returns is bound to the rqdata
it was issued against, so verification now answers a stricter question: was this challenge
passed for this exact rqdata, in this session? Feed a solver stale or
mismatched rqdata and the resulting token is invalid even though the grid was
answered correctly.
You will not find rqdata in hCaptcha’s public developer docs. Its official
SDKs accept the field, call it “custom supplied challenge data”, and refer you to the
Enterprise docs instead. Two details from those SDKs are
worth knowing. The site supplies the value, so it lives in the site’s own code or responses.
And the Android and iOS SDKs moved rqdata out of the widget config and into the
parameters of each verification, which matches how it behaves: one value per challenge, not
one per page.
Where rqdata appears on the page
There are two places a page hands rqdata to hCaptcha. The first is the
render() call that draws the widget:
<!-- The enterprise sitekey passes a fresh rqdata blob into render(). -->
<script>
hcaptcha.render('captcha-box', {
sitekey: 'f5ab1c2d-7e8f-4a9b-b1c2-d3e4f5a6b7c8',
rqdata: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJI...A_per_session_blob',
});
</script>
<!-- after a solve, hCaptcha injects the token as usual: -->
<textarea name="h-captcha-response">P1_eyJ0eXAi...UV8w</textarea>
The second is execute(), which invisible integrations call to start the
check. hCaptcha’s React wrapper documents passing an object into
execute() and says it goes straight through to the underlying JS API,
which is how Enterprise fields like rqdata get in. With
{ async: true } the call returns a Promise that resolves with the token:
hcaptcha
.execute(widgetId, { async: true, rqdata: rqdataFromSite })
.then(({ response }) => submitForm(response));
The same wrapper also exposes a setData() method that it documents only by
pointing to the Enterprise docs, so on some pages the value reaches the widget through
that call instead. Whichever call the page uses, the value is the same thing: the site’s
current rqdata for this challenge.
How to tell a sitekey is enterprise from outside
What you can observe from outside is the integration around the sitekey:
- The page passes
rqdata. The clearest sign. If you see it in arender(),execute()orsetData()call, treat the key as enterprise and send the value. - hCaptcha loads from the site’s own domain. Enterprise first-party hosting
lets visitors connect to something like
hcaptcha.your.cominstead of hCaptcha’s hosts. A working widget with nohcaptcha.comscript on the page is a hint. - Most visitors never see a challenge. Full Passive mode is an Enterprise feature, though Pro accounts get a “99.9% Passive” mode too. Invisible mode on a regular key also hides the widget, so this one alone proves little.
The decisive differences are server-side and only the site owner sees them: siteverify
returns a score and score_reason on Enterprise, and passing
remoteip is what enables the risk score. If you are unsure, sending the enterprise
type is safe: an enterprise sitekey that issues no rqdata works with
type: "hcaptcha_enterprise" and no rqdata field. Finding the
sitekey itself is covered in how to find an hCaptcha sitekey.
Regular vs enterprise at a glance
| Regular hCaptcha | Enterprise hCaptcha | |
|---|---|---|
| Token field | h-captcha-response | h-captcha-response |
| Token prefix | P1_… | P1_… |
Per-challenge rqdata | None | Required when the page supplies it, fresh each render |
| Session / IP binding | Loose | Tight: solve IP must match submit IP |
| Token validity | ~120s, single-use | ~120s, single-use |
| Extra siteverify fields | None | score and score_reason (a risk score; remoteip enables it) |
| Where hCaptcha loads from | js.hcaptcha.com | js.hcaptcha.com, or the site’s own subdomain with first-party hosting |
| Test sitekeys | 10000000-ffff-ffff-ffff-000000000001 | 20000000-ffff-ffff-ffff-000000000002 (safe user), 30000000-ffff-ffff-ffff-000000000003 (bot detected) |
| What a token must prove | A challenge was passed | A challenge was passed for this exact rqdata + session |
| What you send a solver | type + sitekey + url | type + sitekey + url + rqdata |
Capturing rqdata in a browser
Start in DevTools on the page that shows the captcha. In the Sources panel,
search all files for rqdata to find the call that passes it, and set a breakpoint
there to read the live value. In the Network panel, search response bodies
for the same string to find which of the site’s requests delivers it. That request is the
one your automation has to repeat for every solve.
In Playwright or Puppeteer you can log the same thing while the page loads, skipping hCaptcha’s own hosts so only the site’s responses show up:
page.on("response", async (res) => {
if (res.url().includes("hcaptcha.com")) return;
const body = await res.text().catch(() => "");
if (body.includes("rqdata")) console.log(res.status(), res.url());
});
await page.goto("https://target.example/login");
The site’s own field name may differ from rqdata. If the search comes up empty,
break on the render() or execute() call and trace where its argument
came from.
Capturing rqdata in an HTTP pipeline
Once you know which request returns the value, an HTTP client can fetch it without a browser. The order matters more than the code:
- Fetch the page or endpoint that returns
rqdata, with the same cookies and the same exit you will submit from. - Extract the value immediately before you create the solve. Never cache it or share it between sessions.
- Send it with
type: "hcaptcha_enterprise", thesitekey, the pageurl, and the sameproxy. - Submit the token as soon as the solve returns. If anything fails, go back to step 1 for a fresh value.
import re, requests
PROXY = "http://user:[email protected]:8080"
site = requests.Session()
site.proxies = {"http": PROXY, "https": PROXY}
html = site.get("https://target.example/login").text
rqdata = re.search(r'"rqdata"\s*:\s*"([^"]+)"', html).group(1) API = "https://api.nonecap.com/v1/solves"
AUTH = {"Authorization": f"Bearer {NONECAP_KEY}"}
solve = requests.post(f"{API}?wait=90", headers=AUTH, json={
"type": "hcaptcha_enterprise",
"sitekey": "f5ab1c2d-7e8f-4a9b-b1c2-d3e4f5a6b7c8",
"url": "https://target.example/login",
"rqdata": rqdata,
"proxy": PROXY,
"user_agent": site.headers["User-Agent"],
}).json()
while solve["status"] in ("pending", "solving"):
solve = requests.get(f"{API}/{solve['id']}?wait=90", headers=AUTH).json()
if solve["status"] == "solved":
site.post("https://target.example/login",
data={"email": EMAIL, "h-captcha-response": solve["token"]}) The regular expression is a placeholder. Where the value lives (inline script, JSON response, a header) differs per site; match whatever you found in DevTools.
Why rqdata goes stale
Three things expire an rqdata value. It is single-use: once a
challenge has run against it, a second solve on the same value yields a token the site refuses.
It is per challenge: each new page load or render brings a new one and leaves
the old one behind. And it is session-bound: a value fetched in one
cookie session or from one address does not carry over to another. The token it produces then
has its own clock of roughly 120 seconds, so capture, solve and submit have to
run back to back.
Why enterprise is harder than regular hCaptcha
The difficulty is not the image grid. It is the binding around it. An enterprise token has to satisfy three things at once:
- The challenge was passed. Same as regular hCaptcha: the grid (or invisible check) has to clear.
- It was passed against the live
rqdata. The blob is single-use and per-challenge. A token minted a moment too late, or against a different render, does not verify. - The session lines up. Enterprise deployments commonly check that the IP
and session that solved the challenge match the ones submitting the token. Solver vendors
reflect this: NopeCHA, for example, tells users to pass a proxy that matches the submit IP
because, in its words, "most sites will check that the IP used to solve the challenge
matches the IP used to submit the token."
In our own testing a token submitted from a different address than the one that solved it
is refused as
invalid-response, which is why a proxy that stays on one exit is not a nice-to-have on enterprise sitekeys.
Token validity is still short. An hCaptcha token is good for roughly 120 seconds
and is single-use, so the whole capture, solve, and submit loop has to happen inside that
window. With regular hCaptcha you have some slack. With enterprise you have less, because the
rqdata behind it is also expiring.
Common mistakes and what they look like
| Mistake | What you see | Fix |
|---|---|---|
Sending type: "hcaptcha" with no rqdata when the page supplies one | A token comes back and the site refuses it | Send type: "hcaptcha_enterprise" with the page’s live rqdata |
Reusing rqdata from an earlier page load, a retry, or another worker | The token is refused even though the challenge was answered | Capture one fresh value per solve, in the session that will submit |
| Holding the token before submitting | expired-input-response at siteverify (120s default); the widget fires its expired callback | Submit as soon as the solve returns |
| Submitting the same token twice, e.g. on a client retry | already-seen-response at siteverify | Start over: fresh rqdata, new solve, new token |
| Submitting from a different network address than the solve used | Many deployments refuse the token as invalid-response | Pass a proxy that stays on one exit for page load, solve, and submit |
| Using the sitekey of a different widget on the page | Refused when the site passes sitekey to siteverify, which stops a token issued on one sitekey from being redeemed on another | Read the sitekey from the widget that guards the form you submit |
| Dropping the user agent on a site that checks it | On some sites the token is refused without it | Send user_agent from the browser that loaded the page, as is |
The siteverify codes above are what the site’s backend receives; from your side most of them surface as a rejected form. The full list is in hCaptcha error codes.
Why image-recognition APIs struggle here
A pure token or image-recognition service treats a captcha as a vision problem: receive the
challenge images, classify them, return the answer. That works for the regular variant, where
the question really is "which tiles match." Enterprise rqdata moves the goalposts.
The token has to be bound to the exact session the rqdata was issued for, not just
derived from the images. Recognising the grid in isolation does not give you that. You can
answer every tile correctly and still get an invalid token, because the token never matched the
blob.
This is the same reason in-browser stealth tricks fall down on enterprise sitekeys. A patched
automation browser can sometimes pass a regular challenge, but it cannot easily produce the
session signals an enterprise rqdata flow expects. See
solving hCaptcha in Playwright for why the reliable pattern
is to mint the token out of band rather than fight the challenge inside the browser you are
automating.
How NoneCap mints enterprise tokens
NoneCap does not classify images in a vacuum. It returns a finished token bound to the exact
rqdata you pass in, the kind enterprise sitekeys accept and image-recognition
output does not. You send the enterprise type with the per-challenge rqdata
alongside the sitekey and url. Block for the result with
?wait=N (up to 90 seconds), or submit without it and poll the solve id:
curl "https://api.nonecap.com/v1/solves?wait=90" \
-H "Authorization: Bearer $NONECAP_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "hcaptcha_enterprise",
"sitekey": "f5ab1c2d-7e8f-4a9b-b1c2-d3e4f5a6b7c8",
"url": "https://target.example/login",
"rqdata": "eyJ0eXAiOiJKV1QiLCJhbGciOiJI...A_per_session_blob"
}' {
"id": "solve_01HQF7K3JKWZX",
"object": "solve",
"type": "hcaptcha_enterprise",
"status": "solved",
"sitekey": "f5ab1c2d-7e8f-4a9b-b1c2-d3e4f5a6b7c8",
"url": "https://target.example/login",
"token": "P1_eyJ0eXAi...UV8w",
"credits_charged": 2
}
The response is the standard solve object with type: "hcaptcha_enterprise" and a
real P1_ token in the token field. Submit it as the page's
h-captcha-response exactly as you would a regular token. Capture the
rqdata fresh for each challenge, since it is single-use and tied to that session.
An enterprise sitekey that issues no rqdata works too: send the enterprise type
without it. Two optional fields matter on enterprise sitekeys: proxy, so the solve
and your submit share one exit, and user_agent, the user agent of the browser that
loaded the page, which some sites require for the token to be accepted. The full object and
every language sample are in the API reference.
This is where NoneCap is strongest
Enterprise rqdata is the differentiator. It is the case that breaks image-only
solvers and the reason a purpose-built hCaptcha solver matters. If you are evaluating
alternatives, this is the axis to test them on:
-
Moving off NopeCHA, which discontinued hCaptcha on October 2, 2026? The
switch guide shows where its
data.rqdatafield goes on NoneCap. - Against a broad human-and-AI marketplace, see NoneCap vs 2Captcha, where hCaptcha is the headline product rather than one type among many.
When a token-only API is enough
Be honest about the sitekey in front of you. If it is a regular hCaptcha sitekey with no
rqdata, you do not need any of this. A plain image-recognition API, or NoneCap's standardtype: "hcaptcha", will return a valid token, and cheaper broad solvers may be perfectly fine. The enterprise machinery only earns its keep when the page passesrqdataand binds the session. And if your target is reCAPTCHA, Cloudflare Turnstile, or FunCaptcha, NoneCap does not solve those yet (they are on the roadmap), enterprise or otherwise.
Last updated October 2026.
Frequently asked
What exactly is rqdata?
hcaptcha.render() or hcaptcha.execute(). hCaptcha’s SDKs describe it as “custom supplied challenge data”. It encodes session and request context, so the token hCaptcha hands back is bound to that rqdata and the session it was issued in. Submit a token issued against stale or mismatched rqdata and verification fails, even if the challenge itself was solved.Is rqdata documented by hCaptcha?
execute(); the Android and iOS SDKs take it per verification) and point to the Enterprise docs for the details. From outside an enterprise contract, the page that uses it is your reference.Why is enterprise hCaptcha harder to solve than the regular kind?
rqdata, in this session, from an IP that matches the one submitting the token?" That binding is what breaks pure image-recognition solvers: recognising the grid is not enough if the surrounding session does not match. Most enterprise solvers therefore also need a proxy whose IP matches the submit IP.How do I capture the rqdata to send?
rqdata into hcaptcha.render() / hcaptcha.execute(), so grab the value from that call (or from the network request that fetches it) and forward it to NoneCap with type: "hcaptcha_enterprise". Capture it fresh per challenge, since it is single-use and tied to that session.Does every enterprise sitekey use rqdata?
rqdata, send type: "hcaptcha_enterprise" without it. If it does pass one, send it, fresh for each challenge.Can I test an enterprise integration without a live sitekey?
20000000-ffff-ffff-ffff-000000000002 simulates a safe end user and 30000000-ffff-ffff-ffff-000000000003 a detected bot, both with the all-zero test secret. They provide no anti-bot protection, so use them only in test environments. See hCaptcha test sitekeys.