Scientyfic World

Write Structured content faster — Get your copy of DITA Decoded today. Buy it Now

ReCAPTCHA v3 Action Tokens and Why Direct HTTP Reloads Fail

TL;DR: Manually calling the Google reCAPTCHA v3 reload endpoint with an action parameter will not produce a valid action-bound token. The reCAPTCHA v3 workflow relies on the browser’s JavaScript client to collect user context and properly embed the action into the token. Simply sending a POST request to api2/reload with an action field bypasses these client-side steps – resulting in tokens that lack the expected action and are rejected by servers. In short, you can’t trick reCAPTCHA v3 into issuing action-specific tokens with raw HTTP requests alone.

Understanding reCAPTCHA v3 and the “Action” Parameter

Google’s reCAPTCHA v3 is fundamentally different from the old “I am not a robot” puzzles. There’s no user challenge at all – instead, reCAPTCHA v3 runs quietly in the background and generates a risk score (0.0 to 1.0) based on how human-like the browsing behavior is. Site owners define “actions” (like "login", "checkout", "submit_form") to tag each token request, so they know which page or operation the score is for. This action is passed when calling the reCAPTCHA JavaScript API and is encoded into the token that Google returns. Later, when the token is sent to Google’s verify API on the server, Google responds with the score and the action name – allowing the site’s backend to confirm the token was generated for the intended action (e.g. verify that the token’s action == "GetAvailableDaysForOperation"). If the action doesn’t match, or the score is too low, the verification fails.

In other words, reCAPTCHA v3 tokens are context-specific. A token is only “good” for the action it was requested for, and only for a couple minutes. The action parameter is not just a label; it’s tied into Google’s risk analysis and token signature. Any attempt to forge or omit it will lead to an invalid or untrusted token. The experience from the question makes this clear: when the user tried adding an action field to their manual HTTP call, it had no effect on the token content, and the target server responded with “Error while verifying captcha”. Only the genuine browser-executed token (with the action set) was accepted as valid.

How the reCAPTCHA v3 Client-Side Flow Works?

To see why a simple POST request isn’t enough, let’s recap how reCAPTCHA v3 normally works in the browser:

Sequence diagram of the reCAPTCHA workflow (client, server, and Google). In v3, the browser client collects data and requests a token for a specific action. The backend later sends this token to Google for verification (yielding a score and the action name). The token’s integrity and context (action, site, etc.) are enforced by Google’s servers.

  • Browser loads reCAPTCHA scripts: The site includes Google’s reCAPTCHA v3 JavaScript API, usually via <script src="https://www.google.com/recaptcha/api.js?render=SITE_KEY">. This initializes an invisible reCAPTCHA widget in the background. An anchor iframe from Google may load (e.g. .../recaptcha/api2/anchor?k=SITE_KEY&co=...) to establish context and cookies.
  • User triggers an action: When the site needs a token (e.g. form submission or page load event being protected), it calls the JS API:
grecaptcha.execute('SITE_KEY', { action: 'GetAvailableDaysForOperation' })
    .then(function(token) {
        // send token to backend
    });
JavaScript

