{"id":25208,"date":"2026-08-15T11:12:25","date_gmt":"2026-08-15T11:12:25","guid":{"rendered":"https:\/\/capskip.com\/?p=25208"},"modified":"2026-08-15T11:12:25","modified_gmt":"2026-08-15T11:12:25","slug":"recaptcha-data-s-parameter","status":"publish","type":"post","link":"https:\/\/capskip.com\/zh\/recaptcha-data-s-parameter\/","title":{"rendered":"\u5982\u4f55\u5728 Python \u4e2d\u7528 data-s \u53c2\u6570\u8bc6\u522b reCAPTCHA"},"content":{"rendered":"<p>Google&#8217;s own pages ship an extra value on the reCAPTCHA widget, and it is called <code>data-s<\/code>. Solve one of those the ordinary way and you get a token back that the page then rejects. The fix is one more argument: read <code>data-s<\/code> out of the page HTML, pass it to the solver, and submit the token straight away.<\/p>\n<p>The recaptcha data-s parameter is the piece almost everyone misses, mostly because it does not exist on normal sites. Here is where it lives, what to call it, and a script that works end to end.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What data-s actually is<\/h2>\n<p>It is a short-lived, single-use value that Google attaches to reCAPTCHA widgets on its own properties. The &#8220;unusual traffic&#8221; interstitial on Google Search is the one you will hit most often. The value ties the challenge to that specific page load, so the token you get back is only valid against the same request context.<\/p>\n<p>Three things follow from that, and all three cause failures:<\/p>\n<ul>\n<li><strong>It expires.<\/strong> Treat it like a nonce. Fetch the page, solve, submit. Do not cache it between runs.<\/li>\n<li><strong>It is Google-only.<\/strong> A reCAPTCHA on someone else&#8217;s site has no <code>data-s<\/code>. Sending one anyway is a parameter error, not a harmless extra.<\/li>\n<li><strong>It is not the Enterprise flag.<\/strong> These get confused constantly. <code>data-s<\/code> and <code>enterprise<\/code> are separate options that solve different problems, and the <a href=\"https:\/\/capskip.com\/recaptcha-enterprise-solver\/\">reCAPTCHA Enterprise solver<\/a> page covers the second one.<\/li>\n<\/ul>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What you need<\/h2>\n<ul>\n<li>CapSkip running locally. It listens on <code>127.0.0.1:8080<\/code> by default, and the <a href=\"https:\/\/capskip.com\/setup-guide\/\">setup guide<\/a> covers the install.<\/li>\n<li>Python 3.10 or newer.<\/li>\n<li>The SDK and an HTTP client.<\/li>\n<\/ul>\n<div data-no-translation>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># the CapSkip SDK plus requests for fetching the page\npip install capskip requests<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Where to find the data-s value<\/h2>\n<p>Both values you need sit on the same element. The widget renders as a <code>div<\/code> carrying <code>data-sitekey<\/code> and, on Google&#8217;s pages only, <code>data-s<\/code>:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;div class=&quot;g-recaptcha&quot;\n     data-sitekey=&quot;6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&quot;\n     data-s=&quot;SGVsbG8gdGhlcmUsIHRoaXMgaXMgYSBvbmUtdGltZSB2YWx1ZQ&quot;&gt;\n&lt;\/div&gt;<\/pre>\n<\/div>\n<p>The sitekey is stable and you can hardcode it. The <code>data-s<\/code> value changes on every load, so it has to be scraped at run time:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"># pip install capskip requests\nimport re\nimport requests\n\nPAGE = &quot;https:\/\/www.google.com\/search?q=example&quot;\n\n# Keep one session. The value is tied to the request that\n# produced it, cookies included.\nsession = requests.Session()\nhtml = session.get(PAGE, timeout=30).text\n\nsitekey = re.search(r'data-sitekey=&quot;([^&quot;]+)&quot;', html).group(1)\ndatas   = re.search(r'data-s=&quot;([^&quot;]+)&quot;', html).group(1)\n\nprint(sitekey, datas)   # second value is single use<\/pre>\n<\/div>\n<p>If the second match comes back empty, you are not on a Google page and you do not need this parameter at all. Solve it as an ordinary v2 challenge instead.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Pass it as datas, not data-s<\/h2>\n<p>This is the naming trap. The raw HTTP API takes the parameter exactly as it appears in the HTML, <code>data-s<\/code>. The SDKs cannot use that name, because a hyphen is not legal in a Python keyword argument, so every SDK exposes it as <code>datas<\/code>.<\/p>\n<table>\n<thead>\n<tr>\n<th>Where<\/th>\n<th>Name<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Page HTML<\/td>\n<td><code>data-s<\/code><\/td>\n<\/tr>\n<tr>\n<td>Raw API on <code>in.php<\/code><\/td>\n<td><code>data-s<\/code><\/td>\n<\/tr>\n<tr>\n<td>Python, Node.js, PHP, .NET SDKs<\/td>\n<td><code>datas<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>With that sorted, the solve is one call:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">from capskip import CapSkip\n\nsolver = CapSkip(host=&quot;127.0.0.1&quot;, port=8080)\n\nresult = solver.recaptcha(\n    sitekey=sitekey,\n    url=PAGE,\n    datas=datas,      # the data-s value, scraped seconds ago\n)\n\nprint(result[&quot;code&quot;])   # g-recaptcha-response token<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Full working example<\/h2>\n<p>Fetch, scrape, solve, submit, all on one session so the cookies and the IP stay consistent:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"># pip install capskip requests\nimport re\nimport requests\nfrom capskip import (\n    CapSkip, ApiException, NetworkException, TimeoutException,\n)\n\nPAGE = &quot;https:\/\/www.google.com\/search?q=example&quot;\n\nsession = requests.Session()\nsolver = CapSkip(host=&quot;127.0.0.1&quot;, port=8080, recaptchaTimeout=300)\n\nhtml = session.get(PAGE, timeout=30).text\nsitekey = re.search(r'data-sitekey=&quot;([^&quot;]+)&quot;', html).group(1)\ndatas   = re.search(r'data-s=&quot;([^&quot;]+)&quot;', html).group(1)\n\ntry:\n    result = solver.recaptcha(sitekey=sitekey, url=PAGE, datas=datas)\nexcept NetworkException:\n    raise SystemExit(&quot;CapSkip is not running on 127.0.0.1:8080&quot;)\nexcept TimeoutException:\n    raise SystemExit(&quot;solve took longer than recaptchaTimeout&quot;)\nexcept ApiException as err:\n    raise SystemExit(f&quot;API refused the task: {err}&quot;)\n\n# Submit immediately. The token is good for about two minutes.\nresponse = session.post(\n    PAGE,\n    data={&quot;g-recaptcha-response&quot;: result[&quot;code&quot;]},\n    timeout=30,\n)\nprint(response.status_code)<\/pre>\n<\/div>\n<p>Two details in there matter more than they look. The <code>session<\/code> is reused, so the solve and the submit share cookies. And the submit happens right after the solve, because a reCAPTCHA token stops being accepted roughly two minutes after it is issued.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Behind a proxy<\/h2>\n<p>If you fetched the page through a proxy, solve through the same one. The challenge is bound to the request that created it, and a solve from a different network path is the second most common cause of a rejected token.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">result = solver.recaptcha(\n    sitekey=sitekey,\n    url=PAGE,\n    datas=datas,\n    proxy={&quot;type&quot;: &quot;HTTPS&quot;, &quot;uri&quot;: &quot;user:pass@1.2.3.4:3128&quot;},\n)<\/pre>\n<\/div>\n<p>Proxies work for reCAPTCHA, Turnstile and GeeTest. They do nothing for image CAPTCHAs, which never touch the target site.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The same call without the SDK<\/h2>\n<p>The API is 2captcha compatible and runs on your machine, so any HTTP client will do. Here the parameter keeps its hyphen:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># submit the task, get an ID back\ncurl -s -X POST &quot;http:\/\/127.0.0.1:8080\/in.php&quot; \\\n  -d &quot;key=capskip&quot; \\\n  -d &quot;method=userrecaptcha&quot; \\\n  -d &quot;googlekey=YOUR_SITEKEY&quot; \\\n  -d &quot;pageurl=https:\/\/www.google.com\/search?q=example&quot; \\\n  -d &quot;data-s=THE_DATA_S_VALUE&quot; \\\n  -d &quot;json=1&quot;\n\n# {&quot;status&quot;:1,&quot;request&quot;:&quot;2122988149&quot;}<\/pre>\n<\/div>\n<p>Then poll <code>res.php<\/code> for the token. The full two-call flow, including the polling delays, is in <a href=\"https:\/\/capskip.com\/solve-captcha-curl\/\">solving CAPTCHA with cURL<\/a>.<\/p>\n<p>Other SDKs use the same <code>datas<\/code> spelling. Node.js takes it in the options object, and .NET puts it in the options dictionary:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ npm install capskip\nconst { CapSkip } = require('capskip');\n\nconst solver = new CapSkip({ host: '127.0.0.1', port: 8080 });\n\nconst result = await solver.recaptcha(sitekey, pageUrl, {\n  datas: dataS,        \/\/ same value, same rules\n});\n\nconsole.log(result.code);<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Common errors<\/h2>\n<table>\n<thead>\n<tr>\n<th>What you see<\/th>\n<th>Cause<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>ERROR_GOOGLEKEY<\/code><\/td>\n<td>The sitekey never made it into the request, or the regex matched the wrong attribute<\/td>\n<td>Print the scraped value before solving. Details in <a href=\"https:\/\/capskip.com\/error-googlekey-pageurl\/\">fixing ERROR_GOOGLEKEY<\/a><\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_BAD_PARAMETERS<\/code><\/td>\n<td><code>data-s<\/code> sent as an empty string, or sent to a non-Google page<\/td>\n<td>Only pass it when the attribute is actually present<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_CAPTCHA_UNSOLVABLE<\/code><\/td>\n<td>Usually a stale <code>data-s<\/code>: the page was fetched minutes before the solve<\/td>\n<td>Fetch and solve back to back. See <a href=\"https:\/\/capskip.com\/error-captcha-unsolvable\/\">ERROR_CAPTCHA_UNSOLVABLE<\/a><\/td>\n<\/tr>\n<tr>\n<td>Token accepted by the solver, rejected by Google<\/td>\n<td>Different cookies or a different IP between fetch and submit<\/td>\n<td>Reuse one session, and use the same proxy for both<\/td>\n<\/tr>\n<tr>\n<td><code>NetworkException<\/code><\/td>\n<td>CapSkip is not running<\/td>\n<td>Start the app, confirm the port in settings<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Every parameter and response shape is listed in the <a href=\"https:\/\/capskip.com\/api-docs\/\">API documentation<\/a>.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Frequently asked questions<\/h2>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Do I need data-s on a normal website?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. It only appears on Google&#8217;s own pages. If <code>data-s<\/code> is not in the HTML, leave the argument out entirely and solve it as a standard v2 challenge, the way the <a href=\"https:\/\/capskip.com\/recaptcha-v2-solver\/\">reCAPTCHA v2 solver<\/a> page describes.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Is data-s the same as the Enterprise flag?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No, and they are independent. <code>enterprise=1<\/code> tells the solver which reCAPTCHA product it is dealing with. <code>datas<\/code> carries a one-time value from the page. A Google Search challenge needs the second one, and you can pass both if a page genuinely calls for it.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">How long does the value stay good?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Assume seconds, not minutes. It is issued with the page and dies with it. If your scraper queues pages and solves them later, re-fetch the page at solve time rather than storing the value.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Why is it datas in code but data-s in the HTML?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Because a hyphen cannot appear in a Python keyword argument or a C# identifier. The SDKs drop it and send the hyphenated name on the wire for you. The raw <code>in.php<\/code> call still expects <code>data-s<\/code>.<\/p>\n<\/details>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Summary<\/h2>\n<p>Scrape <code>data-sitekey<\/code> and <code>data-s<\/code> from the same element, pass the second one as <code>datas<\/code>, and submit the token on the same session within a couple of minutes. Skip any of those three and you get a token that looks fine and fails.<\/p>\n<p>The rest of the Python surface is on the <a href=\"https:\/\/capskip.com\/python-captcha-solver\/\">Python CAPTCHA solver<\/a> page. CapSkip does all of this as a local <a href=\"https:\/\/capskip.com\/\">captcha bypass<\/a> service on your own machine, so scraping a page twice to get a fresh value costs you nothing but the request.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Google \u81ea\u6709\u7684 reCAPTCHA \u5c0f\u7ec4\u4ef6\u4f1a\u989d\u5916\u5e26\u4e00\u4e2a data-s \u503c\u3002\u6f0f\u6389\u5b83\uff0ctoken \u5c31\u4f1a\u88ab\u62d2\u7edd\u3002\u4e0b\u9762\u8bb2\u6e05\u695a\u5b83\u85cf\u5728\u54ea\u91cc\u3001\u5728 SDK \u91cc\u8be5\u600e\u4e48\u79f0\u547c\u5b83\uff0c\u4ee5\u53ca\u4e00\u6bb5\u53ef\u4ee5\u76f4\u63a5\u8dd1\u901a\u7684\u811a\u672c\u3002<\/p>","protected":false},"author":1,"featured_media":25207,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Solve reCAPTCHA With the data-s Parameter | CapSkip","rank_math_description":"The recaptcha data-s parameter is a single-use value on Google's own pages. Find it, pass it as datas, submit fast. Full Python script and the raw call.","rank_math_focus_keyword":"recaptcha data-s parameter","footnotes":""},"categories":[70],"tags":[],"class_list":["post-25208","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-captcha"],"_links":{"self":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25208","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/comments?post=25208"}],"version-history":[{"count":1,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25208\/revisions"}],"predecessor-version":[{"id":25255,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25208\/revisions\/25255"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media\/25207"}],"wp:attachment":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media?parent=25208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/categories?post=25208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/tags?post=25208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}