Guide
Error Handling
Learn how to handle API errors gracefully with proper retry strategies, error code mappings, and best practices.
Error Codes Reference
| Code | Name |
|---|---|
0 | Success |
1 | ERROR_KEY_DOES_NOT_EXIST |
2 | ERROR_ZERO_BALANCE |
3 | ERROR_INVALID_TASK |
10 | ERROR_CAPTCHA_UNSOLVABLE |
12 | ERROR_TASK_NOT_FOUND |
21 | ERROR_TASK_NOT_READY |
99 | ERROR_INTERNAL |
Retry Strategies
Not all errors should be retried. Here is a guide on which errors are retryable and the recommended strategy for each.
| Error Code | Retryable | Strategy |
|---|---|---|
| 0 | No | Success - no retry needed |
| 1 | No | Check API key, do not retry |
| 2 | No | Purchase credits first |
| 3 | No | Fix request parameters |
| 10 | Yes | Retry after 5-10 seconds, max 3 attempts |
| 12 | No | Create a new task |
| 21 | Yes | Continue polling every 2-3 seconds |
| 99 | Yes | Exponential backoff, max 5 attempts |
Common Mistakes
Sending clientKey in headers instead of body
The clientKey must be in the JSON request body, not in HTTP headers.
// Wrong
fetch('/api/solver/createTask', {
headers: { 'Authorization': 'Bearer cap_...' }
})
// Correct
fetch('/api/solver/createTask', {
body: JSON.stringify({ clientKey: 'cap_...' })
})Polling too frequently
Wait at least 2 seconds between getTaskResult calls. Excessive polling may trigger rate limiting.
// Wrong - no delay
while (true) {
const result = await getTaskResult(taskId);
if (result.status === 'ready') break;
}
// Correct - 2 second delay
while (true) {
await new Promise(r => setTimeout(r, 2000));
const result = await getTaskResult(taskId);
if (result.status === 'ready') break;
}Not handling ERROR_TASK_NOT_READY (code 21)
Error code 21 is not a failure -- it means the task is still processing. Continue polling.
// Wrong - treating code 21 as an error
if (result.errorId !== 0) throw new Error('Failed');
// Correct - checking for processing state
if (result.errorId === 21) continue; // Still processing
if (result.errorId !== 0) throw new Error('Failed');Not storing the taskId before polling
Always save the taskId from createTask before starting to poll for results.
// Wrong - losing the taskId
fetch('/api/solver/createTask', { ... }); // taskId is lost!
// Correct - saving the taskId
const { taskId } = await createResponse.json();
// Now use taskId to poll for results