This is how the action is specified in normal usage. For example, a site might call grecaptcha.execute(..., {action: 'login'}) for a login form. The action is just a string tag – but it must be passed through this API for Google to consider it.

  • reCAPTCHA JS collects data: When execute() is called, the reCAPTCHA v3 client doesn’t just immediately hit an API. It first collects a bunch of telemetry and context about the browser:
    • It may gather signals like user agent, screen dimensions, browser plugin details, click or scroll behavior, timing of events – essentially, a fingerprint of the browsing session. (Google is quite secretive about the exact data, but it’s known to be extensive.)
    • It sets a cookie named _GRECAPTCHA in the browser (associated with *.google.com or *.recaptcha.net). This cookie is used “for the purpose of providing its risk analysis” – likely to correlate the current token request with the initial loading of the widget and possibly the user’s Google account or prior behavior.
  • Network calls to Google’s endpoints: After collecting data, the client JS makes one or more HTTP calls to Google’s reCAPTCHA service to get a token. In reCAPTCHA v3 (non-Enterprise), the flow typically involves:
    • A call to the .../recaptcha/api2/anchor?… endpoint (a GET request) to initialize the challenge and get some initial token (c value). The anchor endpoint responds with HTML that includes a recaptcha-token value (an interim token) and possibly some scripts. This step also often sets the _GRECAPTCHA cookie if not already set.A call to .../recaptcha/api2/reload?k=<sitekey> (a POST request) with a payload including:
      • v (version of the API script),k (site key),c (the token from the anchor step),co (the encoded origin/domain of the site),reason (often "q" for v3 quick execution),the action parameter, among other things.
    Importantly, the action is included in this payload, but not under the obvious name “action” in all cases. Google’s client uses an internal field name. In many observed cases it’s sent as sa (site action) in either the anchor or reload request. For example, a network capture might show sa=vote for an action named “vote”, or data-action='submit' in the HTML attributes. The exact parameter name can vary with API versions (Google often changes internals), but if you send it incorrectly, the server will ignore it.
  • Google returns a token: The response from the reload call contains a JSON-like snippet that includes the “rresp” (response token). This is the g-recaptcha-response value, a long opaque string (starting with 03A… for v3 tokens). This token encapsulates all the information Google wants to convey – the site key, a timestamp, the action name, and the risk score (encrypted), plus integrity signatures. The token is effectively signed/encrypted data that only Google’s servers can interpret during verification.
  • Frontend hands token to backend: The browser now passes this token (via form submission, AJAX, etc.) to your server. The token is associated with the specific user action and browser context that just occurred.
  • Backend verifies token with Google: Your server calls Google’s official site verify API:
POST https://www.google.com/recaptcha/api/siteverify 
secret=YOUR_SECRET_KEY 
response=TOKEN_FROM_USER
JavaScript

Google responds with a JSON containing fields like:

{
  "success": true,
  "score": 0.9,
  "action": "GetAvailableDaysForOperation",
  "challenge_ts": "2025-04-24T03:34:...Z",
  "hostname": "your.site.domain"
}
JSON

The action field here is the action that was originally requested on the client. The site should compare this to the expected action (and check hostname matches, etc.) to prevent token reuse or cross-site attacks. If the action doesn’t match, the token is considered invalid for that request.

  • Site makes a decision: Finally, based on success and score (and possibly action), your backend decides whether the interaction was likely legitimate. For example, if score < 0.5 you might block the action or require additional verification. If all is well, you proceed with the user’s request.

Throughout this process, notice that the browser’s involvement is key. The browser (with Google’s JS) does the heavy lifting of collecting user signals and packaging the token request with all necessary info (site key, action, cookies, fingerprints). The server-side verify step is strictly to double-check the token’s validity and get the risk score – the server can’t generate or modify tokens on its own.

The Pitfalls of Manual HTTP Requests to /reload

