{"id":25818,"date":"2026-09-18T15:17:35","date_gmt":"2026-09-18T15:17:35","guid":{"rendered":"https:\/\/capskip.com\/?p=25818"},"modified":"2026-09-24T10:54:52","modified_gmt":"2026-09-24T10:54:52","slug":"altcha-nodejs","status":"publish","type":"post","link":"https:\/\/capskip.com\/zh\/altcha-nodejs\/","title":{"rendered":"\u5982\u4f55\u5728 Node.js \u4e2d\u8bc6\u522b ALTCHA \u5e76\u7528 Fetch \u63d0\u4ea4"},"content":{"rendered":"<p>You can solve ALTCHA in Node.js with one call and no browser anywhere in the stack. ALTCHA is proof of work rather than recognition: the site issues a challenge, and the client has to hash until it finds the counter that satisfies it. Nothing has to be looked at, so there is no WebDriver, no headless Chrome and no user agent involved, and the answer is computed rather than guessed. CapSkip added the type in version 1.2.6 and the Node SDK exposes it as a single method. That makes this the rare CAPTCHA type where the whole run is an ordinary HTTP script: fetch the page, read the challenge off it, solve, post the token back, all with global fetch and one SDK call.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What you need<\/h2>\n<ul>\n<li>CapSkip 1.2.6 or later running on a Windows machine. ALTCHA support arrived in that release.<\/li>\n<li>Node 18 or later, which the package requires and which is also where the global fetch used below comes from. TypeScript definitions ship inside the package, so there is no types package to install alongside.<\/li>\n<li>The URL of the page the widget sits on, and the endpoint the widget asks for its challenge.<\/li>\n<li>An address for the solver. Local mode answers on 127.0.0.1 for that device only; Server mode listens on your network address or public IP so another machine can reach it. Step 4 covers which one applies, and both live under <a href=\"https:\/\/capskip.com\/setup-guide\/#connection-settings\">connection settings<\/a>.<\/li>\n<\/ul>\n<div data-no-translation>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># npm install capskip\r\nnpm install capskip<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 1: the solve call, and where the challenge comes from<\/h2>\n<p>One method, two arguments: the page URL, then an options object carrying the challenge. Hand it the endpoint and CapSkip fetches the challenge itself.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ npm install capskip\r\nconst { CapSkip } = require('capskip');\r\n\r\nconst solver = new CapSkip({ host: '127.0.0.1', port: 8080 });\r\n\r\n\/\/ CapSkip fetches the challenge, then hashes until the counter fits.\r\nconst result = await solver.altcha('https:\/\/example.com\/signup', {\r\n  challengeUrl: 'https:\/\/example.com\/altcha\/challenge',\r\n});\r\n\r\nconsole.log(result.token);   \/\/ base64 payload for the form field\r\nconsole.log(result.number);  \/\/ the counter that satisfied it<\/pre>\n<\/div>\n<p>Two fields on the result belong to ALTCHA alone. The token is the base64 payload the form wants, and the number is the counter that solved the challenge. The code field carries the same string as the token, so either one works, but the token is named for the field it goes into and reads better at the call site. The GeeTest fields and the Turnstile user agent stay absent here.<\/p>\n<p>The option name has more than one accepted spelling. Both challengeUrl and challenge_url reach the same API parameter, and the same is true of challengeJson and challenge_json. The camel case spelling is the one the Node docs use and the one that matches the rest of the SDK, so prefer it and stay consistent; the snake case aliases exist so a sample copied from the PHP or Python guide still runs.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">Find the endpoint the widget asks for<\/h3>\n<p>Open DevTools, go to the Network tab and reload the page the widget sits on. The widget makes one request for its challenge, usually to a path with altcha in it. That request URL is what you pass, and the JSON it returns is the challenge document, which you can pass instead.<\/p>\n<p>Do not guess the attribute that names it, because it changed between widget generations. Read the page source.<\/p>\n<table>\n<thead>\n<tr>\n<th>Widget generation<\/th>\n<th>Attribute that names the challenge<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>v1 and v2<\/td>\n<td>challengeurl for an endpoint, with a separate challengejson attribute for an inline challenge<\/td>\n<\/tr>\n<tr>\n<td>v3 and later<\/td>\n<td>challenge, and that same attribute takes either a URL or the challenge data<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div data-no-translation>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;!-- v1 and v2 name the endpoint on its own attribute --&gt;\r\n&lt;altcha-widget challengeurl=&quot;https:\/\/example.com\/altcha\/challenge&quot;&gt;&lt;\/altcha-widget&gt;\r\n\r\n&lt;!-- v3 and later put both forms behind one attribute --&gt;\r\n&lt;altcha-widget challenge=&quot;https:\/\/example.com\/altcha\/challenge&quot;&gt;&lt;\/altcha-widget&gt;<\/pre>\n<\/div>\n<p>The three display styles, native, checkbox and switch, are purely visual. They submit the same payload and the difference never reaches the solver, so you do not have to work out which one you are looking at. ALTCHA documents the attributes in <a href=\"https:\/\/altcha.org\/docs\/v2\/widget-integration\/\" rel=\"nofollow noopener\" target=\"_blank\">its own integration guide<\/a>.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">Passing the challenge document instead<\/h3>\n<p>If your scraper already read the challenge off the page, pass the document and no network request happens at all.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ No fetch happens: the document is already here.\r\nconst result = await solver.altcha('https:\/\/example.com\/signup', {\r\n  challengeJson: {\r\n    algorithm: 'SHA-256',\r\n    challenge: 'YOUR_CHALLENGE_HASH',\r\n    salt: 'YOUR_SALT',\r\n    signature: 'YOUR_SIGNATURE',\r\n    maxnumber: 1000000,\r\n  },\r\n});<\/pre>\n<\/div>\n<p>That option takes an object, which is serialised for you, or a JSON string if you already have one. Sending both the endpoint and the document is allowed and the inline document wins, because fetching would only re-obtain what you just supplied. The two paths behave differently under load, though. An inline challenge that has already expired is refused straight away rather than hashed pointlessly, while an endpoint lets the solver fetch a fresh challenge if the first one died while the job sat in the queue.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">Which algorithms the solver covers<\/h3>\n<p>The same method handles both generations. The legacy scheme is covered with SHA-1, SHA-256, SHA-384 and SHA-512, and proof-of-work v2 is covered with PBKDF2 and iterative SHA. PBKDF2 is the default that ALTCHA itself recommends, so the covered set is the large majority of live sites.<\/p>\n<p>Argon2id and scrypt are the exceptions, and they are refused rather than attempted: a task using one comes back in about a third of a second with ERROR_CAPTCHA_UNSOLVABLE and is never retried. That is deliberate. A memory-hard function is not something a retry fixes, so failing immediately beats looking busy. For ALTCHA that result points at the algorithm rather than at an unreadable image.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 2: do the whole run with fetch, without a browser<\/h2>\n<p>Because there is nothing to render, the page you need the challenge from is just a document you can fetch. That is worth saying plainly, because for every widget CAPTCHA type the honest answer involves a browser somewhere. Here it does not. Fetch the page, pull the attribute out of the markup, and pass it straight to the solver.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ npm install capskip\r\nconst PAGE = 'https:\/\/example.com\/signup';\r\n\r\n\/\/ The page is only a document here: no browser, no rendering.\r\nconst html = await (await fetch(PAGE)).text();\r\n\r\n\/\/ v1 and v2 use challengeurl; v3 and later use challenge.\r\nconst found = html.match(\/(?:challengeurl|challenge)=&quot;([^&quot;]+)&quot;\/i);\r\nif (!found) throw new Error('no ALTCHA widget on this page');\r\n\r\nconst result = await solver.altcha(PAGE, { challengeUrl: found[1] });<\/pre>\n<\/div>\n<p>A regular expression is fine for one known page and a bad idea for a crawler, so reach for a real HTML parser as soon as you are handling markup you did not write. The point of the sample is the shape rather than the parsing: a request, a string, a solve, and no process to launch or tear down. That is also why this type behaves well in a serverless function or a short-lived worker, where the cost of starting Chromium would dwarf the solve.<\/p>\n<p>One caveat on the v3 attribute. It holds either a URL or the challenge document itself, so check which you got before you pass it. If the value starts with a brace rather than a scheme, it is an inline challenge, and it belongs in the document option from the previous section instead.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">Typing the result, if you are on TypeScript<\/h3>\n<p>Definitions ship inside the package, so there is no types package to install. One result type covers every CAPTCHA type the SDK solves, which means each field belonging to only one of them is declared optional. The token and the number are ALTCHA fields, so the compiler types the token as a string or undefined and will not let you hand it to anything expecting a plain string.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ npm install capskip\r\nimport { CapSkip, SolveResult, AltchaOptions } from 'capskip';\r\n\r\nconst options: AltchaOptions = { challengeUrl: found[1] };\r\nconst result: SolveResult = await solver.altcha(PAGE, options);\r\n\r\n\/\/ One check, right after the call, and the type is settled.\r\nif (!result.token) throw new Error('no ALTCHA token on this result');\r\n\r\nconst token: string = result.token;<\/pre>\n<\/div>\n<p>That is the same nudge the Turnstile user agent gives you in <a href=\"https:\/\/capskip.com\/turnstile-nodejs\/\">the Node.js Turnstile guide<\/a>, with a sharper consequence: a missing user agent costs you a rejected submit, while a missing token means you have nothing to submit at all. Reach for the non-null assertion only if you are certain, because it silences the one check that tells you the wrong method was called.<\/p>\n<p>One thing the types will not catch. The options interface carries an index signature, so any extra key you write is accepted by the compiler. A misspelled option therefore builds cleanly and then fails when it runs, because the SDK rejects a parameter ALTCHA does not take. Annotating the options object, as above, at least checks the keys it does know about.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 3: post the token back unchanged, before it expires<\/h2>\n<p>The widget submits its payload in a form field named altcha, so that is where your token goes. This is the step that quietly breaks.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ Send it exactly as it came back: no trimming,\r\n\/\/ no re-encoding, no reordering.\r\nconst response = await fetch('https:\/\/example.com\/signup', {\r\n  method: 'POST',\r\n  body: new URLSearchParams({\r\n    email: 'someone@example.com',\r\n    altcha: token,\r\n  }),\r\n});<\/pre>\n<\/div>\n<p>The token is base64 of a JSON document whose fields are covered by the server&#8217;s own HMAC signature. Any modification invalidates it, so anything that looks like tidying up will break the submit: trimming whitespace, decoding and re-encoding it, or rebuilding the JSON with the keys in a different order. Some integrations read the payload out of a JSON body field rather than a form field, so check what the page&#8217;s own submit sends and mirror that.<\/p>\n<p>The other way this step fails is timing. Challenge windows are short and some sites close them inside two minutes. When one expires, the site refuses the answer with a bare verification failure that looks exactly like a wrong answer, and there is nothing in the response to tell you which of the two happened. Three habits avoid it: fetch the challenge immediately before solving rather than at the top of a long run, submit the token in the same unit of work that solved it, and never hold a token while a person fills in a form.<\/p>\n<p>The client&#8217;s own polling timeouts are not what limits you, because the challenge window closes long before either one does. ALTCHA is CPU work rather than a browser session, so it runs on the default polling timeout and not the longer reCAPTCHA one.<\/p>\n<table>\n<thead>\n<tr>\n<th>Constructor option<\/th>\n<th>Default<\/th>\n<th>What it covers<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>defaultTimeout<\/td>\n<td>120 seconds<\/td>\n<td>ALTCHA and image CAPTCHA polling<\/td>\n<\/tr>\n<tr>\n<td>recaptchaTimeout<\/td>\n<td>300 seconds<\/td>\n<td>reCAPTCHA, Turnstile and GeeTest polling<\/td>\n<\/tr>\n<tr>\n<td>pollingInterval<\/td>\n<td>5 seconds maximum<\/td>\n<td>Polling starts at 0.25 seconds and backs off to this<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 4: where the solver runs, and which connection mode that needs<\/h2>\n<p>The samples above use 127.0.0.1 because that is right when your Node process and the solver share a machine. As soon as the calling code runs somewhere else, such as a container, a CI runner, a VPS or a managed host, loopback no longer points at the solver, and the first solve rejects with a NetworkException.<\/p>\n<p>Switch CapSkip to Server mode and it listens on your network address or public IP instead, so any of those can reach it over the same HTTP API. A static public IP is recommended when the route goes over the internet, with a firewall rule that allows only the addresses you expect. Server mode changes where the solver listens and nothing else: it is still your hardware, and it is still unmetered. Read the host and port from the environment so one build works in both places. The client does not read CAPSKIP_HOST or CAPSKIP_PORT by itself, so pass them to the constructor, as the full example below does.<\/p>\n<table>\n<thead>\n<tr>\n<th>Where the Node process runs<\/th>\n<th>Which connection mode<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>On the CapSkip machine, as a script or a local server<\/td>\n<td>Local mode. 127.0.0.1 is genuinely correct<\/td>\n<\/tr>\n<tr>\n<td>On another box on the same network<\/td>\n<td>Server mode, on that machine&#8217;s private address<\/td>\n<\/tr>\n<tr>\n<td>In a container, on a VPS or on a managed platform<\/td>\n<td>Server mode with a static public IP and a firewall rule<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>One ALTCHA-specific note on proxies. A proxy is supported here, but it is used only for the challenge fetch. There is no browser session to route, so it has no effect on the proof of work itself.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Full working example<\/h2>\n<div data-no-translation>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ npm install capskip\r\nimport { CapSkip, ApiException, TimeoutException, NetworkException } from 'capskip';\r\n\r\nconst solver = new CapSkip({\r\n  host: process.env.CAPSKIP_HOST || '127.0.0.1',\r\n  port: Number(process.env.CAPSKIP_PORT || 8080),\r\n});\r\n\r\nexport async function signUp(email: string) {\r\n  try {\r\n    \/\/ Fetch, solve and submit in one unit of work.\r\n    const result = await solver.altcha('https:\/\/example.com\/signup', {\r\n      challengeUrl: 'https:\/\/example.com\/altcha\/challenge',\r\n    });\r\n\r\n    if (!result.token) throw new Error('not an ALTCHA result');\r\n\r\n    const response = await fetch('https:\/\/example.com\/signup', {\r\n      method: 'POST',\r\n      body: new URLSearchParams({ email, altcha: result.token }),\r\n    });\r\n\r\n    console.log(response.status, 'after counter', result.number);\r\n  } catch (err) {\r\n    \/\/ ERROR_CAPTCHA_UNSOLVABLE here means Argon2id or scrypt.\r\n    if (err instanceof ApiException) console.log('refused:', err.message);\r\n    else if (err instanceof TimeoutException) console.log('gave up waiting');\r\n    else if (err instanceof NetworkException) console.log('solver unreachable');\r\n    else throw err;\r\n  }\r\n}<\/pre>\n<\/div>\n<p>The other types are the same shape with a different method. The reCAPTCHA call takes a sitekey and a page URL, Turnstile works the same way, GeeTest takes a gt value and a challenge alongside the page URL, and image solving takes a file path, a URL or base64. The full method list is on <a href=\"https:\/\/capskip.com\/nodejs-captcha-solver\/\">the Node.js CAPTCHA solver page<\/a>, and the same methods exist in every official package on <a href=\"https:\/\/capskip.com\/captcha-solving-sdk\/\">the SDK page<\/a>.<\/p>\n<p>Turnstile is the one type that needs more than a sitekey when it arrives as a full challenge page. Its extra values are covered in <a href=\"https:\/\/capskip.com\/turnstile-nodejs\/\">the Node.js Turnstile guide<\/a>.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Common errors and what they mean<\/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>The compiler refuses the token, saying a string or undefined is not a string<\/td>\n<td>One result type covers every CAPTCHA type, so ALTCHA-only fields are optional<\/td>\n<td>Narrow it once after the solve, then use the narrowed value<\/td>\n<\/tr>\n<tr>\n<td>A misspelled option builds cleanly and fails when it runs<\/td>\n<td>The options interface has an index signature, so unknown keys are allowed through<\/td>\n<td>Annotate the options object with the ALTCHA options type and check the spelling<\/td>\n<\/tr>\n<tr>\n<td>The token reads undefined at runtime<\/td>\n<td>That field is populated for ALTCHA only<\/td>\n<td>Call the ALTCHA method. On an ALTCHA result the code field holds the same string<\/td>\n<\/tr>\n<tr>\n<td>A bare verification failure from the site, with a token that looks fine<\/td>\n<td>The challenge expired before the form was submitted<\/td>\n<td>Fetch, solve and submit in one unit of work<\/td>\n<\/tr>\n<tr>\n<td>ERROR_CAPTCHA_UNSOLVABLE inside an ApiException, in about a third of a second<\/td>\n<td>The challenge uses Argon2id or scrypt<\/td>\n<td>Nothing to retry. Those two are refused by design<\/td>\n<\/tr>\n<tr>\n<td>A ValidationException on the call<\/td>\n<td>Neither challenge option was supplied, or an option was passed that ALTCHA does not take<\/td>\n<td>Pass the challenge endpoint or the challenge document, and drop anything else<\/td>\n<\/tr>\n<tr>\n<td>A NetworkException on the first solve<\/td>\n<td>CapSkip is not running, or the host and port are wrong<\/td>\n<td>Start CapSkip, then check whether it should be in Local mode or Server mode<\/td>\n<\/tr>\n<tr>\n<td>The form rejects a token your logs show was solved<\/td>\n<td>Something re-encoded, trimmed or reordered the payload<\/td>\n<td>Pass the string straight through, untouched<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">FAQ<\/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 Puppeteer or Playwright for an ALTCHA page?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No, and that is the useful part. ALTCHA hands out a hashing problem rather than something to look at, so the work is CPU only and finishes in milliseconds. No browser, no WebDriver and no user agent are involved. A plain script with global fetch is enough, which also means it runs happily inside a worker, a queue consumer or a serverless function where launching Chromium would be slow and awkward.<\/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 a Node app on a hosted platform reach the solver?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Yes. Switch CapSkip to Server mode under connection settings so it listens on a network address instead of loopback, then point the host environment variable at that address. A container, a CI runner, a VPS or a managed app platform all connect the same way, over the same HTTP API. Use a static public IP if the route crosses the internet, and restrict it with a firewall rule. The solver stays on hardware you own in every one of those cases, so nothing about the licence or the solve count changes.<\/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 the async client solve several ALTCHA challenges faster?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Not by itself. In the Node package the async client is an alias of the ordinary one, not a second implementation, so importing it changes nothing about how the work is done. Every method already returns a promise, so concurrency comes from running several of them together and awaiting the set. Keep each fetch next to its own solve when you do, because challenges expire independently and a batch fetched in advance goes stale while the first few are still hashing.<\/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 I have to use TypeScript to use the SDK?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. The definitions ship inside the package, so they are there if your project reads them and invisible if it does not. Plain CommonJS works exactly as shown in the first sample, and the only difference is that the optional token turns into a runtime check you write yourself rather than one the compiler insists on. The check is worth writing either way, because an undefined token is the clearest signal that the wrong method was called.<\/p>\n<\/details>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The short version<\/h2>\n<p>Read the challenge endpoint off the widget, pass it to the one ALTCHA method along with the page URL, and post the token back into the field named altcha without touching it. In a typed project, narrow the token once after the solve, because one result type covers every CAPTCHA type and the ALTCHA fields are optional on it. Keep the fetch, the solve and the submit in the same block, since the challenge window can close inside two minutes and an expired challenge looks exactly like a wrong answer. Switch to Server mode the moment the Node process stops sharing a machine with the solver.<\/p>\n<ul>\n<li>What the challenge is and how the type works: <a href=\"https:\/\/capskip.com\/altcha-solver\/\">the ALTCHA solver page<\/a>.<\/li>\n<li>Every other method the Node package exposes: <a href=\"https:\/\/capskip.com\/nodejs-captcha-solver\/\">the Node.js solver page<\/a>.<\/li>\n<\/ul>\n<p>One last thing that changes how you design the retry. Because an <a href=\"https:\/\/capskip.com\/\">unlimited captcha solver<\/a> computes the proof of work on a machine you already own, retrying an expired challenge costs a few milliseconds of your own CPU and nothing else, so you can afford to fetch a fresh challenge rather than nursing a stale one.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>ALTCHA \u5c5e\u4e8e proof of work\uff0c\u56e0\u6b64 CapSkip \u7528 hashing \u5373\u53ef\u8bc6\u522b\uff0c\u5b8c\u5168\u4e0d\u9700\u8981\u6d4f\u89c8\u5668\u3002\u8fd9\u91cc\u662f\u4f7f\u7528 global fetch \u7684\u5b8c\u6574\u6d41\u7a0b\uff1a\u4ece\u9875\u9762\u4e0a\u8bfb\u53d6\u6311\u6218\uff0c\u5b8c\u6210\u8bc6\u522b\uff0c\u518d\u628a token \u63d0\u4ea4\u56de\u53bb\u3002<\/p>","protected":false},"author":1,"featured_media":25817,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Solve ALTCHA in Node.js (No Browser) | CapSkip","rank_math_description":"To solve ALTCHA in Node.js you pass a challenge endpoint and get a base64 token back in milliseconds. No browser needed, just one call and a submit.","rank_math_focus_keyword":"solve altcha in node.js","footnotes":""},"categories":[70],"tags":[],"class_list":["post-25818","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\/25818","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=25818"}],"version-history":[{"count":2,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25818\/revisions"}],"predecessor-version":[{"id":26068,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25818\/revisions\/26068"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media\/25817"}],"wp:attachment":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media?parent=25818"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/categories?post=25818"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/tags?post=25818"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}