# Errors

> Every Solvecha API error code with its HTTP status, its meaning and what to do. The codes are the same as in the NopeCHA API.

The API uses the error codes of the NopeCHA recognition API.

| Code | Message | HTTP status on `/v1` paths | Meaning |
|---|---|---|---|
| `9` | Unknown error | 500 | Something went wrong on our side, or the recognition engine could not be reached. Retry after a few seconds. |
| `10` | Invalid request | 400 | The request cannot be used: the body is not JSON or url-encoded form data, it is larger than 4.5 MB, a field or the number of images is wrong, or the job id is unknown or belongs to another captcha type. |
| `11` | Rate limit reached | 429 | Your key sent more requests per minute than its plan allows, or too many requests with a wrong key came from your address. |
| `14` | Incomplete job | 409 | The job exists, but the answer is not ready. Poll again in one second. |
| `15` | Invalid key | 401 | The key is missing, has the wrong shape, or is unknown. |
| `16` | Out of credit | 403 | Your key is out of credit: today's plan quota and free quota of successful solves are used (jobs that are still open count as well), and the balance is empty. Top up the balance, pick a plan, or wait for the daily reset at 00:00 UTC. |

## Error bodies

On the `/v1` paths an error has the HTTP status from the table and this body:

```json
{"error": 14, "code": 14, "message": "Incomplete job", "type": null}
```

On the legacy paths (`POST /`, `GET /?id=`, `GET /status`) an error comes with HTTP status 200 and this body. Always look at the `error` field there.

```json
{"error": 14, "message": "Incomplete job"}
```

## What to do

| Code | What to do |
|---|---|
| `14` | Not an error. Wait one second and poll again. Stop after about 30 seconds and create a new job. |
| `15` | Check the key. Copy it again from the [dashboard](https://solvecha.com/dashboard). Do not retry with the same key. |
| `16` | You are out of free quota, plan quota and balance. Call [`GET /v1/status`](https://solvecha.com/docs/api/get-status.md): `credit` shows what is left today, `balance_usd` shows your balance. To fix it, top up the balance or pick a plan in the [dashboard](https://solvecha.com/dashboard), or wait for the daily reset at 00:00 UTC. Open jobs count as used until they finish. |
| `11` | Slow down. Wait a few seconds and retry with a growing pause. See [Rate limits and quotas](https://solvecha.com/docs/rate-limits.md). |
| `10` | Do not retry the same request. Check the body, the number of pictures and the job id. |
| `9` | Retry after a few seconds, with a growing pause. If it lasts, send us the time of the request through the [contact form](https://solvecha.com/contact). |

## Handle errors in code

```python
import time
import requests

def get_answer(base, headers, job_id, kind, timeout=30):
    deadline = time.time() + timeout
    while time.time() < deadline:
        time.sleep(1)
        r = requests.get(f"{base}/v1/recognition/{kind}", headers=headers, params={"id": job_id})
        if r.status_code == 200:
            return r.json()["data"]
        error = r.json().get("error")
        if error == 14:
            continue  # not ready yet
        if error in (9, 11):
            time.sleep(3)  # back off, then ask again
            continue
        raise RuntimeError(f"Solvecha error {error}: {r.json().get('message')}")
    raise TimeoutError("job not ready in time")
```
