Skip to content
CaptchAPI
Reference

Error codes

Every error the API returns, what it means, and what to do about it.

HTTP is always 200, including for errors. That is the anti-captcha contract and we keep it, so a client written for CapSolver does not need its error handling rewritten. Branch on errorId: 0 means success, 1 means read errorCode.

Error response shape
{
  "errorId": 1,
  "errorCode": "ERROR_KEY_DOES_NOT_EXIST",
  "errorDescription": "No API key matches that clientKey."
}
CodeMeaningWhat to do
ERROR_KEY_DOES_NOT_EXISTNo API key matches the clientKey you sent.Check for a copy-paste error or a stale environment variable. Keys are shown in full only once, at creation.
ERROR_ZERO_BALANCEYour wallet cannot cover the price of the task.Top up. Tasks already in flight are unaffected and are still refunded if they fail.
ERROR_TASK_TYPE_NOT_SUPPORTEDThe task.type value is not one we handle.Compare it against the task types reference. The value is case-insensitive but the spelling must match.
ERROR_NO_SUCH_TASKThat taskId does not exist, or does not belong to your key.Results are retained for 5 minutes after completion. Poll sooner, and never share a taskId across accounts.
ERROR_RATE_LIMITToo many requests in the current window.Back off and retry. The task itself is unaffected — keep the same taskId and keep polling.
ERROR_KEY_DISABLEDThe key exists but has been disabled.Re-enable it in the dashboard, or generate a new one. Keys are disabled by you, or by us after an acceptable-use investigation.
ERROR_UPSTREAMThe solver refused or crashed on this task.You have been refunded. Retry once; if it repeats on the same target, send us the sitekey and page URL.
ERROR_TIMEOUTThe task did not resolve within its deadline.You have been refunded. Hardened targets sometimes need a proxy or an exact User-Agent — try the proxy variant of the task type.

Handling them

Only two codes are worth retrying automatically. ERROR_RATE_LIMIT means back off and keep the same task. ERROR_UPSTREAM and ERROR_TIMEOUT mean the task is dead and refunded — retry by creating a new one, at most once, or you will spend your budget on a target that is not going to solve. Everything else needs a human.

const result = await post('/getTaskResult', { taskId });

if (result.errorId !== 0) {
  switch (result.errorCode) {
    case 'ERROR_RATE_LIMIT':
      // Back off and retry the same taskId — the task is still alive.
      await sleep(2000);
      continue;
    case 'ERROR_ZERO_BALANCE':
    case 'ERROR_KEY_DISABLED':
      // Nothing to retry. Alert an operator.
      throw new Error(result.errorDescription);
    default:
      // Refunded already; create a fresh task if it is worth retrying.
      throw new Error(`${result.errorCode}: ${result.errorDescription}`);
  }
}

An error response never leaves you charged. If you ever see a debit without a matching credit for a failed task, that is a bug — send us the task id and we will fix it and refund it.