How to Solve Capy Puzzle in C# and Submit All Three Fields

solve capy puzzle in c# - How to Solve Capy Puzzle in C# and Submit All Three Fields

To solve Capy Puzzle in C#, read the site’s PUZZLE_ key off the page, call CapyAsync with that key and the page URL, then post the three values it returns in the capy_captchakey, capy_challengekey and capy_answer form fields, together and straight away. That is the whole integration, and the part that trips people up is the shape of the answer. Most types you may have solved hand back one token. Capy hands back three values that only work as a set, and one of them expires quickly. CapSkip added Capy Puzzle in version 1.4.0. This guide covers the key, the call, the two second hold that is there on purpose, and the submit.

What you need

  • CapSkip 1.4.0 or later running on a Windows machine. Capy support arrived in that release, alongside CaptchaFox and Friendly Captcha.
  • Version 1.3.0 or later of the CapSkip .NET package, the release that added CapyAsync. It targets .NET Standard 2.0 and depends on System.Text.Json 8, so it is supported on .NET Framework 4.6.2 and later and on .NET 6 and later. The samples use top-level statements and the implicit usings of a .NET 6 console project, so run them on .NET 6 or later.
  • Two values from the target page: the Capy key, which starts with PUZZLE_, and the URL of the page the widget runs on. Step 1 shows where the key lives, and the same place tells you whether you need a third, optional value.
  • 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 box can call it over the API. Both live under connection settings, and a section below covers when to switch.
# dotnet add package CapSkip
dotnet add package CapSkip

Step 1: find the PUZZLE_ key and the Capy host

Everything you need to solve Capy Puzzle in C# comes off the target page. The key is public and identical for every visitor, and it sits in two places. The k parameter of the widget script’s URL is in the HTML the server sends, which is what the page source and HttpClient both see. Once the widget has run in a browser, the key is also in a hidden capy_captchakey input that the widget writes into the form, and that is where the Elements panel in DevTools shows it.

<!-- In the HTML the server sends: the k parameter of the widget script -->
<script src="https://jp.api.capy.me/puzzle/get_js/?k=PUZZLE_YOUR_KEY"></script>

<!-- Written into the form by the widget once it has run (DevTools only) -->
<input type="hidden" name="capy_captchakey" value="PUZZLE_YOUR_KEY" />

Copy the key exactly, prefix included. While you are looking at that script tag, note its host. Everything in the script URL before /puzzle/get_js/ is the Capy API the key lives behind, and CapSkip calls it api_server. It defaults to https://jp.api.capy.me, which is where the live service runs, so you only pass it when a page loads the widget from somewhere else.

One trap here comes from other solvers’ documentation. Several of them still show api.capy.me, without the regional prefix, and that host no longer resolves. If you copied it from an old sample, delete the option and let CapSkip use its default.

Step 2: the CapyAsync call

The method takes the key, the page URL, and an optional options dictionary. For a page on the default host you need nothing beyond the first two.

// dotnet add package CapSkip
using CapSkip;

var solver = new CapSkipClient(host: "127.0.0.1", port: 8080);

// The PUZZLE_ key, and the page the widget runs on.
var result = await solver.CapyAsync(
    "PUZZLE_YOUR_KEY",
    "https://example.com/login");

Console.WriteLine(result.CaptchaKey);    // capy_captchakey
Console.WriteLine(result.ChallengeKey);  // capy_challengekey
Console.WriteLine(result.Answer);        // capy_answer

Read the three named properties. The Code property holds the same answer as raw JSON, which is useful for logging, and RespKey comes back empty because it exists only for compatibility with other services. Answer is a long string that starts along the lines of 0xax8ex0xax84x. It is the drag path the widget would have recorded as the piece moved, not a coordinate.

The options dictionary takes api_server, proxy, proxytype and useragent, plus a per-call timeout in seconds. The user agent, if you set one, is sent on the single request CapSkip makes to fetch the puzzle, and you will rarely need it. There is also a version option, but only puzzle is accepted: Capy’s other family, avatar, is a different challenge behind a different endpoint, so the SDK refuses it with a ValidationException instead of returning an answer the site would reject. An unknown option name, an empty PUZZLE_ key, an empty page URL or an unsupported proxytype raises the same exception before anything is sent. An option whose value is null is simply dropped, which lets you pass an optional host without an if statement.