Given the above, it becomes clearer why a direct HTTP POST to https://www.google.com/recaptcha/api2/reload might fail to produce a usable token:

  • Missing the Proper Action Field:
    The question scenario showed that adding "action": "..." to the POST data did nothing. That’s because Google’s API wasn’t expecting an action field by that name (and simply ignored it). The actual field name for the action in the reload request is obfuscated. In older versions it was an sa parameter (likely short for “site action”). If you don’t use the exact field name and format that the real client uses, Google effectively receives no action. Thus the returned token will either have a default action (often an empty string or something like “homepage”) or no action at all. When the target server later calls siteverify, the returned action won’t match "GetAvailableDaysForOperation", and the server rightly rejects the token. This was evidenced by the error on verification in the direct-call attempt.
  • No User Context or Behavioral Signals:
    Even if one deduced the correct sa field name or other required params, a raw HTTP client cannot easily mimic the rich browser context that reCAPTCHA v3 relies on. The real grecaptcha.js collects a variety of data (some likely non-obvious like device motion, timings, prior interactions, etc.). A simple requests.post() from a script provides none of this – to Google, it looks like a blind request with minimal headers. This disparity is exactly what reCAPTCHA v3 is designed to detect. Result: tokens generated by scripts tend to get a very low score (0.1, “likely a bot”) or are flagged as suspicious. The target site will treat such tokens as failures (even if the action string matched) because the score is below threshold or other trust checks fail.
  • Cookies and Browser State Omitted:
    As mentioned, reCAPTCHA sets a _GRECAPTCHA cookie when the challenge is initialized. In a normal browser session, this cookie (and possibly others like Google login cookies if present) would be sent along with the reload request. If your manual script didn’t capture and resend cookies, Google might see the reload request as completely stateless. In the asker’s code, for example, they used requests.get and then requests.post without preserving cookies in a session, which means the second request likely went without the cookie. This can cause Google to treat the request as invalid or start a fresh session, potentially returning a replay of the same token or an error. (Using a single requests.Session and copying cookies might fix that particular issue, but it’s only one piece of the puzzle.)
  • Required Parameters Not Fully Understood:
    Google’s API uses several parameters (v, reason, c, co, hl, size, vh, bg, etc.) – some are documented, many are not. The vh and bg fields, for instance, are obscure values likely related to internal checksums or challenge states. The bypass code that the user followed had to scrape or guess these values from the initial page. In fact, the GitHub project they referenced noted steps to find vh and bg from the network payload. These can change over time and might be optional or required depending on Google’s updates. By not executing the real JS, you risk sending stale or incorrect values. Google can detect inconsistencies (e.g., if vh doesn’t match the expected value for the given c token) and might just return a canned response or the same token again. The Stack Overflow question hints that the token came back identical on repeated calls when trying different action variations – a sign that the script was likely reusing some state and Google wasn’t actually updating anything (possibly due to a missing or wrong parameter).
  • Domain Binding and Fingerprinting:
    reCAPTCHA tokens are tied to the domain (or site) they were generated for. The anchor and reload calls include the site’s origin (co parameter) and require the correct site key. If you were to attempt a reload from a different host or without the proper Referer header matching the site domain, Google could flag it. The user did set a Referer to the Google iframe URL and included co in payload, which is good. But subtle things like TLS fingerprint, cipher support, or ordering of headers can still tip Google off that this is not a normal browser. (Google’s risk analysis likely considers network-level signals too.)

In summary, the reCAPTCHA v3 system is built to resist exactly what you tried to do: obtaining tokens via a headless or automated client without running the official JS. The end result of a successful bypass would be a token that says “action=XYZ” and a high score – which essentially means fooling Google into thinking a human with normal behavior just performed XYZ on your site. That’s a very tall order. Google invests heavily in bot detection, and they expect the browser to be involved. Without a real browser environment, your token is either going to lack the action or have a terrible score.

Comparison: Real Browser Execution vs Direct HTTP

Let’s compare the two approaches to highlight why one works and the other doesn’t:

  • Browser-based (Correct Way): Using a real browser (or browser automation like Selenium) calls the official API. For example:
// In browser console or script
grecaptcha.ready(function() {
  grecaptcha.execute('SITE_KEY', {action: 'GetAvailableDaysForOperation'})
    .then(function(token) {
        console.log("Token:", token);
    });
});
JavaScript

This will involve all the steps we discussed: loading the iframe, setting cookies, collecting user activity signals, and finally contacting Google with the proper parameters (including the action). The returned token will be specific to the action "GetAvailableDaysForOperation" and encoded as such. When verified, Google will include "action": "GetAvailableDaysForOperation" in the response, and the token will likely have a decent "score" (assuming the user isn’t acting like a bot). This is why the asker saw correct responses when using Selenium or the Chrome console – the tokens generated in those cases passed the server’s checks.

  • HTTP requests (Attempted Bypass): Using Python requests, the user attempted something like:
