How to Fix ERROR_KEY_DOES_NOT_EXIST and Wrong User Key

Short answer: ERROR_KEY_DOES_NOT_EXIST means the API key in your request is not a key the solver recognises. Nothing is wrong with the CAPTCHA, the sitekey or the page URL. The request never got as far as being solved. This guide covers the three things that actually cause it, the fix in all four SDKs, and why you sometimes get ERROR_WRONG_USER_KEY instead.
What ERROR_KEY_DOES_NOT_EXIST means
It comes back from /in.php, at submit time, before any solving starts. The API compared the key parameter in your request against the key it is configured to accept, and did not find a match.
# Submitting with a key the app does not know: curl "http://127.0.0.1:8080/in.php?key=WRONG&method=userrecaptcha&googlekey=YOUR_SITEKEY&pageurl=https://example.com" ERROR_KEY_DOES_NOT_EXIST
Because it is returned at submit time you never get a captcha ID, so there is nothing to poll for on /res.php. If your code is looping and waiting, it is waiting on an ID that was never issued.
One thing that trips people up coming from a hosted service: CapSkip is a 2captcha-compatible API that runs on your own machine, so the key is not an account credential and it is not tied to a balance. It is a local access check on the API server. That changes what “wrong key” can even mean, which is why the fixes below are short.
First, check whether key validation is even on
CapSkip ships with key validation optional. When it is off, any non-empty string works and you will never see this error. When it is on, the key has to match exactly.
Open the CapSkip app, go to Settings, and look at the API server section. Two things matter there: whether key validation is enabled, and the exact key string if it is. Copy the key from that screen rather than retyping it. Most reports of ERROR_KEY_DOES_NOT_EXIST end here.
While you are in Settings, confirm the port too. A wrong port gives you a connection error rather than this one, but it is worth checking both at the same time. The setup guide walks through the whole screen.
Pass the key correctly in each SDK
All four SDKs take the same option name, and all four read the same environment variable, CAPSKIP_API_KEY. Set it explicitly while you are debugging so there is no doubt about what is being sent.
# pip install capskip
from capskip import CapSkip
# apiKey defaults to "capskip", which only works when
# key validation is turned off in the app.
solver = CapSkip(
apiKey="YOUR_API_KEY",
host="127.0.0.1",
port=8080,
)// npm install capskip
const { CapSkip } = require('capskip');
const solver = new CapSkip({
apiKey: 'YOUR_API_KEY',
host: '127.0.0.1',
port: 8080,
});// composer require capskip/capskip
use CapSkip\CapSkip;
$solver = new CapSkip([
'apiKey' => 'YOUR_API_KEY',
'host' => '127.0.0.1',
'port' => 8080,
]);// dotnet add package CapSkip
using CapSkip;
var solver = new CapSkipClient(
apiKey: "YOUR_API_KEY",
host: "127.0.0.1",
port: 8080);Full signatures for every method are on the CAPTCHA solving SDK page.
The three causes worth checking
1. The key never left your code
You set CAPSKIP_API_KEY in a .env file, but nothing loads .env at runtime. Or you exported it in one shell and ran the script in another. The SDK falls back to its default, the default is not your key, and you get the error.
Print what you are about to send. One line, and it settles the question:
import os
# Never print the whole key in a shared log.
key = os.environ.get("CAPSKIP_API_KEY", "<unset>")
print(len(key), repr(key[:4]))Printing the length catches the case a masked value hides: a key that is present but empty, or one carrying a trailing newline from cat key.txt.
2. The key is right but mangled in the URL
This only bites people calling the raw API. If your key contains characters with a meaning in a query string, they have to be percent-encoded. A + becomes a space, a & ends the parameter early, and a # truncates everything after it.
# Wrong: curl sends this raw and the key gets cut at the & curl "http://127.0.0.1:8080/in.php?key=ab&cd&method=userrecaptcha" # Right: let curl encode the parameter for you curl -G http://127.0.0.1:8080/in.php \ --data-urlencode "key=ab&cd" \ --data-urlencode "method=userrecaptcha"
Or POST the parameters as a form body instead, which sidesteps the whole class of problem. MDN has a short reference on percent-encoding if you want the exact character list. The SDKs encode for you, so this cause disappears the moment you switch to one.
3. You changed the key and something is still holding the old one
A long-running worker, a Docker container built with the key baked in, a CI secret, a browser extension configured separately. The app has the new key, one caller still has the old one, and only that caller fails. If some of your requests work and others do not, this is almost always it.
ERROR_KEY_DOES_NOT_EXIST vs ERROR_WRONG_USER_KEY
Two error strings, same family. Treat them as one problem with two shapes.
| Error | What it points at | First thing to check |
|---|---|---|
ERROR_KEY_DOES_NOT_EXIST | The key was read and did not match a known key | The key string itself, copied from Settings |
ERROR_WRONG_USER_KEY | Key validation is on and the key sent is wrong | Whether validation is on at all, then the string |
In the SDKs both surface as an ApiException, so catch that one type and read the message rather than branching on the string.
from capskip import CapSkip, ApiException, NetworkException
solver = CapSkip(apiKey="YOUR_API_KEY")
try:
result = solver.recaptcha(
sitekey="YOUR_SITEKEY",
url="https://example.com/page-with-recaptcha",
)
except ApiException as e:
# Key problems land here, with the raw code in the message.
print("api rejected the request:", e)
except NetworkException as e:
# The app is not running, or the port is wrong.
print("cannot reach capskip:", e)Nearby error codes
If the key checks out, the next few failures at submit time look similar but mean something else entirely.
| Code | Cause | Fix |
|---|---|---|
ERROR_WRONG_METHOD | The method parameter is missing or misspelled | Use userrecaptcha, turnstile, geetest, post or base64 |
ERROR_BAD_PARAMETERS | A required parameter for that method is absent | Check the method’s parameter list before resubmitting |
ERROR_GOOGLEKEY | The sitekey was rejected | Re-read it from the live page, not from cached source |
ERROR_PAGEURL | The page URL is missing or malformed | Send the full URL including the scheme |
| Connection refused | The app is not running or the port differs | Start CapSkip, confirm the API server is on |
Every code the API can return is listed in the API documentation.
Frequently asked questions
Do I need an API key at all?
Only if key validation is enabled in the app. With it off, any non-empty string is accepted and the SDK default of capskip works fine. The key is a local access check, not an account.
Can ERROR_KEY_DOES_NOT_EXIST mean I ran out of credit?
No. There is no balance to run out of. Solving happens on your own machine, so a key error is always a mismatch between what you sent and what the app expects.
Does the key go in the poll request too?
Yes. /res.php takes the same key parameter as /in.php. If you only fixed the submit call, the poll can still fail on the old value.
Why does my old 2captcha key not work here?
Because it was issued by their service and means nothing to a solver running on your machine. The API shape is the same, the credentials are not. Point your client at 127.0.0.1 and use the key from CapSkip Settings.
Summary
Check whether key validation is on, copy the key from Settings instead of retyping it, confirm the value actually reaches your process, and encode it properly if you are building the URL by hand. That covers effectively every occurrence of error_key_does_not_exist.
The whole class of problem gets smaller when the solver is a local captcha solver you control: no account, no balance, no rotating credentials, and one key you can read off the screen in front of you.