// The script tag's host. jp.api.capy.me is the default, so pass
// this only when the page loads the widget from somewhere else.
var result = await solver.CapyAsync(
    "PUZZLE_YOUR_KEY",
    "https://example.com/login",
    new Dictionary<string, object?>
    {
        ["api_server"] = "https://jp.api.capy.me/",
    });

Why a Capy solve takes about two seconds

The detection itself is fast. CapSkip fetches the puzzle image, finds the hole with some pixel math, and builds the drag path, all without a browser or a model. Then it waits, deliberately.

Capy measures the time between drawing a puzzle and receiving the answer, and it refuses anything that arrives faster than a person could have dragged the piece. CapSkip’s own measurements against Capy put the floor at about one second, with an answer at half a second refused and answers from one second to four, the longest tested, accepted. The refusal uses the same message as a wrong answer, so a correct solve delivered too quickly looks exactly like a broken solver. CapSkip therefore holds every Capy result until two seconds after it drew the puzzle. The wait is a sleep, so it costs latency and no CPU, and because CapyAsync polls for you, all you see is a call that takes a few seconds.

Why a few and not two: the SDK’s polls start a quarter of a second apart and then back off, doubling the gap up to its pollingInterval, 5 seconds by default. With default settings the answer is usually collected at about the four second mark. If Capy is most of what a client solves, construct it with pollingInterval: 0.5 and the call returns much closer to two.

Two practical consequences. Do not add a delay of your own before submitting, since the hold has already covered it. And do not try to shave the time off, because the hold is exactly what makes the answer pass.

Step 3: submit all three values in one request

The three values go into the fields the widget would have written into the form itself. Send them together, in the same request as the rest of the form.

// http is your HttpClient; result comes from Step 2.
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
    ["username"] = "someone",
    ["capy_captchakey"] = result.CaptchaKey!,
    ["capy_challengekey"] = result.ChallengeKey!,
    ["capy_answer"] = result.Answer!,
});

var response = await http.PostAsync("https://example.com/login", form);

Submit the answer exactly as returned. It is the drag path, and the site’s backend checks it against the puzzle that was drawn, so trimming it, rebuilding it from its parts or tidying it in any way invalidates it. Percent-encoding it for the form body is fine, because that is transport and the server decodes it first.

Then submit promptly. CapSkip generates a fresh challenge key for every solve and the puzzle is bound to it, so the key is single-use and short-lived. One solve covers one submission. If the form is abandoned and resumed later, solve again rather than reaching for the values you kept.

Mirror what the real form sends. Most pages also carry hidden fields of their own, such as an anti-forgery token, and some submit through script with a JSON body instead of a form post. Many also refuse a request with no User-Agent header, which HttpClient does not send unless you add one. Submit once by hand with DevTools open and copy the request, then put the three Capy values where the page puts them.

Step 4: failures, retries and running many at once

When you solve Capy Puzzle in C# at any volume, a few solves will fail. A failed Capy solve arrives as an ApiException, and through the SDK it reads as unsolvable in both of the cases that matter. They need opposite responses, and what tells them apart is how often they happen. Both fail early, before the two second hold, because CapSkip only holds back a solved answer.

What happenedHow it looksWhat to do
The hole was not locatedOccasional, and the next attempt usually succeedsRetry. Each attempt draws a brand new puzzle over a different photo, so a retry is a genuinely independent attempt
The Capy API refused the keyEvery attempt, for that one key, and the Capy task list in CapSkip shows Invalid captcha keyCheck the key and api_server. CapSkip does not retry a refused key, because it would be refused identically

Misses are rare, so one retry covers nearly everything. You can retry in your own code, as the full example does, or set Retries in the Capy section of CapSkip’s settings, which is 0 by default and allows up to three per task.

CapyAsync polls on defaultTimeout, 120 seconds, because a Capy solve is one fetch and some arithmetic rather than a browser session. That covers a normal solve many times over. Raise it in the constructor if you turn Retries up, or if you queue far more solves than CapSkip runs at once. The Capy section’s Max. Threads setting defaults to 10, which you can raise, and extra tasks wait for a free thread.

