{"id":24950,"date":"2026-08-06T06:51:58","date_gmt":"2026-08-06T06:51:58","guid":{"rendered":"https:\/\/capskip.com\/?p=24950"},"modified":"2026-08-06T06:51:58","modified_gmt":"2026-08-06T06:51:58","slug":"recaptcha-v3-enterprise-csharp","status":"publish","type":"post","link":"https:\/\/capskip.com\/zh\/recaptcha-v3-enterprise-csharp\/","title":{"rendered":"\u5982\u4f55\u5728 C# \u4e2d\u8bc6\u522b reCAPTCHA v3 Enterprise\uff08.NET SDK\uff09"},"content":{"rendered":"<p>Solving reCAPTCHA v3 Enterprise in C# uses the same method as every other reCAPTCHA type. There is no <code>RecaptchaEnterpriseAsync<\/code>. You call <code>RecaptchaAsync<\/code> and pass two flags: <code>version<\/code> set to <code>v3<\/code>, and <code>enterprise<\/code> set to <code>1<\/code>. Miss either one and you get a token for a different product, which the site will reject.<\/p>\n<p>Here is the full call, what the <code>action<\/code> option actually does, and how to tell which variant you&#8217;re looking at.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Version and enterprise are independent flags<\/h2>\n<p>This trips up almost everyone the first time. Enterprise isn&#8217;t a fourth version of reCAPTCHA. It&#8217;s a different Google product tier that runs both v2 and v3, so the two settings form a genuine 2&#215;2.<\/p>\n<table>\n<thead>\n<tr>\n<th>What the site runs<\/th>\n<th>Options you pass<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>reCAPTCHA v2 checkbox<\/td>\n<td>none<\/td>\n<\/tr>\n<tr>\n<td>reCAPTCHA v2 Enterprise<\/td>\n<td><code>enterprise = 1<\/code><\/td>\n<\/tr>\n<tr>\n<td>reCAPTCHA v3<\/td>\n<td><code>version = \"v3\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>reCAPTCHA v3 Enterprise<\/td>\n<td><code>version = \"v3\"<\/code> and <code>enterprise = 1<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Passing <code>enterprise = 1<\/code> on its own gets you a v2 Enterprise solve, and it will fail on a v3 page. That&#8217;s the single most common cause of a token that comes back fine and then gets rejected.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">How to tell if a page is Enterprise<\/h2>\n<p>Open the page source and look at which object the script calls. Standard reCAPTCHA uses <code>grecaptcha<\/code>. Enterprise uses <code>grecaptcha.enterprise<\/code>.<\/p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\/\/ Enterprise pages call grecaptcha.enterprise, not grecaptcha.\n\/\/ The action name you need is right here in execute().\ngrecaptcha.enterprise.ready(function () {\n  grecaptcha.enterprise.execute(&quot;YOUR_SITEKEY&quot;, { action: &quot;login&quot; })\n    .then(function (token) {\n      \/\/ token gets posted with the form\n    });\n});<\/pre>\n<p>Two more tells: the script tag loads <code>\/recaptcha\/enterprise.js<\/code> instead of <code>\/recaptcha\/api.js<\/code>, and Enterprise sitekeys usually start with <code>6L<\/code> just like standard ones, so the key itself tells you nothing. Trust the script, not the key.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Setup<\/h2>\n<p>CapSkip runs on your own machine, so start the app before any of this works, then install the SDK from NuGet:<\/p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># .NET Standard 2.0, so .NET Framework 4.6.1+, Core 2.0+\n# and every modern .NET release are all supported.\ndotnet add package CapSkip<\/pre>\n<p>Point the client at the port shown in the app:<\/p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">using CapSkip;\n\nvar solver = new CapSkipClient(\n    apiKey: &quot;capskip&quot;,        \/\/ any string when key validation is off\n    host: &quot;127.0.0.1&quot;,\n    port: 8080,\n    recaptchaTimeout: 300);   \/\/ seconds; v3 rarely gets near this<\/pre>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The v3 Enterprise call<\/h2>\n<p>Everything beyond the sitekey and URL goes in an options dictionary:<\/p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">using System.Collections.Generic;\nusing CapSkip;\n\nvar result = await solver.RecaptchaAsync(\n    &quot;YOUR_SITEKEY&quot;,\n    &quot;https:\/\/example.com\/page-with-recaptcha&quot;,\n    new Dictionary&lt;string, object?&gt;\n    {\n        [&quot;version&quot;]    = &quot;v3&quot;,\n        [&quot;enterprise&quot;] = 1,\n        [&quot;action&quot;]     = &quot;login&quot;,   \/\/ match the page exactly\n    });\n\nConsole.WriteLine(result.Code);   \/\/ g-recaptcha-response token<\/pre>\n<p>The signature is <code>RecaptchaAsync(string sitekey, string url, Dictionary&lt;string, object?&gt;? options = null)<\/code>. The dictionary is nullable-valued, so <code>object?<\/code> matters if your project has nullable reference types switched on.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What the action option actually does<\/h2>\n<p><code>action<\/code> is v3-only. On a v2 page it does nothing.<\/p>\n<p>It&#8217;s the label the page passes to <code>grecaptcha.enterprise.execute()<\/code>. Google scores each action separately, and the site&#8217;s backend usually checks that the action in the verification response matches what it expected. If the page says <code>login<\/code> and you solve with the default, your token is valid and still gets thrown out. Copy the string verbatim, including case. Google restricts actions to alphanumerics, slashes and underscores, so there&#8217;s nothing exotic to escape.<\/p>\n<p>Getting it wrong is quiet. Nothing errors, because nothing went wrong on the solving side. You just get a token the endpoint refuses.<\/p>\n<h3 style=\"font-size:1.3rem;line-height:1.4;\">There is no minimum-score option<\/h3>\n<p>Worth stating plainly, because some solving APIs advertise one: you don&#8217;t ask CapSkip for a token at a particular score. The score is Google&#8217;s judgement, produced when your target site verifies the token, and no parameter on the solve request sets a floor for it. If you&#8217;ve seen a <code>min_score<\/code> field elsewhere and gone looking for the equivalent here, that&#8217;s why you can&#8217;t find it.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Submitting the token<\/h2>\n<p>The token goes into the field the page posts, which is normally <code>g-recaptcha-response<\/code>:<\/p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">using System.Net.Http;\n\nusing var http = new HttpClient();\n\nvar form = new FormUrlEncodedContent(new[]\n{\n    new KeyValuePair&lt;string, string&gt;(\n        &quot;g-recaptcha-response&quot;, result.Code),\n    new KeyValuePair&lt;string, string&gt;(&quot;username&quot;, &quot;demo&quot;),\n});\n\nvar response = await http.PostAsync(\n    &quot;https:\/\/example.com\/login&quot;, form);<\/pre>\n<p>One thing you don&#8217;t need here: <code>result.UserAgent<\/code>. It&#8217;s populated for Turnstile only, and it&#8217;s null for every reCAPTCHA solve. If you&#8217;ve copied a helper from Turnstile code, strip that header out rather than sending a null.<\/p>\n<p>reCAPTCHA tokens are also short-lived. Google expires them after two minutes, so solve at the moment you&#8217;re ready to submit, not at the top of a long workflow.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What about v2 Enterprise?<\/h2>\n<p>Same method, different options. Drop <code>version<\/code> and <code>action<\/code>, and add <code>datas<\/code> if Google handed the page a <code>data-s<\/code> value:<\/p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">var v2 = await solver.RecaptchaAsync(sitekey, pageUrl,\n    new Dictionary&lt;string, object?&gt;\n    {\n        [&quot;enterprise&quot;] = 1,\n        [&quot;datas&quot;]      = &quot;YOUR_DATA_S_VALUE&quot;,\n    });<\/pre>\n<p><code>data-s<\/code> shows up on Google&#8217;s own properties and almost nowhere else. If you can&#8217;t find one in the page, you don&#8217;t need it.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Handling failures<\/h2>\n<p>Every SDK exception derives from <code>CapSkipError<\/code>. There&#8217;s one .NET trap worth knowing before you write the catch blocks.<\/p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">using System;\nusing CapSkip;\n\ntry\n{\n    var result = await solver.RecaptchaAsync(sitekey, pageUrl, options);\n}\ncatch (CapSkip.ValidationException) { \/* bad parameters *\/ }\ncatch (NetworkException)            { \/* CapSkip is not running *\/ }\ncatch (ApiException)                { \/* API returned an error *\/ }\ncatch (CapSkip.TimeoutException)    { \/* exceeded recaptchaTimeout *\/ }\ncatch (CapSkipError)                { \/* anything else from the SDK *\/ }<\/pre>\n<p><code>TimeoutException<\/code> and <code>ValidationException<\/code> exist in both <code>System<\/code> and <code>CapSkip<\/code>. With both namespaces imported, an unqualified catch resolves to the <code>System<\/code> type and quietly never fires. Qualify them, or catch <code>CapSkipError<\/code> and inspect it.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Common errors<\/h2>\n<table>\n<thead>\n<tr>\n<th>Code<\/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>Sitekey is empty or malformed<\/td>\n<td>Re-read it from the live page, not from a cached copy<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_PAGEURL<\/code><\/td>\n<td>URL missing or not a full absolute URL<\/td>\n<td>Include the scheme, and use the page the widget is on<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_BAD_PARAMETERS<\/code><\/td>\n<td>An option value is the wrong type<\/td>\n<td>Check <code>enterprise<\/code> is the number 1, not the string &#8220;1&#8221;<\/td>\n<\/tr>\n<tr>\n<td><code>ERROR_CAPTCHA_UNSOLVABLE<\/code><\/td>\n<td>The solve could not be completed<\/td>\n<td>Confirm the version flags match the page, then add a proxy on the same network path<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The full parameter list for every type 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 there a separate method for Enterprise?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. <code>RecaptchaAsync<\/code> handles all four combinations. Enterprise is a flag in the options dictionary, and it works alongside <code>version<\/code> rather than replacing 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;\">What happens if I get the action name wrong?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">You get a perfectly valid token that the site refuses. Google returns the action alongside the score during verification, and most backends compare it to what they expected. There&#8217;s no error from the solver, because nothing went wrong on the solving side.<\/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 need a proxy for v3 Enterprise?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Only when the site is geo-sensitive or the score depends on the requesting IP. Pass <code>[\"proxy\"] = new Proxy(\"HTTPS\", \"user:pass@1.2.3.4:3128\")<\/code> in the same options dictionary. Proxies are supported for reCAPTCHA, Turnstile and GeeTest, but not for image CAPTCHAs.<\/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 solve several pages at once?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Yes. Every method returns a <code>Task<\/code>, so <code>await Task.WhenAll(...)<\/code> is all you need. The SDK exports <code>AsyncCapSkip<\/code> too, but in .NET it&#8217;s just an alias of <code>CapSkipClient<\/code>, kept so code ported from the Python SDK still compiles. It adds nothing.<\/p>\n<\/details>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Summary<\/h2>\n<p>One method, two flags. Set <code>version<\/code> to <code>v3<\/code> and <code>enterprise<\/code> to <code>1<\/code>, copy the <code>action<\/code> off the page exactly, then submit <code>result.Code<\/code> within two minutes. There&#8217;s no score to tune, so if a token is refused, check the action and the flags first.<\/p>\n<p>The rest of the .NET surface is on the <a href=\"https:\/\/capskip.com\/csharp-captcha-solver\/\">C# CAPTCHA solver<\/a> page, <a href=\"https:\/\/capskip.com\/recaptcha-enterprise-solver\/\">Enterprise support<\/a> covers the other languages, and <a href=\"https:\/\/capskip.com\/recaptcha-v3-solver\/\">reCAPTCHA v3 solving<\/a> goes deeper on scoring. CapSkip is a <a href=\"https:\/\/capskip.com\/\">local captcha solver<\/a>, so every one of these solves happens on your own machine.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Enterprise \u4e0d\u662f\u4e00\u4e2a\u5355\u72ec\u7684\u65b9\u6cd5\u3002\u5b83\u662f\u540c\u4e00\u4e2a\u8c03\u7528\u4e0a\u7684\u4e00\u4e2a\u6807\u5fd7\uff0c\u4e0e version \u548c action \u540d\u79f0\u5e76\u5217\u3002\u8fd9\u91cc\u7528 C# \u8bf4\u660e\u6bcf\u4e00\u9879\u7684\u4f5c\u7528\u3002<\/p>","protected":false},"author":1,"featured_media":24955,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Solve reCAPTCHA v3 Enterprise in C# | CapSkip","rank_math_description":"reCAPTCHA v3 Enterprise in C# is one call with two flags and an action name. Here is the exact .NET code, and the mistake that gets your token rejected.","rank_math_focus_keyword":"recaptcha v3 enterprise in c#","footnotes":""},"categories":[70],"tags":[],"class_list":["post-24950","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\/24950","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=24950"}],"version-history":[{"count":2,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/24950\/revisions"}],"predecessor-version":[{"id":24956,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/24950\/revisions\/24956"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media\/24955"}],"wp:attachment":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media?parent=24950"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/categories?post=24950"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/tags?post=24950"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}