# Pseudocode of the attempted manual process
session = requests.Session()
resp1 = session.get("https://www.google.com/recaptcha/api2/anchor?ar=1&k=SITE_KEY&co=...&hl=en&v=<ver>&size=invisible&cb=<rand>",
                    headers={"User-Agent": "...", "Referer": "..."})
# parse resp1.text to extract the value of recaptcha-token
c_token = parse_recaptcha_token(resp1.text)
data = {
    "v": "<ver>",
    "reason": "q",
    "c": c_token,
    "k": "SITE_KEY",
    "co": "<encoded origin>",
    "sa": "GetAvailableDaysForOperation",  # using the correct param name for action
    "hl": "en",
    "size": "invisible",
    # "vh": "...",  # (maybe optional now, some bypass guides say it's no longer required)
    # "bg": "..."   # (big blob from initial payload, possibly optional in newer impl)
}
resp2 = session.post(f"https://www.google.com/recaptcha/api2/reload?k=SITE_KEY", data=data,
                     headers={"User-Agent": "...", "Referer": "https://www.google.com/recaptcha/api2/anchor?…"})
token = extract_rresp(resp2.text)
print("Token:", token)
JavaScript

Even if we send a well-formed request, a few things will happen:

  • If we guess sa correctly, Google might embed "GetAvailableDaysForOperation" in the token’s action field. But since no real user interaction occurred, Google’s risk analysis will likely score this as 0.1 (very bot-like). When the token is verified, "score": 0.1 and "action": "GetAvailableDaysForOperation" come back. The site’s backend will see the correct action, but the low score could cause it to reject the token anyway (depending on the site’s threshold). Many sites expect at least >0.5 for humans.
  • If we send the wrong param name (like the user originally did with "action": "..."), Google ignores it. The token might then default to some previous action or none. In the asker’s case, it came back identical every time – likely meaning no action was applied and Google returned a token for whatever context was last used or a dummy token. That token, when verified, probably had "action": "" (empty) or some generic value, which the target site didn’t accept (since it wanted "GetAvailableDaysForOperation").
  • The token might not even be truly new. Google could detect this pattern of calling anchor and reload in quick succession via script and just return the same token or an error. (The question mentioned the token was identical when including or omitting action, which suggests the bypass didn’t actually change the token at all.
  • Ultimately, the direct method failed – as evidenced by the server’s verification error.

The bottom line is that reCAPTCHA v3 is engineered to be browser-dependent and resist manual replay. It’s not as simple as adding a missing parameter; it’s about the entire verification ecosystem. If you try to cheat one piece (like injecting action without the rest of the puzzle), Google will not give you a usable result.

Why You Can’t Replicate grecaptcha.execute() with a Single POST

To directly answer the main question: “How can I correctly send the action so Google returns an action-specific token via HTTP?”you essentially can’t, not in any “correct” or reliable way. There is no secret API parameter you forgot; the action gets factored in only when the full client script runs as intended.

Let’s break down the fundamental reasons:

  • Action is verified end-to-end:
    The action value is introduced on the client side (in the JS call) and then validated on Google’s server side. By the time your server calls siteverify, the action is baked into the token. You cannot post-edit it. So the only way to get a token for a specific action is to have Google’s servers generate it as such. They will only do that if the request appears to come from a legitimate client specifying that action. A random script sending to an undocumented endpoint does not qualify.
  • Google’s anti-bot logic runs on the client:
    Unlike a normal API where you send a request and get a predictable response, reCAPTCHA’s response depends on an assessment of the client. When using grecaptcha.execute, Google’s code is assessing “Is this a real browser? Did the user actually interact with the site for this action?” If the answer is no, Google might issue a token that is technically valid but carries a low score (or even indicate failure). In other words, you can’t get a high-score token without fooling Google’s detection, and fooling it requires mimicking a real browser and user. A simple POST with an action flag will never achieve that.
  • Undocumented, evolving protocol:
    The internal API (api2/anchor and api2/reload) is not officially documented for external use. It can change without notice. For instance, parameters like vh and bg were once needed, then seemingly became optional or changed format in 2023 (as per the bypass repository’s notes). Even if you momentarily get it working, Google could deploy an update that breaks your approach (and they do this to thwart bot developers). Meanwhile, the public method (grecaptcha.execute) continues to work since Google ensures backward compatibility at the JS API level. Relying on the private endpoints is brittle and not a “correct” solution.
  • Domain locks and keys:
    reCAPTCHA site keys are typically locked to specific domains (configured in the admin console). Google will not return a success:true verification if the token’s domain and site key don’t match the requesting domain. The manual method respects this by using the correct k and co values (so that part is fine). But if you were thinking of skipping the official JS on another site, you’d hit this roadblock. (Not directly the ask here, but relevant to why the protocol isn’t meant for cross-site hacking.)

Given all this, the only robust way to get action-specific v3 tokens is to run the client-side code. This doesn’t necessarily mean a visible browser – you could use headless Chrome or frameworks like Puppeteer/Selenium to automate it – but it means executing Google’s JavaScript in an environment that behaves like a real browser. In fact, anti-captcha services that offer reCAPTCHA v3 solving (e.g. 2Captcha, CapSolver) do exactly that behind the scenes: they spin up a browser with a real user profile to generate tokens with high scores. They also allow you to specify the action (because they’ll pass it into grecaptcha.execute on your behalf). There’s no magic “HTTP-only” shortcut – they simulate a human as closely as possible.

Ethical & Legal Considerations

It’s worth noting that attempting to bypass reCAPTCHA can violate the terms of service of both the website you’re targeting and Google’s API. reCAPTCHA is a security measure to prevent abuse. Defeating it (especially on someone else’s site) may be considered malicious or illegal in some jurisdictions (it could fall under anti-circumvention or anti-hacking laws if done without authorization). At the very least, it’s against Google’s policies to use their service in unintended ways.

For testing purposes on your own site, Google actually provides options: you can use test keys or reCAPTCHA Enterprise’s testing mode where you can get predictable scores. The FAQ explicitly states to create a separate key for automated testing and warns that v3 “relies on seeing real traffic,” implying test automation might not get accurate scores. This reinforces the point – automation is detected and scored differently.

In short: don’t use these techniques for nefarious reasons. Our discussion here is meant for educational understanding. If you legitimately need to simulate users (for load testing, etc.), consider whitelisting certain tokens or using official testing options. And if you’re thinking of abusing it, remember that site owners can detect patterns of low-score tokens and will likely still block you.

Conclusion

ReCAPTCHA v3’s design makes it impractical to obtain valid, action-tagged tokens through a simple HTTP script. The action parameter is not a mere input to a REST API; it’s part of a broader client–server interaction that includes browser fingerprints and user behavior analysis. The attempt to include action in the raw /reload call failed because it bypassed the very steps that give that field meaning (and possibly used the wrong field name on top of that). The resulting tokens carried no valid action or trust signal, so the server rightfully rejected them.

To get action-specific tokens, you must execute the official reCAPTCHA client – either in a real browser or a faithful simulation of one. That ensures the action is correctly embedded and Google’s verification accepts the token. Any shortcut that tries to skip the client will produce tokens that are either invalid or low-reputation. In essence, “correctly including the action parameter” means letting Google’s own code include it for you by using grecaptcha.execute() or equivalent.

So, while it’s an intriguing idea to save time by reusing network calls, reCAPTCHA v3 is a black box for a reason. Embrace the intended integration method – it may be the only viable path. This keeps you on the right side of both functionality and ethics.

Snehasish Konger
Snehasish Konger

Snehasish Konger is a passionate technical writer and the founder of Scientyfic World, a platform dedicated to sharing knowledge on science, technology, and web development. With expertise in React.js, Firebase, and SEO, he creates user-friendly content to help students and tech enthusiasts. Snehasish is also the author of books like DITA Decoded and Mastering the Art of Technical Writing, reflecting his commitment to making complex topics accessible to all.

Articles: 230