When you run many solves in parallel, keep each solve paired with its submit. Starting a hundred CapyAsync calls with Task.WhenAll and posting the results afterwards leaves the earliest challenge keys ageing while the last solves finish. Wrap the solve and the submit together in one task, and cap the number in flight with a SemaphoreSlim sized to the thread count. And at volume, add proxies. Every solve draws a new puzzle from the Capy API, and a steady stream of those from one address is the pattern rate limiting exists to catch. Configure a proxy pool in CapSkip, or pass a per-request proxy. It only touches the puzzle fetch, which is the one request a Capy solve makes.

Running the solver somewhere else

The samples use 127.0.0.1 because that is right while your code and CapSkip share a machine. Once the .NET app runs anywhere else, such as a container, a build agent, a VPS or an app service, loopback points at the wrong box and the first solve throws a NetworkException. Switch CapSkip to Server mode and it listens on your network address or public IP, so any of those can reach it over the API. Use a static public IP if the route crosses the internet, turn on API key validation, and restrict the port to the addresses you expect with a Windows Firewall rule. It is still your own Windows machine, and solving is still unmetered.

The client does not read environment variables by itself. Read CAPSKIP_HOST and CAPSKIP_API_KEY in your own code and pass them to the constructor, as the full example does, so the same build runs on your desk and on a server.

Full working example

// dotnet add package CapSkip
using System.Text.RegularExpressions;
using CapSkip;

var pageUrl = "https://example.com/login";
var http = new HttpClient();
var solver = new CapSkipClient(
    apiKey: Environment.GetEnvironmentVariable("CAPSKIP_API_KEY") ?? "capskip",
    host: Environment.GetEnvironmentVariable("CAPSKIP_HOST") ?? "127.0.0.1",
    port: 8080,
    // Capy answers after a two second hold; poll often enough to catch it.
    pollingInterval: 0.5);

// The widget script's k parameter. The capy_captchakey input only
// exists once the widget has run in a browser.
var html = await http.GetStringAsync(pageUrl);
var key = Regex.Match(html, "PUZZLE_[A-Za-z0-9_-]+").Value;
if (key.Length == 0)
    throw new InvalidOperationException("No PUZZLE_ key in the HTML; check DevTools.");

// The script URL up to /puzzle/get_js/, when there is one; a null value is dropped.
var host = Regex.Match(html, @"(https://[^""'\s<>]+?)/puzzle/get_js/").Groups[1].Value;
var options = new Dictionary<string, object?>
{
    ["api_server"] = host.Length > 0 ? host : null,
};

try
{
    // A missed hole comes back unsolvable; a retry draws a new puzzle.
    SolveResult? result = null;
    for (var attempt = 1; result is null; attempt++)
    {
        try
        {
            result = await solver.CapyAsync(key, pageUrl, options);
        }
        catch (ApiException ex) when (attempt < 3 && ex.Message.Contains("UNSOLVABLE"))
        {
            Console.WriteLine($"attempt {attempt}: {ex.Message}");
        }
    }

    // All three together, straight away: the challenge key is single-use.
    var form = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        ["username"] = "someone",
        ["capy_captchakey"] = result.CaptchaKey!,
        ["capy_challengekey"] = result.ChallengeKey!,
        ["capy_answer"] = result.Answer!,
    });

    // Post wherever the form's action attribute points.
    var response = await http.PostAsync(pageUrl, form);
    Console.WriteLine((int)response.StatusCode);
}
catch (CapSkip.ValidationException ex)
{
    // An empty key or URL, avatar as the version, or an unknown option.
    Console.WriteLine($"not sent: {ex.Message}");
}
catch (CapSkip.TimeoutException)
{
    Console.WriteLine("gave up waiting; defaultTimeout is 120 seconds");
}
catch (CapSkipError ex)
{
    // The third miss, a refused key, or CapSkip unreachable.
    Console.WriteLine($"gave up: {ex.Message}");
}

