> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fapihub.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Error format, every error code, what counts against your quota, and how to retry.

Every error response is JSON with the same shape:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "insufficient_credits",
    "message": "Monthly request limit reached. Please upgrade your plan or wait until next month.",
    "request_id": "req_12345"
  },
  "timestamp": "2026-10-27T10:00:00+00:00"
}
```

**success** `boolean`

Always `false` for errors.

**error** `object`

**properties**

**code** `string`

Machine-readable error code. Use this in your code, not the message.

**message** `string`

Human-readable description of the error.

**request\_id** `string`

Included whenever the request was logged. Send it to [support@fapihub.com](mailto:support@fapihub.com) when you report a problem.

**timestamp** `string`

Time of the response.

A `429` response also has top-level `limit`, `retry_after` and `reset_at` fields. See [Rate Limit](https://docs.fapihub.com/rate-limit.md#when-you-exceed-the-rate-limit-429).

### Error codes

| Status | `error.code` | When | Counts against quota | What to do |
| - | - | - | - | - |
| 400 | `missing_image` | No `image` field | Yes | Fix the request |
| 400 | `invalid_image` | Unsupported type, empty, or outside 1 KB–10 MB | Yes | Fix the file; don't retry |
| 400 | `invalid_parameter` | Bad parameter, e.g. unknown `model` | Yes | Fix the request |
| 401 | `api_key_required` | No `ApiKey` header | No | Add the header |
| 401 | `invalid_api_key` | Key is wrong or deactivated | No | Check the key; don't retry |
| 402 | `insufficient_credits` | Monthly or lifetime quota is used up | No | Upgrade, or wait for the next billing period |
| 403 | `subscription_expired` | Subscription has expired | No | Renew |
| 403 | `subscription_inactive` | Subscription isn't active (e.g. payment past due) | No | Fix billing |
| 403 | `no_subscription` | Account has no subscription | No | Contact support |
| 429 | `rate_limit_exceeded` | Per-minute limit exceeded | No | Wait `Retry-After` seconds, then retry |
| 500 | `internal_error` | Server error | No | Retry with backoff |
| 504 | `timeout` | Image processing timed out | No | Retry with backoff |
| other | `external_api_error` | The image processor returned an error; its status code is passed through | Only if 4xx | Retry 5xx with backoff |

The exact messages for the `401` errors are:

* `api_key_required`: "Please provide an API key in the ApiKey header"
* `invalid_api_key`: "The provided API key is invalid or inactive"

> [!NOTE]
>
> **Image requirements:** formats JPG, PNG, WEBP, BMP or GIF; size 1 KB to 10 MB; content type `image/jpeg`, `image/png`, `image/webp`, `image/bmp` or `image/gif`. Anything else returns `400 invalid_image`.

### What counts against your quota

* **Counts:** successful requests, and `4xx` errors produced while processing: `missing_image`, `invalid_image`, `invalid_parameter`, and `4xx` errors returned by the image processor.
* **Doesn't count:** `401`, `402`, `403` and `429` rejections; any `5xx` error; calls to [`/v2/account/usage/`](https://docs.fapihub.com/usage). Retrying after a `429` is free.

### Recommended retry policy

* **429:** wait the number of seconds in the `Retry-After` header, then retry.
* **5xx and timeouts (including `504`):** retry with exponential backoff: wait 1 s, then 2 s, then 4 s.
* **400, 401, 402, 403:** never retry. Fix the request, key, plan or billing first.

```python Python theme={null}
import time
import requests

URL = "https://fapihub.com/v2/rembg/"
BACKOFF = [1, 2, 4]  # seconds between retries for 5xx errors and timeouts


def process_image(path, api_key):
    attempt = 0
    while True:
        try:
            with open(path, "rb") as f:
                response = requests.post(
                    URL,
                    headers={"ApiKey": api_key},
                    files={"image": f},
                    timeout=130,
                )
        except requests.exceptions.Timeout:
            response = None

        if response is not None and response.status_code == 200:
            return response.content

        if response is not None and response.status_code == 429:
            time.sleep(int(response.headers["Retry-After"]))
            continue

        if response is None or response.status_code >= 500:
            if attempt < len(BACKOFF):
                time.sleep(BACKOFF[attempt])
                attempt += 1
                continue

        # 400, 401, 402, 403, or out of retries: don't retry
        error = response.json()["error"] if response is not None else {"code": "timeout"}
        raise RuntimeError(f"{error['code']}: {error.get('message', '')} "
                           f"(request_id: {error.get('request_id', 'n/a')})")
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.