Handling hCaptcha in Automated Tests and Scraping Pipelines

Handling hCaptcha in Automated Tests and Scraping Pipelines

by admin

A lot of automation breaks at the same spot: a login, sign-up or checkout form that shows an hCaptcha widget. A browser script can fill in every field, but it cannot get through the checkbox or the image rounds that follow, so the run stops there. This post covers what the widget needs from your code and how to get a valid token from an API without anyone clicking anything.

What the form actually checks

When someone passes hCaptcha in a browser, the widget writes a token into a hidden field called h-captcha-response (the token starts with P1_). The form submits that token with everything else, and the site’s server asks hCaptcha whether it is valid. Your automation doesn’t have to imitate clicks on the widget. It needs a valid token in that field when the form is submitted.

Two values tie a token to a page:

  • the sitekey, a UUID in the widget’s data-sitekey attribute or in the sitekey parameter of the hCaptcha script URL;
  • the page URL where the widget is shown.

Some sites, usually ones on hCaptcha Enterprise, also pass an extra rqdata value to the widget. If the page has one, send it along with the request.

Getting a token from an API

A solver API takes the sitekey and URL and returns a token. Here is the request with NoneCap in Python. wait=30 keeps the call open for up to 30 seconds so you get the token in a single response:


import os, requests

res = requests.post(
"https://api.nonecap.com/v1/solves",
params={"wait": 30},
headers={"Authorization": f"Bearer {os.environ['NONECAP_KEY']}"},
json={
"type": "hcaptcha",
"sitekey": "f5ab1c2d-7e8f-4a9b-b1c2-d3e4f5a6b7c8",
"url": "https://target.example/login",
},
)
solve = res.json()
token = solve["token"]
user_agent = solve["user_agent"]

If the solve is still running when the wait window closes, the API returns 202 with no token yet. Poll GET /v1/solves/{id} until it finishes.

Submitting the token

Put the token into the form and submit it the way the page would. With Playwright:


await page.evaluate(
"""t => {
document.querySelector('[name="h-captcha-response"]').value = t;
}""",
token,
)
await page.click("button[type=submit]")

If you post the form with plain HTTP instead, send h-captcha-response=<token> in the body.

Two details cause most refusals:

  1. Use the returned user agent. Each solve returns the user_agent it was made with. Send that value as your User-Agent header when you submit, because some sites refuse a token that arrives from a different browser version.
  2. Submit it soon. A token is meant to be used once, shortly after it is made. Request it just before you submit rather than at the start of a long run.

Telling the solver what happened

A token can pass hCaptcha and still be turned down by the site’s own checks. Only your code sees that outcome, so report the refusals back:


requests.post(
"https://api.nonecap.com/v1/feedback",
headers={"Authorization": f"Bearer {os.environ['NONECAP_KEY']}"},
json={"feedback": [{"solve_id": solve["id"], "outcome": "rejected"}]},
)

Moving code over from 2Captcha

If your pipeline already uses 2Captcha’s in.php / res.php API, you can point it at api.nonecap.com/compat/2captcha and use a NoneCap key as key. With the official 2captcha-python package that is a single argument:


from twocaptcha import TwoCaptcha

solver = TwoCaptcha("nc_live_...", server="api.nonecap.com/compat/2captcha")
result = solver.hcaptcha(sitekey="...", url="https://example.com/login")
print(result["code"])

A CapSolver-compatible endpoint works the same way. Both are covered in the docs.

Before you commit to a service

hCaptcha support across solver services changed a lot over the past year, and some services that used to list it have since dropped it. Run any service against your own sitekey before building on it. Our hCaptcha solver comparison lists what each one supports today, including the cases where another service is the better choice.

Daniel Carlsson runs NoneCap, an API for hCaptcha, Enterprise included.

Related articles

Speed Up WordPress Cartoon
How Document Compression Boosts WordPress Speed: Beyond Traditional Caching

If you’ve already implemented caching on your WordPress site, you’ve taken an important step toward better performance. Your pages load…

Scaling Security: The Enterprise Security Tool Stack
Scaling Security: The Enterprise Security Tool Stack

With today’s rapid pace of software development, security cannot be an afterthought. Organizations will require secure tooling for their DevOps…

Monitoring Unusual Website Activities: A Simple Guide
How to Monitor Unusual Activities on Your Website

Every day, countless potential threats increase your website’s risk for vulnerabilities. Unusual traffic patterns emerge without warning, and suspicious activities…

Ready to get started?

Purchase your first license and see why 1,500,000+ websites globally around the world trust us.