{"id":25204,"date":"2026-08-14T01:06:55","date_gmt":"2026-08-14T01:06:55","guid":{"rendered":"https:\/\/capskip.com\/?p=25204"},"modified":"2026-08-14T22:02:59","modified_gmt":"2026-08-14T22:02:59","slug":"error-googlekey-pageurl","status":"publish","type":"post","link":"https:\/\/capskip.com\/zh\/error-googlekey-pageurl\/","title":{"rendered":"\u5982\u4f55\u4fee\u590d\u63d0\u4ea4\u65f6\u7684 ERROR_GOOGLEKEY \u4e0e ERROR_PAGEURL"},"content":{"rendered":"<p>Short answer: <code>ERROR_GOOGLEKEY<\/code> means the <code>googlekey<\/code> value you sent was rejected, and <code>ERROR_PAGEURL<\/code> means the <code>pageurl<\/code> value was. Both come back from <code>\/in.php<\/code> at submit time, so no task was created and there is no captcha ID to poll. Nine times out of ten the cause is a parameter name: reCAPTCHA wants <code>googlekey<\/code>, Turnstile wants <code>sitekey<\/code>, and sending the wrong one produces exactly this. Here is how to tell them apart and fix each.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What the two codes actually check<\/h2>\n<table>\n<thead>\n<tr>\n<th>Code<\/th>\n<th>Documented meaning<\/th>\n<th>Which parameter<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>ERROR_GOOGLEKEY<\/code><\/td>\n<td>Invalid <code>googlekey<\/code> parameter<\/td>\n<td>The sitekey you read off the page<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_PAGEURL<\/code><\/td>\n<td>Invalid <code>pageurl<\/code> parameter<\/td>\n<td>The URL of the page the widget sits on<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>They are validation failures, not solve failures. Nothing has been attempted yet, and nothing is queued.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Submit-time, not poll-time<\/h2>\n<p>This distinction saves a lot of debugging. The API has two stages, and each one produces its own family of errors.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># Stage 1: submit. These codes appear HERE.\ncurl -X POST http:\/\/127.0.0.1:8080\/in.php \\\n  -d &quot;key=YOUR_API_KEY&quot; -d &quot;method=userrecaptcha&quot; \\\n  -d &quot;sitekey=YOUR_SITEKEY&quot; \\\n  -d &quot;pageurl=https:\/\/example.com\/page-with-recaptcha&quot;\n\nERROR_GOOGLEKEY   # no ID came back, so there is nothing to poll<\/pre>\n<\/div>\n<p>Compare that with a healthy submit, which returns <code>OK|2122988149<\/code> and then fails, if it fails at all, on <code>\/res.php<\/code>. A poll-time failure means your parameters were accepted and the solve itself did not work, which is a completely different investigation. See <a href=\"https:\/\/capskip.com\/error-captcha-unsolvable\/\">ERROR_CAPTCHA_UNSOLVABLE<\/a> for that one, and <a href=\"https:\/\/capskip.com\/error-key-does-not-exist\/\">ERROR_KEY_DOES_NOT_EXIST<\/a> if the key itself is being rejected before either check runs.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Why the value gets rejected<\/h2>\n<p>Three causes account for nearly all of these, in this order.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">Cause one: googlekey against sitekey<\/h3>\n<p>The 2captcha-compatible API uses a different parameter name per method, and they are not interchangeable.<\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th>Key parameter<\/th>\n<th>Wrong name gives you<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>userrecaptcha<\/code><\/td>\n<td><code>googlekey<\/code><\/td>\n<td><code>ERROR_GOOGLEKEY<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>turnstile<\/code><\/td>\n<td><code>sitekey<\/code><\/td>\n<td><code>ERROR_BAD_PARAMETERS<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>geetest<\/code><\/td>\n<td><code>gt<\/code> plus <code>challenge<\/code><\/td>\n<td><code>ERROR_BAD_PARAMETERS<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The trap is that everybody calls the value a sitekey in conversation, and the HTML attribute is literally <code>data-sitekey<\/code> for both reCAPTCHA and Turnstile. Only the reCAPTCHA submit parameter is named after Google. If you copied a working Turnstile call and changed <code>method<\/code> to <code>userrecaptcha<\/code>, this is your bug.<\/p>\n<p>The SDKs hide the difference entirely. <code>solver.recaptcha(sitekey, url)<\/code> and <code>solver.turnstile(sitekey, url)<\/code> take the same first argument and map it to the right wire name for you, which is a good reason to use one.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">Cause two: the key is stale, cropped or empty<\/h3>\n<p>Assuming the parameter name is right, the value itself is the next suspect.<\/p>\n<ul>\n<li><strong>You hardcoded it.<\/strong> Sites rotate sitekeys. A constant that worked last month can be dead today.<\/li>\n<li><strong>It came from cached page source.<\/strong> View-source in a browser can serve you an older copy than the one the widget actually rendered from.<\/li>\n<li><strong>You grabbed the wrong attribute.<\/strong> The value lives in <code>data-sitekey<\/code>, not in <code>id<\/code>, <code>name<\/code> or the iframe&#8217;s <code>title<\/code>.<\/li>\n<li><strong>A shell variable brought a newline with it.<\/strong> Command substitution keeps trailing whitespace, and whitespace is not part of a valid key.<\/li>\n<li><strong>It is empty.<\/strong> An unset variable expands to nothing, and the request goes out with <code>googlekey=<\/code>.<\/li>\n<\/ul>\n<p>Read it from the live DOM instead of trusting a constant:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ Run in the page, or via your automation tool's evaluate().\n\/\/ reCAPTCHA and Turnstile both expose it as data-sitekey.\nconst recaptchaKey = document.querySelector('.g-recaptcha')?.dataset.sitekey;\nconst turnstileKey = document.querySelector('.cf-turnstile')?.dataset.sitekey;\n\n\/\/ Fallback: reCAPTCHA also carries it as k= in the widget iframe URL.\nconst iframe = document.querySelector('iframe[src*=&quot;\/recaptcha\/&quot;]');\nconst fromSrc = iframe ? new URL(iframe.src).searchParams.get('k') : null;\n\nconsole.log(recaptchaKey || fromSrc);<\/pre>\n<\/div>\n<p>The <code>k=<\/code> fallback matters on sites that render the widget through JavaScript and never leave a <code>.g-recaptcha<\/code> element in the DOM.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">Cause three: what makes a pageurl invalid<\/h3>\n<p><code>ERROR_PAGEURL<\/code> is a narrower problem, and usually mechanical.<\/p>\n<ul>\n<li><strong>No scheme.<\/strong> <code>example.com\/login<\/code> is not a URL. Send <code>https:\/\/example.com\/login<\/code>.<\/li>\n<li><strong>A relative path.<\/strong> <code>\/login<\/code> is what your scraper had in hand, not what the API needs.<\/li>\n<li><strong>GET truncation.<\/strong> If the page URL contains its own query string and you submit over GET without encoding it, everything after the first ampersand is parsed as a parameter of your API call. POST, or use <code>--data-urlencode<\/code>.<\/li>\n<li><strong>Whitespace or quotes.<\/strong> A URL read out of a config file can arrive wrapped in quotes that were never stripped.<\/li>\n<\/ul>\n<p>The URL does not have to be reachable from the machine running CapSkip, and it does not have to be the URL you eventually POST the form to. It has to be the page the widget is embedded in, because that is what the token gets bound to.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">How this looks from an SDK<\/h2>\n<p>The SDKs check obvious problems before anything leaves your process, and pass the rest through. Two different exceptions, two different fixes.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"># pip install capskip\nfrom capskip import CapSkip, ApiException, ValidationException\n\nsolver = CapSkip(host=&quot;127.0.0.1&quot;, port=8080)\n\ntry:\n    result = solver.recaptcha(\n        sitekey=sitekey_from_dom,\n        url=&quot;https:\/\/example.com\/page-with-recaptcha&quot;,\n    )\nexcept ValidationException:\n    # Caught locally: empty sitekey, malformed URL, missing argument.\n    print(&quot;fix the values before sending&quot;)\nexcept ApiException as e:\n    # Came back from the API: GOOGLEKEY or PAGEURL rejected.\n    print(f&quot;rejected at submit: {e}&quot;)<\/pre>\n<\/div>\n<p>A <code>ValidationException<\/code> means you never reached the API. An <code>ApiException<\/code> carrying one of these codes means you did, and the value was refused. Same idea in C#, with the base type doing the work:<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">using CapSkip;\n\nvar solver = new CapSkipClient(host: &quot;127.0.0.1&quot;, port: 8080);\n\ntry\n{\n    var result = await solver.RecaptchaAsync(sitekey, pageUrl);\n}\ncatch (ApiException ex)\n{\n    \/\/ ERROR_GOOGLEKEY or ERROR_PAGEURL arrives here.\n    Console.WriteLine(ex.Message);\n}\ncatch (CapSkipError ex)\n{\n    \/\/ Everything else the SDK can throw.\n    Console.WriteLine(ex.Message);\n}<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">A two-minute checklist<\/h2>\n<ol>\n<li>Print the exact request body you are sending. Not the variables, the body.<\/li>\n<li>Check the parameter name against the method: <code>googlekey<\/code> for reCAPTCHA, <code>sitekey<\/code> for Turnstile.<\/li>\n<li>Confirm the value is non-empty and has no trailing newline.<\/li>\n<li>Re-read the sitekey from the live page and compare it to the one you sent.<\/li>\n<li>Confirm <code>pageurl<\/code> starts with <code>http:\/\/<\/code> or <code>https:\/\/<\/code>.<\/li>\n<li>Switch the call to POST if the page URL has a query string.<\/li>\n<\/ol>\n<p>Step one solves this more often than the other five combined, because the mismatch is usually between what you think you are sending and what is on the wire.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Nearby codes and what they mean instead<\/h2>\n<table>\n<thead>\n<tr>\n<th>Code<\/th>\n<th>Means<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>ERROR_WRONG_USER_KEY<\/code><\/td>\n<td>The API key is missing or empty, checked before either of these<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_WRONG_METHOD<\/code><\/td>\n<td>The <code>method<\/code> or <code>action<\/code> value is not one the API knows<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_BAD_PARAMETERS<\/code><\/td>\n<td>A required parameter for that method is missing entirely<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_CAPTCHA_UNSOLVABLE<\/code><\/td>\n<td>Submission worked, the solve did not. Poll-time, not submit-time<\/td>\n<\/tr>\n<tr>\n<td><code>CAPCHA_NOT_READY<\/code><\/td>\n<td>Not an error. Keep polling the same ID<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Every code with its exact wording 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;\">Should I retry after ERROR_GOOGLEKEY?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. The same values will be rejected the same way every time, so a retry loop just wastes wall-clock time and hides the real problem. Fix the value, then submit once. Retrying is the correct response to a poll-time failure, not a submit-time one.<\/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;\">My sitekey is correct but still rejected. Now what?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Check for invisible damage first: a trailing newline from command substitution, quotes from a config file, or a truncated value from a fixed-width database column. Print the length of the string you are sending. A standard reCAPTCHA sitekey is 40 characters, so anything shorter has been clipped somewhere.<\/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;\">Does pageurl have to be publicly reachable?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. It identifies the page the widget belongs to, so a page behind a login works fine. What it cannot be is a placeholder: sending a different domain than the one hosting the widget produces a token the site will reject even when the submit succeeds.<\/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;\">Do Turnstile solves ever return ERROR_GOOGLEKEY?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">They should not, because the <code>turnstile<\/code> method never reads a <code>googlekey<\/code> parameter. Seeing it back from a Turnstile call almost always means <code>method<\/code> is still set to <code>userrecaptcha<\/code> from a copied request. Fix the method, then send <code>sitekey<\/code> rather than <code>googlekey<\/code>.<\/p>\n<\/details>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Summary<\/h2>\n<p>Treat <code>error_googlekey<\/code> and <code>ERROR_PAGEURL<\/code> as spelling mistakes rather than failures. No task was created, no attempt was made, and nothing needs retrying. Check the parameter name against the method, read the sitekey from the live DOM instead of a constant, and send a page URL with its scheme attached. Full parameter tables for every method are on the <a href=\"https:\/\/capskip.com\/recaptcha-solver\/\">reCAPTCHA solver<\/a> page.<\/p>\n<p>Debugging this is cheap on an <a href=\"https:\/\/capskip.com\/\">unlimited captcha solver<\/a> that runs locally. Rejected submits burn no credit and consume no quota, so you can fire the same request twenty times while you narrow down which character is wrong.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u8fd9\u4e24\u4e2a\u9519\u8bef\u7801\u90fd\u662f in.php \u5728\u4efb\u52a1\u521b\u5efa\u4e4b\u524d\u5c31\u8fd4\u56de\u7684\u3002\u5e38\u89c1\u539f\u56e0\u662f\u5728 reCAPTCHA \u9700\u8981 googlekey \u7684\u5730\u65b9\u53d1\u4e86 sitekey\u3002\u4e0b\u9762\u8bb2\u600e\u4e48\u5b9a\u4f4d\u5e76\u9010\u4e2a\u4fee\u597d\u3002<\/p>","protected":false},"author":1,"featured_media":25203,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Fix ERROR_GOOGLEKEY and ERROR_PAGEURL | CapSkip","rank_math_description":"ERROR_GOOGLEKEY and ERROR_PAGEURL are rejected at submit, so no task exists to retry. Here is what each one checks, and the two-minute fix for both.","rank_math_focus_keyword":"error_googlekey","footnotes":""},"categories":[70],"tags":[],"class_list":["post-25204","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\/25204","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=25204"}],"version-history":[{"count":2,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25204\/revisions"}],"predecessor-version":[{"id":25254,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25204\/revisions\/25254"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media\/25203"}],"wp:attachment":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media?parent=25204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/categories?post=25204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/tags?post=25204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}