The key regex looks for the PUZZLE_ prefix anywhere in the HTML, so it picks the key out of the widget script’s URL without parsing the tag. The capy_captchakey input is not in the downloaded HTML at all, because the widget writes it only when it runs in a browser. The host regex keeps everything before /puzzle/get_js/ in an https script URL, path included, and when it finds nothing the null value is dropped and CapSkip uses its default. If all three attempts fail, look at the key before anything else, since a refused key fails the same way every time while a miss almost never happens three times running. When the page builds the widget from a bundled script and neither regex matches, open the Network tab and copy both values from the widget’s own request. The raw endpoint behind this method is documented in the API reference, and every other CAPTCHA type the package solves is on the C# CAPTCHA solver page.

Common errors and what they mean

What you seeCauseFix
The site rejects the submit although CapSkip returned all three valuesThe answer was altered, or only one or two of the values reached the formPost result.Answer verbatim, with the other two values, in one request
A submit that worked once fails on the second attemptThe challenge key is single-use and short-livedSolve again for every submission, and submit straight away
An ApiException on every attempt, for one keyThe Capy API refused the key, or api_server points at the wrong hostCopy the key again, prefix included, and check the script tag’s host
An occasional ApiException saying the CAPTCHA is unsolvableCapSkip could not locate the hole in that puzzleRetry; the next attempt draws a different puzzle
Every solve fails after you copied a sample from another serviceThe sample sets api_server to api.capy.me, which no longer resolvesRemove the option and use the default host
A ValidationException before anything is sentAn empty key or page URL, avatar as the version, or an option the method does not takeCheck the regex found the key, and drop the option the message names
More failures as a long run goes onEvery puzzle is drawn from one addressSpread solves across a proxy pool configured in CapSkip
A TimeoutException under heavy parallel loadMore tasks queued than Max. Threads can clear inside 120 secondsCap concurrency with a SemaphoreSlim, or raise defaultTimeout
A NetworkException on the first solveCapSkip is not running, or the host and port are wrongStart CapSkip, then check whether it should be in Local mode or Server mode
The build fails on an ambiguous TimeoutExceptionCapSkip and System both define that short nameWrite CapSkip.TimeoutException in full, or catch CapSkipError

FAQ

Why does a Capy solve return three values instead of a token?

Because that is what the Capy form posts. There is no token issued by a server at any point. The widget itself generates a challenge key, fetches the puzzle that belongs to it, and records the drag, and the site then sends the challenge key and the answer to Capy with its private key to have them checked. CapSkip plays the part of the widget, so it returns what the widget would have written into the form.

Can a .NET app on a hosted platform reach the solver?

Yes. Switch CapSkip to Server mode under connection settings so it listens on a network address instead of loopback, read that address from CAPSKIP_HOST in your code, and pass it to the client. A container host, a VPS, a CI agent and a managed app service all connect the same way, over the same HTTP API. Use a static public IP with a firewall rule if the route crosses the internet. The solver stays on hardware you own, so nothing about the licence or the solve count changes.

How is this different from solving GeeTest v3 in C#?

Both are slide puzzles that return several values posted together, but they start from different places. GeeTest v3 starts from a challenge the site issues, which you must fetch immediately before solving because it expires within about a minute. Capy starts from nothing but the key, since the challenge key is generated at solve time, so there is nothing to fetch first and the only clock that matters runs after the solve. The GeeTest side is covered in the C# GeeTest v3 guide.

Do I need to send a user agent with the submit, as with CaptchaFox?

No. A CaptchaFox token is bound to the browser that minted it, so the submit has to carry that browser’s user agent, as the C# CaptchaFox guide explains. A Capy answer is not tied to a browser at all. CapSkip returns no user agent for it, and the optional one you can pass only changes the request that fetches the puzzle.

The short version

To solve Capy Puzzle in C#, copy the PUZZLE_ key from the widget script’s URL or the capy_captchakey input, and note the script’s host in case it is not the default. Call CapyAsync with the key and the real page URL and let it take its few seconds. Post CaptchaKey, ChallengeKey and Answer in capy_captchakey, capy_challengekey and capy_answer, verbatim, together and at once, and solve again for every submission. Retry the occasional miss, add proxies as volume grows, and switch to Server mode when the calling code leaves the solver’s machine.

One last thing about that retry. Because each attempt draws a different puzzle, trying again is the right fix for a miss, and with a local captcha solver on your own machine a second attempt costs a few seconds rather than another billed solve.