{"id":24846,"date":"2026-08-04T19:27:31","date_gmt":"2026-08-04T19:27:31","guid":{"rendered":"https:\/\/capskip.com\/?p=24846"},"modified":"2026-08-04T19:27:31","modified_gmt":"2026-08-04T19:27:31","slug":"capcha-not-ready","status":"publish","type":"post","link":"https:\/\/capskip.com\/zh\/capcha-not-ready\/","title":{"rendered":"\u5982\u4f55\u5728\u8f6e\u8be2\u7ed3\u679c\u65f6\u4fee\u590d CAPCHA_NOT_READY"},"content":{"rendered":"<p>Short answer: <code>CAPCHA_NOT_READY<\/code> is not an error. It is the API telling you the CAPTCHA is still being solved and you asked too early. There is nothing to fix in your request. You just need the right polling rhythm, and a loop that knows when to stop. This guide covers both, plus the read-once behaviour that turns a working script into a confusing one.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What CAPCHA_NOT_READY actually means<\/h2>\n<p>When you submit a CAPTCHA to <code>\/in.php<\/code> you get back an ID, not an answer. Solving happens in the background. You then poll <code>\/res.php<\/code> with that ID until the answer is ready.<\/p>\n<p>Until it is, every poll returns the same string:<\/p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># Poll for the result. Note action=get and the id from \/in.php.\ncurl &quot;http:\/\/127.0.0.1:8080\/res.php?key=YOUR_API_KEY&amp;action=get&amp;id=CAPTCHA_ID&quot;\n\n# Still working:\nCAPCHA_NOT_READY\n\n# Done:\nOK|03AGdBq26Sxo...<\/pre>\n<p>And yes, it is spelled <code>CAPCHA<\/code>, not <code>CAPTCHA<\/code>. That typo has been in the 2captcha API since the beginning. CapSkip is drop-in compatible with that API, so the misspelling is preserved deliberately. If it were corrected, every existing client library checking for the exact string would break. Match it exactly in your code.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Poll on the right schedule<\/h2>\n<p>Most people hit this constantly because they poll immediately after submitting. Different CAPTCHA types take very different amounts of time, so the first check should not happen at the same moment for all of them.<\/p>\n<table>\n<thead>\n<tr>\n<th>Type<\/th>\n<th>Wait before first check<\/th>\n<th>Then retry every<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Image \/ text<\/td>\n<td>1 second<\/td>\n<td>5 seconds<\/td>\n<\/tr>\n<tr>\n<td>reCAPTCHA v2<\/td>\n<td>15 to 20 seconds<\/td>\n<td>5 seconds<\/td>\n<\/tr>\n<tr>\n<td>reCAPTCHA v3<\/td>\n<td>10 to 15 seconds<\/td>\n<td>5 seconds<\/td>\n<\/tr>\n<tr>\n<td>GeeTest<\/td>\n<td>about 5 seconds<\/td>\n<td>5 seconds<\/td>\n<\/tr>\n<tr>\n<td>Cloudflare Turnstile<\/td>\n<td>about 5 seconds<\/td>\n<td>5 seconds<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Polling faster than every 5 seconds does not make anything solve quicker. It just burns requests.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">A polling loop that terminates<\/h2>\n<p>The shell version, showing the shape clearly:<\/p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># Submit, capture the id, give it a head start, then poll.\nID=$(curl -s &quot;http:\/\/127.0.0.1:8080\/in.php?key=YOUR_API_KEY&amp;method=userrecaptcha&amp;googlekey=YOUR_SITEKEY&amp;pageurl=https:\/\/example.com&quot; | cut -d'|' -f2)\n\nsleep 15\nwhile :; do\n  RES=$(curl -s &quot;http:\/\/127.0.0.1:8080\/res.php?key=YOUR_API_KEY&amp;action=get&amp;id=$ID&quot;)\n  [ &quot;$RES&quot; = &quot;CAPCHA_NOT_READY&quot; ] || break\n  sleep 5\ndone\necho &quot;$RES&quot;<\/pre>\n<p>That loop has a flaw worth naming: it runs forever if something goes wrong upstream. In real code, cap it.<\/p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"># pip install requests\nimport time\nimport requests\n\nBASE = &quot;http:\/\/127.0.0.1:8080&quot;\n\ndef solve_recaptcha(sitekey, page_url, api_key=&quot;YOUR_API_KEY&quot;, timeout=180):\n    task = requests.get(BASE + &quot;\/in.php&quot;, params={\n        &quot;key&quot;: api_key,\n        &quot;method&quot;: &quot;userrecaptcha&quot;,\n        &quot;googlekey&quot;: sitekey,\n        &quot;pageurl&quot;: page_url,\n        &quot;json&quot;: 1,\n    }).json()\n    task_id = task[&quot;request&quot;]\n\n    time.sleep(15)                      # reCAPTCHA needs a head start\n    deadline = time.monotonic() + timeout\n\n    while time.monotonic() &lt; deadline:\n        res = requests.get(BASE + &quot;\/res.php&quot;, params={\n            &quot;key&quot;: api_key,\n            &quot;action&quot;: &quot;get&quot;,\n            &quot;id&quot;: task_id,\n        }).text.strip()\n\n        if res.startswith(&quot;OK|&quot;):\n            return res.split(&quot;|&quot;, 1)[1]\n\n        # Anything that is not the pending string is terminal.\n        if res != &quot;CAPCHA_NOT_READY&quot;:\n            raise RuntimeError(res or &quot;empty response: already read, or bad id&quot;)\n\n        time.sleep(5)\n\n    raise TimeoutError(&quot;gave up after %ss&quot; % timeout)<\/pre>\n<p>Three things make this safe: a deadline so it cannot hang, treating any non-pending string as terminal so real errors surface immediately, and reading the answer exactly once.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Or skip the loop entirely<\/h2>\n<p>If you are using one of the official SDKs, none of the above is your problem. Polling happens inside the call and you get the token back directly, so <code>CAPCHA_NOT_READY<\/code> never reaches your code.<\/p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"># pip install capskip\nfrom capskip import CapSkip\n\nsolver = CapSkip(host=&quot;127.0.0.1&quot;, port=8080)\n\n# Submit and poll happen inside this one call.\nresult = solver.recaptcha(\n    sitekey=&quot;YOUR_SITEKEY&quot;,\n    url=&quot;https:\/\/example.com\/page-with-recaptcha&quot;,\n)\n\nprint(result[&quot;code&quot;])   # token, ready to inject<\/pre>\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 });\nconst result = await solver.recaptcha('YOUR_SITEKEY', 'https:\/\/example.com\/page-with-recaptcha');\n\nconsole.log(result.code);<\/pre>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\/\/ composer require capskip\/capskip\nuse CapSkip\\CapSkip;\n\n$solver = new CapSkip(['host' =&gt; '127.0.0.1', 'port' =&gt; 8080]);\n$result = $solver-&gt;recaptcha('YOUR_SITEKEY', 'https:\/\/example.com\/page-with-recaptcha');\n\necho $result['code'];<\/pre>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\/\/ dotnet add package CapSkip\nusing CapSkip;\n\nvar solver = new CapSkipClient(host: &quot;127.0.0.1&quot;, port: 8080);\nvar result = await solver.RecaptchaAsync(&quot;YOUR_SITEKEY&quot;, &quot;https:\/\/example.com\/page-with-recaptcha&quot;);\n\nConsole.WriteLine(result.Code);<\/pre>\n<p>Full method signatures for all four languages are on the <a href=\"https:\/\/capskip.com\/captcha-solving-sdk\/\">CAPTCHA solving SDK<\/a> page. The raw loop above is still what you want for Go, Java, Ruby, or anything without an official package.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The read-once trap<\/h2>\n<p>This is the one that wastes an afternoon. <strong>Each result can be read only once.<\/strong> Poll again after a successful read and you get an empty response, not the token you already had.<\/p>\n<p>So an empty body does not mean &#8220;still working&#8221;. It means one of two things:<\/p>\n<ul>\n<li>You already retrieved this result and threw it away<\/li>\n<li>The ID does not exist, usually a mangled or truncated ID from parsing <code>OK|ID<\/code><\/li>\n<\/ul>\n<p>Store the token the moment you get it. Do not re-poll to &#8220;confirm&#8221; it.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">When it is genuinely a problem<\/h2>\n<p>If <code>CAPCHA_NOT_READY<\/code> never resolves, the pending string is a symptom rather than the cause. Check these in order:<\/p>\n<table>\n<thead>\n<tr>\n<th>Response<\/th>\n<th>What it means<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>ERROR_CAPTCHA_UNSOLVABLE<\/code><\/td>\n<td>Solving was attempted and failed<\/td>\n<td>Verify the sitekey and pageurl are the live ones, then resubmit<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_WRONG_ID_FORMAT<\/code><\/td>\n<td>The ID is not a valid integer<\/td>\n<td>You are parsing <code>OK|ID<\/code> wrong. Split on the pipe, take field 2<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_GOOGLEKEY<\/code><\/td>\n<td>The sitekey was rejected at submit time<\/td>\n<td>Re-read it from the live page, not from cached source<\/td>\n<\/tr>\n<tr>\n<td>Empty body<\/td>\n<td>Already read, or unknown ID<\/td>\n<td>Store the result on first read<\/td>\n<\/tr>\n<tr>\n<td>Pending past 3 minutes<\/td>\n<td>The solver is not running or not reachable<\/td>\n<td>Confirm the service is up on the configured port. See the <a href=\"https:\/\/capskip.com\/setup-guide\/\">setup guide<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The full list of error strings is 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;\">Is CAPCHA_NOT_READY an error I should log?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Not as an error. It is the normal in-progress state and you will see it several times per solve. Log it at debug level if at all, or you will bury real failures under noise.<\/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 polling faster return the answer sooner?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. Solving time is independent of how often you ask. Every 5 seconds is the documented interval and anything faster is wasted requests.<\/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 spelled CAPCHA and not CAPTCHA?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">It is an old typo in the original 2captcha API that became part of the contract. CapSkip is drop-in compatible with that API, so the string is preserved exactly. Fixing the spelling would break every client that checks 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;\">Can I get the pending state as JSON?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Yes. Add <code>json=1<\/code> to the request and responses come back as an object with <code>status<\/code> and <code>request<\/code> fields instead of plain text. The pending string itself is unchanged.<\/p>\n<\/details>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Summary<\/h2>\n<p>Give the solve a head start before your first check, retry every 5 seconds, treat anything other than <code>CAPCHA_NOT_READY<\/code> as terminal, cap the loop with a deadline, and read the result exactly once. That is the whole pattern.<\/p>\n<p>If you would rather not write the loop at all, CapSkip is a <a href=\"https:\/\/capskip.com\/\">captcha solver<\/a> that runs locally and ships SDKs for Python, Node.js, PHP and .NET that handle polling for you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>CAPCHA_NOT_READY \u8868\u793a\u4f60\u7684\u9a8c\u8bc1\u7801\u4ecd\u5728\u8bc6\u522b\u4e2d\uff0c\u800c\u4e0d\u662f\u51fa\u4e86\u6545\u969c\u3002\u6b63\u786e\u7684\u8f6e\u8be2\u8282\u594f\u3001\u5b89\u5168\u7684\u91cd\u8bd5\u5faa\u73af\uff0c\u4ee5\u53ca\u53ea\u8bfb\u4e00\u6b21\u7684\u9677\u9631\u3002<\/p>","protected":false},"author":1,"featured_media":24845,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Fix CAPCHA_NOT_READY When Polling | CapSkip","rank_math_description":"CAPCHA_NOT_READY is not an error, it means the CAPTCHA is still solving. Here is the correct polling interval, the retry loop, and the code to handle it.","rank_math_focus_keyword":"capcha_not_ready","footnotes":""},"categories":[70],"tags":[],"class_list":["post-24846","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\/24846","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=24846"}],"version-history":[{"count":1,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/24846\/revisions"}],"predecessor-version":[{"id":24847,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/24846\/revisions\/24847"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media\/24845"}],"wp:attachment":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media?parent=24846"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/categories?post=24846"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/tags?post=24846"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}